#include "dablooms.h"
#include "packet_filter.h"
#include "packet_private.h"

struct packet_key
{
    // TCP or UDP
    uint32_t seq;         // network order
    uint32_t ack;         // network order
    uint16_t src_port;    // network order
    uint16_t dst_port;    // network order
    uint16_t l4_checksum; // network order

    // IPv4
    uint16_t ip_id;    // network order
    uint32_t src_addr; // network order
    uint32_t dst_addr; // network order
} __attribute__((__packed__));

struct packet_filter
{
    struct expiry_dablooms_handle *dablooms;
};

/******************************************************************************
 * Private API
 ******************************************************************************/

// return  0: success
// reutrn -1: error
static inline int packet_key_get(const struct packet *packet, struct packet_key *key)
{
    const struct layer_private *ip_layer = packet_get_innermost_layer(packet, LAYER_PROTO_IPV4);
    const struct layer_private *tcp_layer = packet_get_innermost_layer(packet, LAYER_PROTO_TCP);
    const struct layer_private *udp_layer = packet_get_innermost_layer(packet, LAYER_PROTO_UDP);
    if (ip_layer == NULL || (tcp_layer == NULL && udp_layer == NULL))
    {
        return -1;
    }

    memset(key, 0, sizeof(struct packet_key));

    const struct ip *iphdr = (const struct ip *)ip_layer->hdr_ptr;
    key->ip_id = iphdr->ip_id;
    key->src_addr = iphdr->ip_src.s_addr;
    key->dst_addr = iphdr->ip_dst.s_addr;

    if (tcp_layer)
    {
        const struct tcphdr *tcphdr = (const struct tcphdr *)tcp_layer->hdr_ptr;
        key->seq = tcphdr->th_seq;
        key->ack = tcphdr->th_ack;
        key->src_port = tcphdr->th_sport;
        key->dst_port = tcphdr->th_dport;
        key->l4_checksum = tcphdr->th_sum;
    }
    else
    {
        const struct udphdr *udphdr = (const struct udphdr *)udp_layer->hdr_ptr;
        key->src_port = udphdr->uh_sport;
        key->dst_port = udphdr->uh_dport;
        key->l4_checksum = udphdr->uh_sum;
    }

    return 0;
}

/******************************************************************************
 * Public API
 ******************************************************************************/

struct packet_filter *packet_filter_new(uint32_t capacity, uint32_t timeout, double error_rate, uint64_t now)
{
    struct packet_filter *filter = (struct packet_filter *)calloc(1, sizeof(struct packet_filter));
    if (filter == NULL)
    {
        return NULL;
    }

    filter->dablooms = expiry_dablooms_new(capacity, error_rate, now, timeout);
    if (filter->dablooms == NULL)
    {
        free(filter);
        return NULL;
    }

    return filter;
}

void packet_filter_free(struct packet_filter *filter)
{
    if (filter)
    {
        if (filter->dablooms)
        {
            expiry_dablooms_free(filter->dablooms);
            filter->dablooms = NULL;
        }
        free(filter);
        filter = NULL;
    }
}

// return 1: found
// reutrn 0: no found
int packet_filter_lookup(struct packet_filter *filter, const struct packet *packet, uint64_t now)
{
    struct packet_key key;
    if (packet_key_get(packet, &key) == -1)
    {
        return 0;
    }

    if (expiry_dablooms_search(filter->dablooms, (const char *)&key, sizeof(struct packet_key), now) == 1)
    {
        return 1;
    }

    return 0;
}

void packet_filter_add(struct packet_filter *filter, const struct packet *packet, uint64_t now)
{
    struct packet_key key;
    if (packet_key_get(packet, &key) == -1)
    {
        return;
    }

    expiry_dablooms_add(filter->dablooms, (const char *)&key, sizeof(struct packet_key), now);
}
