#include "tuple.h"
#include "uthash.h"
#include "log_private.h"
#include "packet_helper.h"
#include "packet_private.h"

#define PACKET_LOG_ERROR(format, ...) STELLAR_LOG_ERROR(__thread_local_logger, "packet", format, ##__VA_ARGS__)

/******************************************************************************
 * metadata utils
 ******************************************************************************/

void packet_set_route_ctx(struct packet *pkt, const struct route_ctx *ctx)
{
    pkt->meta.route_ctx = *ctx;
}

const struct route_ctx *packet_get_route_ctx(const struct packet *pkt)
{
    return &pkt->meta.route_ctx;
}

void packet_set_origin_ctx(struct packet *pkt, void *ctx)
{
    pkt->meta.origin_ctx = ctx;
}

const void *packet_get_origin_ctx(const struct packet *pkt)
{
    return pkt->meta.origin_ctx;
}

void packet_set_sids(struct packet *pkt, const struct sids *sids)
{
    pkt->meta.sids = *sids;
}

const struct sids *packet_get_sids(const struct packet *pkt)
{
    return &pkt->meta.sids;
}

void packet_prepend_sids(struct packet *pkt, const struct sids *sids)
{
    if (pkt->meta.sids.used + sids->used > MAX_SIDS)
    {
        PACKET_LOG_ERROR("sids overflow");
        return;
    }
    else
    {
        for (int i = pkt->meta.sids.used - 1; i >= 0; i--)
        {
            pkt->meta.sids.sid[i + sids->used] = pkt->meta.sids.sid[i];
        }
        for (int i = 0; i < sids->used; i++)
        {
            pkt->meta.sids.sid[i] = sids->sid[i];
        }
        pkt->meta.sids.used += sids->used;
    }
}

void packet_set_session_id(struct packet *pkt, uint64_t id)
{
    pkt->meta.session_id = id;
}

uint64_t packet_get_session_id(const struct packet *pkt)
{
    return pkt->meta.session_id;
}

void packet_set_domain(struct packet *pkt, uint64_t domain)
{
    pkt->meta.domain = domain;
}

uint64_t packet_get_domain(const struct packet *pkt)
{
    return pkt->meta.domain;
}

void packet_set_link_id(struct packet *pkt, uint16_t id)
{
    pkt->meta.link_id = id;
}

uint16_t packet_get_link_id(const struct packet *pkt)
{
    return pkt->meta.link_id;
}

void packet_set_ctrl(struct packet *pkt, uint8_t ctrl)
{
    pkt->meta.is_ctrl = ctrl;
}

uint8_t packet_is_ctrl(const struct packet *pkt)
{
    return pkt->meta.is_ctrl;
}

void packet_set_direction(struct packet *pkt, enum packet_direction dir)
{
    pkt->meta.direction = dir;
}

enum packet_direction packet_get_direction(const struct packet *pkt)
{
    return pkt->meta.direction;
}

void packet_set_action(struct packet *pkt, enum packet_action action)
{
    pkt->meta.action = action;
}

enum packet_action packet_get_action(const struct packet *pkt)
{
    return pkt->meta.action;
}

void packet_set_timeval(struct packet *pkt, const struct timeval *tv)
{
    pkt->meta.tv = *tv;
}

const struct timeval *packet_get_timeval(const struct packet *pkt)
{
    return &pkt->meta.tv;
}

void packet_set_user_data(struct packet *pkt, void *data)
{
    pkt->user_data = data;
}

void *packet_get_user_data(struct packet *pkt)
{
    return pkt->user_data;
}

/******************************************************************************
 * tuple uitls
 ******************************************************************************/

// return  0 : found
// return -1 : not found
int packet_get_innermost_tuple2(const struct packet *pkt, struct tuple2 *tuple)
{
    memset(tuple, 0, sizeof(struct tuple2));
    const struct layer_private *layer = NULL;

    for (int8_t i = pkt->layers_used - 1; i >= 0; i--)
    {
        layer = &pkt->layers[i];

        if (layer->proto == LAYER_PROTO_IPV4)
        {
            const struct ip *ip4_hdr = (const struct ip *)layer->hdr_ptr;
            tuple->addr_family = AF_INET;
            tuple->src_addr.v4 = ip4_hdr_get_src_in_addr(ip4_hdr);
            tuple->dst_addr.v4 = ip4_hdr_get_dst_in_addr(ip4_hdr);
            return 0;
        }
        if (layer->proto == LAYER_PROTO_IPV6)
        {
            const struct ip6_hdr *ip6_hdr = (const struct ip6_hdr *)layer->hdr_ptr;
            tuple->addr_family = AF_INET6;
            tuple->src_addr.v6 = ip6_hdr_get_src_in6_addr(ip6_hdr);
            tuple->dst_addr.v6 = ip6_hdr_get_dst_in6_addr(ip6_hdr);
            return 0;
        }
    }

    return -1;
}

