#ifndef _IP_REASSEMBLE_H
#define _IP_REASSEMBLE_H

#ifdef __cplusplus
extern "C"
{
#endif

#include "packet_priv.h"
#include "log.h"

#define IP_REASSEMBLE_DEBUG(format, ...) LOG_DEBUG("ip_reassembly", format, ##__VA_ARGS__)
#define IP_REASSEMBLE_ERROR(format, ...) LOG_ERROR("ip_reassembly", format, ##__VA_ARGS__)

struct ip_reassembly_options
{
    uint8_t enable;
    uint32_t timeout;        // range: [1, 60000]
    uint32_t bucket_entries; // range: [1, 256] (must be power of 2)
    uint32_t bucket_num;     // range: [1, 4294967295]
};

struct ip_reassembly_stat
{
    // IPv4 flow stat
    uint64_t ip4_flow_find;
    uint64_t ip4_flow_add;
    uint64_t ip4_flow_del;
    uint64_t ip4_flow_timeout;

    uint64_t ip4_flow_fail_no_space;
    uint64_t ip4_flow_fail_overlap;
    uint64_t ip4_flow_fail_many_frag;
    uint64_t ip4_flow_fail_invalid_length;
    uint64_t ip4_flow_bypass_dup_fist_frag;
    uint64_t ip4_flow_bypass_dup_last_frag;

    // IPv6 flow stat
    uint64_t ip6_flow_find;
    uint64_t ip6_flow_add;
    uint64_t ip6_flow_del;
    uint64_t ip6_flow_timeout;

    uint64_t ip6_flow_fail_no_space;
    uint64_t ip6_flow_fail_overlap;
    uint64_t ip6_flow_fail_many_frag;
    uint64_t ip6_flow_fail_invalid_length;
    uint64_t ip6_flow_bypass_dup_fist_frag;
    uint64_t ip6_flow_bypass_dup_last_frag;
};

struct ip_reassembly *ip_reassembly_new(const struct ip_reassembly_options *opts);
void ip_reassembly_free(struct ip_reassembly *assy);
void ip_reassembly_expire(struct ip_reassembly *assy, uint64_t now);
struct ip_reassembly_stat *ip_reassembly_stat(struct ip_reassembly *assy);

/*
 * Returns the reassembled packet, or NULL if the packet is not reassembled
 * The returned packet should be freed by calling the packet_free() function
 */
struct packet *ip_reassembly_packet(struct ip_reassembly *assy, const struct packet *pkt, uint64_t now);
struct packet *ipv4_reassembly_packet(struct ip_reassembly *assy, const struct packet *pkt, uint64_t now);
struct packet *ipv6_reassembly_packet(struct ip_reassembly *assy, const struct packet *pkt, uint64_t now);

#ifdef __cplusplus
}
#endif

#endif
