/**
 * \brief Make the threshold logic for signatures
 *
 * \param de_ctx Dectection Context
 * \param tsh_ptr Threshold element
 * \param p Packet structure
 * \param s Signature structure
 *
 * \retval 2 silent match (no alert but apply actions)
 * \retval 1 alert on this event
 * \retval 0 do not alert on this event
 */
int PacketAlertThreshold(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
        const DetectThresholdData *td, Packet *p, const Signature *s, PacketAlert *pa)
{
    SCEnter();

    int ret = 0;
    if (td == NULL) {
        SCReturnInt(0);
    }

    if (td->type == TYPE_SUPPRESS) {
        ret = ThresholdHandlePacketSuppress(p,td,s->id,s->gid);
    } else if (td->track == TRACK_SRC) {
        Host *src = HostGetHostFromHash(&p->src);
        if (src) {
            ret = ThresholdHandlePacketHost(src,p,td,s->id,s->gid,pa);
            HostRelease(src);
        }
    } else if (td->track == TRACK_DST) {
        Host *dst = HostGetHostFromHash(&p->dst);
        if (dst) {
            ret = ThresholdHandlePacketHost(dst,p,td,s->id,s->gid,pa);
            HostRelease(dst);
        }
    } else if (td->track == TRACK_RULE) {
        SCMutexLock(&de_ctx->ths_ctx.threshold_table_lock);
        ret = ThresholdHandlePacketRule(de_ctx,p,td,s,pa);
        SCMutexUnlock(&de_ctx->ths_ctx.threshold_table_lock);
    }

    SCReturnInt(ret);
}
/**
 * \brief Add a tag entry for a host. If it already exist, update it.
 *
 * \param tag_ctx Tag context for hosts
 * \param tde Tag data
 * \param p packet
 *
 * \retval 0 if it was added, 1 if it was updated
 */
int TagHashAddTag(DetectTagDataEntry *tde, Packet *p)
{
    SCEnter();

    uint8_t updated = 0;
    uint16_t num_tags = 0;
    Host *host = NULL;

    /* Lookup host in the hash. If it doesn't exist yet it's
     * created. */
    if (tde->flags & TAG_ENTRY_FLAG_DIR_SRC) {
        host = HostGetHostFromHash(&p->src);
    } else if (tde->flags & TAG_ENTRY_FLAG_DIR_DST) {
        host = HostGetHostFromHash(&p->dst);
    }
    /* no host for us */
    if (host == NULL) {
        return -1;
    }

    if (host->tag == NULL) {
        /* get a new tde as the one we have is on the stack */
        DetectTagDataEntry *new_tde = DetectTagDataCopy(tde);
        if (new_tde != NULL) {
            host->tag = new_tde;
            (void) SC_ATOMIC_ADD(num_tags, 1);
        }
    } else {
        /* Append the tag to the list of this host */

        /* First iterate installed entries searching a duplicated sid/gid */
        DetectTagDataEntry *iter = NULL;

        for (iter = host->tag; iter != NULL; iter = iter->next) {
            num_tags++;
            if (iter->sid == tde->sid && iter->gid == tde->gid) {
                iter->cnt_match++;
                /* If so, update data, unless the maximum MATCH limit is
                 * reached. This prevents possible DOS attacks */
                if (iter->cnt_match < DETECT_TAG_MATCH_LIMIT) {
                    /* Reset time and counters */
                    iter->first_ts = iter->last_ts = tde->first_ts;
                    iter->packets = 0;
                    iter->bytes = 0;
                }
                updated = 1;
                break;
            }
        }

        /* If there was no entry of this rule, append the new tde */
        if (updated == 0 && num_tags < DETECT_TAG_MAX_TAGS) {
            /* get a new tde as the one we have is on the stack */
            DetectTagDataEntry *new_tde = DetectTagDataCopy(tde);
            if (new_tde != NULL) {
                (void) SC_ATOMIC_ADD(num_tags, 1);

                new_tde->next = host->tag;
                host->tag = new_tde;
            }
        } else if (num_tags == DETECT_TAG_MAX_TAGS) {
            SCLogDebug("Max tags for sessions reached (%"PRIu16")", num_tags);
        }
    }

    HostRelease(host);
    SCReturnInt(updated);
}
/**
 * \brief This function is used to match packets via the eDDOS rule
 *
 * \param t pointer to thread vars
 * \param det_ctx pointer to the pattern matcher thread
 * \param p pointer to the current packet
 * \param m pointer to the sigmatch that we will cast into DetectDummyData
 *
 * \retval 0 no match
 * \retval 1 match
 */
int DetecteDDOSMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, SigMatch *m) {

    int ret = 0;
    DetecteDDOSSig *dsig = (DetecteDDOSSig *) m->ctx;
    DetecteDDOSData *ddata;
    Host *h;
    time_t t1;
    double time_diff_ms;

    if (PKT_IS_PSEUDOPKT(p)
            || !PKT_IS_IPV4(p)
            || p->flags & PKT_HOST_SRC_LOOKED_UP) {
        return 0;
    }

    /* TODO: Inspect the packet contents here.
     * Suricata defines a `Packet` structure in decode.h which already defines 
     * many useful elements -- have a look! */

    h = HostGetHostFromHash(&(p->src)); // Only SRC or can DEST be used too?
    p->flags |= PKT_HOST_SRC_LOOKED_UP;

    if (h == NULL) {
        printf("host not found!\n");
        return 0;
    }

    ddata = (DetecteDDOSData *) h->eddos;
    if (!ddata) {
        /* initialize fresh dummydata */
        ddata = SCMalloc(sizeof (DetecteDDOSData));
        bzero(ddata, sizeof (DetecteDDOSData));
        h->eddos = ddata;
    }
    /**
     * Start counting for evaluation
     */
    (ddata->cnt_packets)++;

    if (PKT_IS_TCP(p)) {
        //counter host
        (ddata->cnt_tcp)++;
        //counter global
        tcp_pkts_gesamt++;
        //printf("\nPakets TCP Flag: %d\n", p->tcph->th_flags);
        if (p->tcph->th_flags == tcp_syn_ack_flag) {
            (ddata->cnt_tcp_syn_ack)++;
            tcp_syn_ack_gesamt++;
            //printf("TCP_SYN_ACK");
        }
        if (p->tcph->th_flags == tcp_fin_ack_flag) {
            (ddata->cnt_tcp_fin_ack)++;
            tcp_fin_ack_gesamt++;
            //printf("TCP_FIN_ACK");
        }
        if (p->tcph->th_flags == TH_SYN) {
            (ddata->cnt_tcp_syn)++;
            tcp_syn_gesamt++;
            //printf("TCP_SYN");

        }
    }

    if (PKT_IS_UDP(p)) {
        (ddata->cnt_udp)++;
        udp_pkt_gesamt++;
    }

    if (PKT_IS_ICMPV4(p) && p->icmpv4h->type == ICMP_ECHO) {
        (ddata->cnt_icmp_echo_req)++;
        icmp_gesamt++;
    }

    /**
     * End Counting
     */
    /**
     * Start evaluation
     */
    if (PKT_IS_UDP(p) || PKT_IS_TCP(p)) {
        t1 = p->ts.tv_sec;
        time_diff_ms = difftime(t1, ddata->PeriodStart);
        if (time_diff_ms > (60)) {
            /*check for alarm here*/
            // abweichung vom 1:2 verhältnis SYN/ACK zu FIN/ACK
            float ver_syn_ack;
            float abw_syn_ack;
            // verhältnis von ICMP echo req paketen zu allen paketen
            float ver_echoreq_norm;
            // verhältnis von udp pakten zu allen paketen
            float ver_udp_norm;
            if (ddata->cnt_tcp_fin_ack != 0) {
                ver_syn_ack = ((float) (ddata->cnt_tcp_syn_ack) / ddata->cnt_tcp_fin_ack);
            } else {
                // we have syn but no syn_acks
                if ((((ddata->cnt_tcp_syn - ddata->cnt_tcp_syn_ack)*2) > dsig->max_abweichung_syn_ack ) && ddata->cnt_tcp_syn > 250) {
                    printf("\nSYN zu SYN/ACK Host\n");
                    printf("\n SYN: %llu SYN/ACK: %llu \n", ddata->cnt_tcp_syn, ddata->cnt_tcp_syn_ack );
                    ver_syn_ack = -1;
                }
                ver_syn_ack = 0;
            }

            abw_syn_ack = (((1 / 2) - ver_syn_ack))*2;
            //printf("\nSYN/ACK zu FIN/ACK Host Value: %f # SYN/ACK: %llu - FIN/ACK: %llu\n", (abw_syn_ack), ddata->cnt_tcp_syn_ack, ddata->cnt_tcp_fin_ack);

            if (abw_syn_ack < 0) {

                // check if abweichung größer als in signatur angegeben
                if (fabs(abw_syn_ack) > dsig->max_abweichung_syn_ack) {
                    ret = 1;
                    printf("\nSYN/ACK zu FIN/ACK Host Value: %f # SYN/ACK: %llu - FIN/ACK: %llu\n", fabs(abw_syn_ack), ddata->cnt_tcp_syn_ack, ddata->cnt_tcp_fin_ack);

                }
            }


            // verhältnis icmp echo request berechnen
            ver_echoreq_norm = ((float) (ddata->cnt_icmp_echo_req) / ddata->cnt_packets)*100;

            // check if ICMP echo request pakete zu viel im verhältnis zu allen
            if (ver_echoreq_norm > (float) (dsig->max_icmp_echo_req_packets)) {
                ret = 1;
                //printf("\nICMP Host\n");
            }
            /**
            // verhältnis udp paketen berechnen
            ver_udp_norm = ((float)(ddata->cnt_udp) / ddata->cnt_packets);
            // check if udp pakete zu viel im verhältnis zu allen
            if ( ver_udp_norm > (float)(dsig->max_udp_packets) ) {
                ret = 1;
            }  
             */
            /**
             * gesamtauswertung 
             */
            time_diff_ms = difftime(t1, start_time);
            if (time_diff_ms > (300)) {
                // auswertung udp pakete
                // verhältnis udp paketen berechnen
                ver_udp_norm = ((float) (udp_pkt_gesamt) / (udp_pkt_gesamt + tcp_pkts_gesamt))*100;
                //printf("UDP Pakete Verhaeltnis: %f", ver_udp_norm);
                // check if udp pakete zu viel im verhältnis zu allen
                if (ver_udp_norm > (float) (dsig->max_verhaeltnis_udp_packets)) {
                    ret = 1;
                    //printf("\nUDP ALL\n");
                }

                // auswertung echo requests
                ver_echoreq_norm = ((float) (icmp_gesamt) / (udp_pkt_gesamt + tcp_pkts_gesamt))*100;
                if (ver_echoreq_norm > (float) (icmp_gesamt)) {
                    ret = 1;
                    printf("\nICMP ALL\n");
                }

                // auswertung syn/ack
                if (tcp_fin_ack_gesamt != 0) {
                    ver_syn_ack = ((float) (tcp_syn_ack_gesamt) / tcp_fin_ack_gesamt);
                } else {
                    // we have syn but no syn_acks
                    if (fabs((float)((tcp_syn_gesamt - tcp_syn_gesamt)*2)) > dsig->max_abweichung_syn_ack) {
                        printf("\nSYN zu SYN/ACK ALL\n");
                        ver_syn_ack = -1;
                    }
                    ver_syn_ack = 0;
                }

                abw_syn_ack = (((1 / 2) - ver_syn_ack))*2;
                if (abw_syn_ack < 0) {
                    // check if abweichung größer als in signatur angegeben
                    if (fabs(abw_syn_ack) > dsig->max_abweichung_syn_ack) {
                        ret = 1;
                        printf("\nSYN/ACK zu FIN/ACK ALL\n");
                    }
                }
                // reset global counter for new interval
                
                tcp_pkts_gesamt = 0;
                udp_pkt_gesamt = 0;
                tcp_fin_ack_gesamt = 0;
                tcp_syn_ack_gesamt = 0;
                icmp_gesamt = 0;
                


            }
            /**printf("host found, packets now %d\n", (int)(ddata->cnt_packets));
            ret = (ddata->cnt_packets > dsig->max_numpackets);
             */

            // reset der parameter, da neuer zeitraum beginnt
            ddata->PeriodStart = p->ts.tv_sec;
            ddata->cnt_icmp_echo_req = 0;
            ddata->cnt_packets = 0;
            ddata->cnt_tcp = 0;
            ddata->cnt_tcp_fin_ack = 0;
            ddata->cnt_tcp_syn_ack = 0;
            ddata->cnt_udp = 0;

        } else {
            ret = 0;
        }
    }
    /**
     * End of evaluation
     */

    //printf("\nHost: %d - %d\n", (int)h, ddata->cnt_packets);
    /**
    printf("#################################\n");
    printf("Host: %d\n",(int)h);
    printf("\n TCP-SYN: %d \n", ddata->cnt_tcp_syn);
    printf("\n TCP-ACK: %d \n", ddata->cnt_tcp_ack);
    printf("\n Packets gesamt: %d \n", ddata->cnt_packets);
    printf("\n Packets TCP gesamt: %d \n", ddata->cnt_tcp);
    printf("\n Packets UDP gesamt: %d \n", ddata->cnt_udp);
    printf("\n ICMP ECHO REQUEST Packets: %d \n", ddata->cnt_icmp_echo_req);
    printf("#################################\n");
    
    printf("\n\nTCP Gesamt: %llu\n",tcp_pkts_gesamt);
    printf("UDP Gesamt: %llu\n",udp_pkt_gesamt);
    printf("SYN: %llu\n",tcp_syn_gesamt);
    printf("ICMP: %llu\n",icmp_gesamt);
    printf("SYN_ACK gesamt %d\n",(int)tcp_syn_ack_gesamt);
    printf("FIN_ACK_gesamt %d\n\n",(int)tcp_fin_ack_gesamt);
    */
    HostRelease(h);
    return ret;
}