// return  0 : found
// return -1 : not found
int packet_get_outermost_tuple2(const struct packet *pkt, struct tuple2 *tuple)
{
    memset(tuple, 0, sizeof(struct tuple2));
    const struct layer_private *layer = NULL;

    for (int8_t i = 0; i < pkt->layers_used; i++)
    {
        layer = &pkt->layers[i];

        if (layer->proto == LAYER_PROTO_IPV4)
        {
            const struct ip *ip4_hdr = (const struct ip *)layer->hdr_ptr;
            tuple->addr_family = AF_INET;
            tuple->src_addr.v4 = ip4_hdr_get_src_in_addr(ip4_hdr);
            tuple->dst_addr.v4 = ip4_hdr_get_dst_in_addr(ip4_hdr);
            return 0;
        }
        if (layer->proto == LAYER_PROTO_IPV6)
        {
            const struct ip6_hdr *ip6_hdr = (const struct ip6_hdr *)layer->hdr_ptr;
            tuple->addr_family = AF_INET6;
            tuple->src_addr.v6 = ip6_hdr_get_src_in6_addr(ip6_hdr);
            tuple->dst_addr.v6 = ip6_hdr_get_dst_in6_addr(ip6_hdr);
            return 0;
        }
    }

    return -1;
}

// return  0 : found
// return -1 : not found
int packet_get_innermost_tuple4(const struct packet *pkt, struct tuple4 *tuple)
{
    memset(tuple, 0, sizeof(struct tuple4));
    const struct layer_private *layer_l3 = NULL;
    const struct layer_private *layer_l4 = NULL;
    const struct layer_private *layer = NULL;

    for (int8_t i = pkt->layers_used - 1; i >= 0; i--)
    {
        layer = &pkt->layers[i];

        // first get L4 layer
        if (layer->proto == LAYER_PROTO_UDP)
        {
            const struct udphdr *udp_hdr = (const struct udphdr *)layer->hdr_ptr;
            tuple->src_port = udp_hdr->uh_sport;
            tuple->dst_port = udp_hdr->uh_dport;
            layer_l4 = layer;
            continue;
        }
        if (layer->proto == LAYER_PROTO_TCP)
        {
            const struct tcphdr *tcp_hdr = (const struct tcphdr *)layer->hdr_ptr;
            tuple->src_port = tcp_hdr->th_sport;
            tuple->dst_port = tcp_hdr->th_dport;
            layer_l4 = layer;
            continue;
        }

        // second get L3 layer
        if (layer->proto == LAYER_PROTO_IPV4)
        {
            const struct ip *ip4_hdr = (const struct ip *)layer->hdr_ptr;
            tuple->addr_family = AF_INET;
            tuple->src_addr.v4 = ip4_hdr_get_src_in_addr(ip4_hdr);
            tuple->dst_addr.v4 = ip4_hdr_get_dst_in_addr(ip4_hdr);
            layer_l3 = layer;
            break;
        }
        if (layer->proto == LAYER_PROTO_IPV6)
        {
            const struct ip6_hdr *ip6_hdr = (const struct ip6_hdr *)layer->hdr_ptr;
            tuple->addr_family = AF_INET6;
            tuple->src_addr.v6 = ip6_hdr_get_src_in6_addr(ip6_hdr);
            tuple->dst_addr.v6 = ip6_hdr_get_dst_in6_addr(ip6_hdr);
            layer_l3 = layer;
            break;
        }
    }

    if (layer_l3 && layer_l4 && layer_l4 - layer_l3 == 1)
    {
        return 0;
    }
    else
    {
        return -1;
    }
}

// return  0 : found
// return -1 : not found
int packet_get_outermost_tuple4(const struct packet *pkt, struct tuple4 *tuple)
{
    memset(tuple, 0, sizeof(struct tuple4));
    const struct layer_private *layer_l3 = NULL;
    const struct layer_private *layer_l4 = NULL;
    const struct layer_private *layer = NULL;

    for (int8_t i = 0; i < pkt->layers_used; i++)
    {
        layer = &pkt->layers[i];

        // first get L3 layer
        if (layer->proto == LAYER_PROTO_IPV4)
        {
            const struct ip *ip4_hdr = (const struct ip *)layer->hdr_ptr;
            tuple->addr_family = AF_INET;
            tuple->src_addr.v4 = ip4_hdr_get_src_in_addr(ip4_hdr);
            tuple->dst_addr.v4 = ip4_hdr_get_dst_in_addr(ip4_hdr);
            layer_l3 = layer;
            continue;
        }
        if (layer->proto == LAYER_PROTO_IPV6)
        {
            const struct ip6_hdr *ip6_hdr = (const struct ip6_hdr *)layer->hdr_ptr;
            tuple->addr_family = AF_INET6;
            tuple->src_addr.v6 = ip6_hdr_get_src_in6_addr(ip6_hdr);
            tuple->dst_addr.v6 = ip6_hdr_get_dst_in6_addr(ip6_hdr);
            layer_l3 = layer;
            continue;
        }

        // second get L4 layer
        if (layer->proto == LAYER_PROTO_UDP)
        {
            const struct udphdr *udp_hdr = (const struct udphdr *)layer->hdr_ptr;
            tuple->src_port = udp_hdr->uh_sport;
            tuple->dst_port = udp_hdr->uh_dport;
            layer_l4 = layer;
            break;
        }
        if (layer->proto == LAYER_PROTO_TCP)
        {
            const struct tcphdr *tcp_hdr = (const struct tcphdr *)layer->hdr_ptr;
            tuple->src_port = tcp_hdr->th_sport;
            tuple->dst_port = tcp_hdr->th_dport;
            layer_l4 = layer;
            break;
        }
    }

    if (layer_l3 && layer_l4 && layer_l4 - layer_l3 == 1)
    {
        return 0;
    }
    else
    {
        return -1;
    }
}

// return  0 : found
// return -1 : not found
int packet_get_innermost_tuple6(const struct packet *pkt, struct tuple6 *tuple)
{
    memset(tuple, 0, sizeof(struct tuple6));
    const struct layer_private *layer_l3 = NULL;
    const struct layer_private *layer_l4 = NULL;
    const struct layer_private *layer = NULL;

    for (int8_t i = pkt->layers_used - 1; i >= 0; i--)
    {
        layer = &pkt->layers[i];

        // first get L4 layer
        if (layer->proto == LAYER_PROTO_UDP)
        {
            const struct udphdr *udp_hdr = (const struct udphdr *)layer->hdr_ptr;
            tuple->ip_proto = IPPROTO_UDP;
            tuple->src_port = udp_hdr->uh_sport;
            tuple->dst_port = udp_hdr->uh_dport;
            layer_l4 = layer;
            continue;
        }
        if (layer->proto == LAYER_PROTO_TCP)
        {
            const struct tcphdr *tcp_hdr = (const struct tcphdr *)layer->hdr_ptr;
            tuple->ip_proto = IPPROTO_TCP;
            tuple->src_port = tcp_hdr->th_sport;
            tuple->dst_port = tcp_hdr->th_dport;
            layer_l4 = layer;
            continue;
        }

        // second get L3 layer
        if (layer->proto == LAYER_PROTO_IPV4)
        {
            const struct ip *ip4_hdr = (const struct ip *)layer->hdr_ptr;
            tuple->addr_family = AF_INET;
            tuple->src_addr.v4 = ip4_hdr_get_src_in_addr(ip4_hdr);
            tuple->dst_addr.v4 = ip4_hdr_get_dst_in_addr(ip4_hdr);
            layer_l3 = layer;
            break;
        }
        if (layer->proto == LAYER_PROTO_IPV6)
        {
            const struct ip6_hdr *ip6_hdr = (const struct ip6_hdr *)layer->hdr_ptr;
            tuple->addr_family = AF_INET6;
            tuple->src_addr.v6 = ip6_hdr_get_src_in6_addr(ip6_hdr);
            tuple->dst_addr.v6 = ip6_hdr_get_dst_in6_addr(ip6_hdr);
            layer_l3 = layer;
            break;
        }
    }

    if (layer_l3 && layer_l4 && layer_l4 - layer_l3 == 1)
    {
        tuple->domain = packet_get_domain(pkt);
        return 0;
    }
    else
    {
        return -1;
    }
}

// return  0 : found
// return -1 : not found
int packet_get_outermost_tuple6(const struct packet *pkt, struct tuple6 *tuple)
{
    memset(tuple, 0, sizeof(struct tuple6));
    const struct layer_private *layer_l3 = NULL;
    const struct layer_private *layer_l4 = NULL;
    const struct layer_private *layer = NULL;

    for (int8_t i = 0; i < pkt->layers_used; i++)
    {
        layer = &pkt->layers[i];

        // first get L3 layer
        if (layer->proto == LAYER_PROTO_IPV4)
        {
            const struct ip *ip4_hdr = (const struct ip *)layer->hdr_ptr;
            tuple->addr_family = AF_INET;
            tuple->src_addr.v4 = ip4_hdr_get_src_in_addr(ip4_hdr);
            tuple->dst_addr.v4 = ip4_hdr_get_dst_in_addr(ip4_hdr);
            layer_l3 = layer;
            continue;
        }
        if (layer->proto == LAYER_PROTO_IPV6)
        {
            const struct ip6_hdr *ip6_hdr = (const struct ip6_hdr *)layer->hdr_ptr;
            tuple->addr_family = AF_INET6;
            tuple->src_addr.v6 = ip6_hdr_get_src_in6_addr(ip6_hdr);
            tuple->dst_addr.v6 = ip6_hdr_get_dst_in6_addr(ip6_hdr);
            layer_l3 = layer;
            continue;
        }

        // second get L4 layer
        if (layer->proto == LAYER_PROTO_UDP)
        {
            const struct udphdr *udp_hdr = (const struct udphdr *)layer->hdr_ptr;
            tuple->ip_proto = IPPROTO_UDP;
            tuple->src_port = udp_hdr->uh_sport;
            tuple->dst_port = udp_hdr->uh_dport;
            layer_l4 = layer;
            break;
        }
        if (layer->proto == LAYER_PROTO_TCP)
        {
            const struct tcphdr *tcp_hdr = (const struct tcphdr *)layer->hdr_ptr;
            tuple->ip_proto = IPPROTO_TCP;
            tuple->src_port = tcp_hdr->th_sport;
            tuple->dst_port = tcp_hdr->th_dport;
            layer_l4 = layer;
            break;
        }
    }

    if (layer_l3 && layer_l4 && layer_l4 - layer_l3 == 1)
    {
        tuple->domain = packet_get_domain(pkt);
        return 0;
    }
    else
    {
        return -1;
    }
}

/******************************************************************************
 * layer uitls
 ******************************************************************************/

int packet_get_layer_count(const struct packet *pkt)
{
    return pkt->layers_used;
}

const struct layer *packet_get_layer_by_idx(const struct packet *pkt, int idx)
{
    const struct layer_private *layer = packet_get_layer(pkt, idx);
    if (layer == NULL)
    {
        return NULL;
    }
    else
    {
        return (const struct layer *)layer;
    }
}

const struct layer_private *packet_get_layer(const struct packet *pkt, int idx)
{
    if (idx < 0 || idx >= pkt->layers_used)
    {
        return NULL;
    }
    return &pkt->layers[idx];
}

const struct layer_private *packet_get_innermost_layer(const struct packet *pkt, enum layer_proto proto)
{
    const struct layer_private *layer = NULL;

    for (int8_t i = pkt->layers_used - 1; i >= 0; i--)
    {
        layer = &pkt->layers[i];
        if (layer->proto == proto)
        {
            return layer;
        }
    }

    return NULL;
}

const struct layer_private *packet_get_outermost_layer(const struct packet *pkt, enum layer_proto proto)
{
    const struct layer_private *layer = NULL;

    for (int8_t i = 0; i < pkt->layers_used; i++)
    {
        layer = &pkt->layers[i];
        if (layer->proto == proto)
        {
            return layer;
        }
    }

    return NULL;
}

/******************************************************************************
 * tunnel uitls
 ******************************************************************************/

struct tunnel_detector
{
    enum tunnel_type type;
    int contain_layers;
    int (*identify_func)(const struct layer_private *curr, const struct layer_private *next1, const struct layer_private *next2);
};

static int is_ipv4_tunnel(const struct layer_private *curr, const struct layer_private *next1, const struct layer_private *next2 __attribute__((unused)))
{
    if (curr && curr->proto == LAYER_PROTO_IPV4 &&
        next1 && (next1->proto == LAYER_PROTO_IPV4 || next1->proto == LAYER_PROTO_IPV6))
    {
        return 1;
    }

    return 0;
}

static int is_ipv6_tunnel(const struct layer_private *curr, const struct layer_private *next1, const struct layer_private *next2 __attribute__((unused)))
{
    if (curr && curr->proto == LAYER_PROTO_IPV6 &&
        next1 && (next1->proto == LAYER_PROTO_IPV4 || next1->proto == LAYER_PROTO_IPV6))
    {
        return 1;
    }

    return 0;
}

static int is_gre_tunnel(const struct layer_private *curr, const struct layer_private *next1, const struct layer_private *next2 __attribute__((unused)))
{
    if (curr && (curr->proto == LAYER_PROTO_IPV4 || curr->proto == LAYER_PROTO_IPV6) &&
        next1 && next1->proto == LAYER_PROTO_GRE)
    {
        return 1;
    }

    return 0;
}

static int is_gtp_tunnel(const struct layer_private *curr, const struct layer_private *next1, const struct layer_private *next2)
{
    if (curr && (curr->proto == LAYER_PROTO_IPV4 || curr->proto == LAYER_PROTO_IPV6) &&
        next1 && next1->proto == LAYER_PROTO_UDP &&
        next2 && next2->proto == LAYER_PROTO_GTP_U)
    {
        return 1;
    }

    return 0;
}

static int is_vxlan_tunnel(const struct layer_private *curr, const struct layer_private *next1, const struct layer_private *next2)
{
    if (curr && (curr->proto == LAYER_PROTO_IPV4 || curr->proto == LAYER_PROTO_IPV6) &&
        next1 && next1->proto == LAYER_PROTO_UDP &&
        next2 && next2->proto == LAYER_PROTO_VXLAN)
    {
        return 1;
    }

    return 0;
}

static int is_l2tp_tunnel(const struct layer_private *curr, const struct layer_private *next1, const struct layer_private *next2)
{
    if (curr && (curr->proto == LAYER_PROTO_IPV4 || curr->proto == LAYER_PROTO_IPV6) &&
        next1 && next1->proto == LAYER_PROTO_UDP &&
        next2 && next2->proto == LAYER_PROTO_L2TP)
    {
        return 1;
    }

    return 0;
}

static int is_teredo_tunnel(const struct layer_private *curr, const struct layer_private *next1, const struct layer_private *next2)
{
    if (curr && curr->proto == LAYER_PROTO_IPV4 &&
        next1 && next1->proto == LAYER_PROTO_UDP &&
        next2 && next2->proto == LAYER_PROTO_IPV6)
    {
        return 1;
    }

    return 0;
}

static struct tunnel_detector detectors[] = {
    {TUNNEL_IPV4, 1, is_ipv4_tunnel},
    {TUNNEL_IPV6, 1, is_ipv6_tunnel},
    {TUNNEL_GRE, 2, is_gre_tunnel},
    {TUNNEL_GTP, 3, is_gtp_tunnel},
    {TUNNEL_VXLAN, 3, is_vxlan_tunnel},
    {TUNNEL_L2TP, 3, is_l2tp_tunnel},
    {TUNNEL_TEREDO, 2, is_teredo_tunnel},
};

// return index of detectors
static int detect_tunnel(const struct layer_private *curr, const struct layer_private *next1, const struct layer_private *next2)
{
    for (int i = 0; i < (int)(sizeof(detectors) / sizeof(detectors[0])); i++)
    {
        if (detectors[i].identify_func(curr, next1, next2))
        {
            return i;
        }
    }

    return -1;
}

int packet_get_tunnel_count(const struct packet *pkt)
{
    int count = 0;
    int used = packet_get_layer_count(pkt);
    const struct layer_private *curr = NULL;
    const struct layer_private *next1 = NULL;
    const struct layer_private *next2 = NULL;

    for (int i = 0; i < used; i++)
    {
        curr = packet_get_layer(pkt, i);
        next1 = packet_get_layer(pkt, i + 1);
        next2 = packet_get_layer(pkt, i + 2);
        if (detect_tunnel(curr, next1, next2) >= 0)
        {
            count++;
        }
    }

    return count;
}

// return  0: success 
// return -1: failed
int packet_get_tunnel_by_idx(const struct packet *pkt, int idx, struct tunnel *out)
{
    int ret = -1;
    int count = 0;
    int used = packet_get_layer_count(pkt);
    const struct layer_private *curr = NULL;
    const struct layer_private *next1 = NULL;
    const struct layer_private *next2 = NULL;
    memset(out, 0, sizeof(struct tunnel));

    for (int i = 0; i < used; i++)
    {
        curr = packet_get_layer(pkt, i);
        next1 = packet_get_layer(pkt, i + 1);
        next2 = packet_get_layer(pkt, i + 2);
        ret = detect_tunnel(curr, next1, next2);
        if (ret >= 0 && count++ == idx)
        {
            struct tunnel_detector *hit = &detectors[ret];
            out->type = hit->type;
            out->layer_count = hit->contain_layers;
            if (out->layer_count >= 1)
                out->layers[0] = (const struct layer *)curr;
            if (out->layer_count >= 2)
                out->layers[1] = (const struct layer *)next1;
            if (out->layer_count >= 3)
                out->layers[2] = (const struct layer *)next2;
            return 0;
        }
    }

    return -1;
}

/******************************************************************************
 * load balance uitls
 ******************************************************************************/

uint64_t packet_ldbc_hash(const struct packet *pkt, enum packet_load_balance_method method, enum packet_direction direction)
{
    uint64_t temp = 0;
    uint64_t hash_value = 1;

    int inner_addr_len = 0;
    int outer_addr_len = 0;
    const char *inner_src_addr = NULL;
    const char *inner_dst_addr = NULL;
    const char *outer_src_addr = NULL;
    const char *outer_dst_addr = NULL;

    struct tuple2 inner_addr;
    struct tuple2 outer_addr;

    if (pkt == NULL)
    {
        return hash_value;
    }

    if (packet_get_innermost_tuple2(pkt, &inner_addr) == -1)
    {
        return hash_value;
    }

    if (packet_get_outermost_tuple2(pkt, &outer_addr) == -1)
    {
        return hash_value;
    }

    if (inner_addr.addr_family == AF_INET)
    {
        inner_src_addr = (const char *)&inner_addr.src_addr.v4;
        inner_dst_addr = (const char *)&inner_addr.dst_addr.v4;
        inner_addr_len = sizeof(struct in_addr);
    }
    else
    {
        inner_src_addr = (const char *)&inner_addr.src_addr.v6;
        inner_dst_addr = (const char *)&inner_addr.dst_addr.v6;
        inner_addr_len = sizeof(struct in6_addr);
    }

    if (outer_addr.addr_family == AF_INET)
    {
        outer_src_addr = (const char *)&outer_addr.src_addr.v4;
        outer_dst_addr = (const char *)&outer_addr.dst_addr.v4;
        outer_addr_len = sizeof(struct in_addr);
    }
    else
    {
        outer_src_addr = (const char *)&outer_addr.src_addr.v6;
        outer_dst_addr = (const char *)&outer_addr.dst_addr.v6;
        outer_addr_len = sizeof(struct in6_addr);
    }

    switch (method)
    {
    case PKT_LDBC_METH_OUTERMOST_INT_IP:
        if (direction == PACKET_DIRECTION_INCOMING)
        {
            // direction 1: E2I
            HASH_VALUE(outer_dst_addr, outer_addr_len, hash_value);
        }
        else
        {
            // direction 0: I2E
            HASH_VALUE(outer_src_addr, outer_addr_len, hash_value);
        }
        break;
    case PKT_LDBC_METH_OUTERMOST_EXT_IP:
        if (direction == PACKET_DIRECTION_INCOMING)
        {
            // direction 1: E2I
            HASH_VALUE(outer_src_addr, outer_addr_len, hash_value);
        }
        else
        {
            // direction 0: I2E
            HASH_VALUE(outer_dst_addr, outer_addr_len, hash_value);
        }
        break;
    case PKT_LDBC_METH_OUTERMOST_INT_EXT_IP:
        HASH_VALUE(outer_src_addr, outer_addr_len, hash_value);
        HASH_VALUE(outer_dst_addr, outer_addr_len, temp);
        hash_value = hash_value ^ temp;
        break;
    case PKT_LDBC_METH_INNERMOST_INT_IP:
        if (direction == PACKET_DIRECTION_INCOMING)
        {
            // direction 1: E2I
            HASH_VALUE(inner_dst_addr, inner_addr_len, hash_value);
        }
        else
        {
            // direction 0: I2E
            HASH_VALUE(inner_src_addr, inner_addr_len, hash_value);
        }
        break;
    case PKT_LDBC_METH_INNERMOST_EXT_IP:
        if (direction == PACKET_DIRECTION_INCOMING)
        {
            // direction 1: E2I
            HASH_VALUE(inner_src_addr, inner_addr_len, hash_value);
        }
        else
        {
            // direction 0: I2E
            HASH_VALUE(inner_dst_addr, inner_addr_len, hash_value);
        }
        break;
    default:
        return hash_value;
    }

    return hash_value;
}

/******************************************************************************
 * other uitls
 ******************************************************************************/

const char *packet_get_raw_data(const struct packet *pkt)
{
    return pkt->data_ptr;
}

uint16_t packet_get_raw_len(const struct packet *pkt)
{
    return pkt->data_len;
}

const char *packet_get_payload(const struct packet *pkt)
{
    if (pkt == NULL || pkt->layers_used == 0)
    {
        return NULL;
    }

    if (pkt->layers[pkt->layers_used - 1].pld_len)
    {
        return pkt->layers[pkt->layers_used - 1].pld_ptr;
    }
    else
    {
        return NULL;
    }
}

uint16_t packet_get_payload_len(const struct packet *pkt)
{
    if (pkt == NULL || pkt->layers_used == 0)
    {
        return 0;
    }
    return pkt->layers[pkt->layers_used - 1].pld_len;
}

struct packet *packet_new(uint16_t pkt_len)
{
    struct packet *pkt = (struct packet *)calloc(1, sizeof(struct packet) + pkt_len);
    if (pkt == NULL)
    {
        return NULL;
    }
    pkt->data_len = pkt_len;
    pkt->data_ptr = (const char *)pkt + sizeof(struct packet);
    pkt->need_free = 1;

    return pkt;
}

struct packet *packet_dup(const struct packet *pkt)
{
    if (pkt == NULL)
    {
        return NULL;
    }

    struct packet *dup_pkt = packet_new(pkt->data_len);
    if (dup_pkt == NULL)
    {
        return NULL;
    }

    dup_pkt->layers_used = pkt->layers_used;
    dup_pkt->layers_size = pkt->layers_size;

    memcpy((char *)dup_pkt->data_ptr, pkt->data_ptr, pkt->data_len);
    dup_pkt->data_len = pkt->data_len;
    packet_set_action(dup_pkt, PACKET_ACTION_DROP);

    for (int8_t i = 0; i < pkt->layers_used; i++)
    {
        dup_pkt->layers[i].proto = pkt->layers[i].proto;
        dup_pkt->layers[i].hdr_ptr = dup_pkt->data_ptr + pkt->layers[i].hdr_offset;
        dup_pkt->layers[i].pld_ptr = dup_pkt->data_ptr + pkt->layers[i].hdr_offset + pkt->layers[i].hdr_len;
        dup_pkt->layers[i].hdr_offset = pkt->layers[i].hdr_offset;
        dup_pkt->layers[i].hdr_len = pkt->layers[i].hdr_len;
        dup_pkt->layers[i].pld_len = pkt->layers[i].pld_len;
    }

    // update frag_layer
    if (pkt->frag_layer)
    {
        dup_pkt->frag_layer = &dup_pkt->layers[pkt->frag_layer - pkt->layers];
    }
    memcpy(&dup_pkt->meta, &pkt->meta, sizeof(struct metadata));
    packet_set_origin_ctx(dup_pkt, (void *)NULL);

    return dup_pkt;
}

void packet_free(struct packet *pkt)
{
    if (pkt && pkt->need_free)
    {
        free((void *)pkt);
    }
}

int packet_is_fragment(const struct packet *pkt)
{
    return (pkt->frag_layer) ? 1 : 0;
}
