768 lines
25 KiB
C
768 lines
25 KiB
C
#include <assert.h>
|
|
|
|
#include "uthash.h"
|
|
#include "checksum.h"
|
|
#include "ip_reassembly.h"
|
|
#include "packet_parser.h"
|
|
#include "packet_helper.h"
|
|
#include "packet_internal.h"
|
|
|
|
#define IP_REASSEMBLY_LOG_ERROR(format, ...) STELLAR_LOG_ERROR(__thread_local_logger, "IP reassembly", format, ##__VA_ARGS__)
|
|
#define IP_REASSEMBLY_LOG_INFO(format, ...) STELLAR_LOG_INFO(__thread_local_logger, "IP reassembly", format, ##__VA_ARGS__)
|
|
|
|
/*
|
|
* https://datatracker.ietf.org/doc/html/rfc8200#section-4.5
|
|
*
|
|
* Note: unlike IPv4, fragmentation in IPv6 is performed only by source nodes,
|
|
* not by routers along a packet's delivery path -- see Section 5.
|
|
*/
|
|
|
|
/*
|
|
* original packet:
|
|
* +-----------------+-----------------+--------+--------+-//-+--------+
|
|
* | Per-Fragment |Ext & Upper-Layer| first | second | | last |
|
|
* | Headers | Headers |fragment|fragment|....|fragment|
|
|
* +-----------------+-----------------+--------+--------+-//-+--------+
|
|
*
|
|
* fragment packets:
|
|
* +-----------------+--------+-------------------+----------+
|
|
* | Per-Fragment |Fragment| Ext & Upper-Layer | first |
|
|
* | Headers | Header | Headers | fragment |
|
|
* +-----------------+--------+-------------------+----------+
|
|
*
|
|
* +-----------------+--------+----------+
|
|
* | Per-Fragment |Fragment| second |
|
|
* | Headers | Header | fragment |
|
|
* +-----------------+--------+----------+
|
|
* o
|
|
* o
|
|
* o
|
|
* +-----------------+--------+----------+
|
|
* | Per-Fragment |Fragment| last |
|
|
* | Headers | Header | fragment |
|
|
* +-----------------+--------+----------+
|
|
*
|
|
* reassembled packet:
|
|
* +-----------------+-----------------+--------+--------+-//-+--------+
|
|
* | Per-Fragment |Ext & Upper-Layer| first | second | | last |
|
|
* | Headers | Headers |fragment|fragment|....|fragment|
|
|
* +-----------------+-----------------+--------+--------+-//-+--------+
|
|
*/
|
|
|
|
#define IP_FIRST_FRAG_IDX 0
|
|
#define IP_LAST_FRAG_IDX 1
|
|
#define IP_MIN_FRAG_NUM 2
|
|
|
|
struct frag_key
|
|
{
|
|
uint8_t ip_version;
|
|
union
|
|
{
|
|
struct in_addr v4; // network order
|
|
struct in6_addr v6; // network order
|
|
} saddr, daddr;
|
|
|
|
uint32_t ip_id; // IPv4 identification is uint16_t; IPv6 identification is uint32_t
|
|
uint8_t next_proto; // IPv4 every fragment has next protocol; IPv6 only first fragment has next protocol
|
|
};
|
|
|
|
struct frag_encap
|
|
{
|
|
const void *data;
|
|
uint16_t len; // Eth header len + tunnel header len + IP header len
|
|
uint16_t l3_offset; // Eth header len + tunnel header len
|
|
uint16_t l3_len; // IP header len
|
|
uint8_t next_proto;
|
|
};
|
|
|
|
struct frag
|
|
{
|
|
const void *data; // IP payload data
|
|
uint16_t len; // IP payload length
|
|
uint16_t offset; // IP frag offset
|
|
bool more; // IP frag more
|
|
struct packet *pkt;
|
|
};
|
|
|
|
struct frag_queue
|
|
{
|
|
uint64_t start_time;
|
|
uint32_t expect_size;
|
|
uint32_t recved_size;
|
|
uint32_t next_fill;
|
|
uint32_t frag_used;
|
|
|
|
struct frag_key key;
|
|
struct frag_encap encap;
|
|
UT_hash_handle hh;
|
|
TAILQ_ENTRY(frag_queue)
|
|
tqe;
|
|
struct frag frags[];
|
|
};
|
|
|
|
TAILQ_HEAD(frag_queue_list, frag_queue);
|
|
|
|
struct ip_reassembly
|
|
{
|
|
uint64_t timeout_ms;
|
|
uint64_t fq_num;
|
|
uint64_t fq_size;
|
|
|
|
struct frag_queue *htable;
|
|
struct frag_queue_list free_list;
|
|
struct frag_queue_list lru_list;
|
|
struct packet_queue evict_pkt;
|
|
|
|
struct ip_reassembly_stat stat;
|
|
};
|
|
|
|
#define STAT_INC(stat, filed, key) \
|
|
{ \
|
|
if ((key)->ip_version == 4) \
|
|
(stat)->ip4_##filed++; \
|
|
else \
|
|
(stat)->ip6_##filed++; \
|
|
}
|
|
|
|
#define STAT_ADD(stat, filed, key, n) \
|
|
{ \
|
|
if ((key)->ip_version == 4) \
|
|
(stat)->ip4_##filed += n; \
|
|
else \
|
|
(stat)->ip6_##filed += n; \
|
|
}
|
|
|
|
#define IP_DEFRAG_ERROR_WITH_KEY(desc, key, ...) \
|
|
do \
|
|
{ \
|
|
char saddr_str[INET6_ADDRSTRLEN] = {0}; \
|
|
char daddr_str[INET6_ADDRSTRLEN] = {0}; \
|
|
if ((key)->ip_version == 4) \
|
|
{ \
|
|
inet_ntop(AF_INET, &(key)->saddr.v4, saddr_str, INET6_ADDRSTRLEN); \
|
|
inet_ntop(AF_INET, &(key)->daddr.v4, daddr_str, INET6_ADDRSTRLEN); \
|
|
} \
|
|
else \
|
|
{ \
|
|
inet_ntop(AF_INET6, &(key)->saddr.v6, saddr_str, INET6_ADDRSTRLEN); \
|
|
inet_ntop(AF_INET6, &(key)->daddr.v6, daddr_str, INET6_ADDRSTRLEN); \
|
|
} \
|
|
IP_REASSEMBLY_LOG_ERROR("%s (%s-%s 0x%0x)", (desc), saddr_str, daddr_str, (key)->ip_id); \
|
|
} while (0)
|
|
|
|
static int frag_key_init(struct frag_key *key, const struct packet *pkt)
|
|
{
|
|
memset(key, 0, sizeof(struct frag_key));
|
|
const struct layer_internal *layer = pkt->frag_layer;
|
|
if (layer->proto == LAYER_PROTO_IPV4)
|
|
{
|
|
const struct ip *hdr = (const struct ip *)layer->hdr_ptr;
|
|
key->ip_version = 4;
|
|
key->saddr.v4 = ip4_hdr_get_src_in_addr(hdr);
|
|
key->daddr.v4 = ip4_hdr_get_dst_in_addr(hdr);
|
|
key->ip_id = ip4_hdr_get_ipid(hdr);
|
|
key->next_proto = ip4_hdr_get_proto(hdr);
|
|
}
|
|
else
|
|
{
|
|
const struct ip6_hdr *hdr = (const struct ip6_hdr *)layer->hdr_ptr;
|
|
const struct ip6_frag *frag = ip6_hdr_get_frag_ext(hdr);
|
|
if (frag == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
key->ip_version = 6;
|
|
key->saddr.v6 = ip6_hdr_get_src_in6_addr(hdr);
|
|
key->daddr.v6 = ip6_hdr_get_dst_in6_addr(hdr);
|
|
key->ip_id = ipv6_frag_get_ident(frag);
|
|
key->next_proto = 0; // only first fragment has the upper layer protocol
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void frag_encap_init(struct frag_encap *encap, const struct packet *pkt)
|
|
{
|
|
struct layer_internal *layer = pkt->frag_layer;
|
|
|
|
encap->data = (void *)pkt->data_ptr;
|
|
encap->len = layer->hdr_offset + layer->hdr_len;
|
|
|
|
encap->l3_offset = layer->hdr_offset;
|
|
encap->l3_len = layer->hdr_len;
|
|
|
|
if (layer->proto == LAYER_PROTO_IPV6)
|
|
{
|
|
const struct ip6_hdr *hdr = (const struct ip6_hdr *)layer->hdr_ptr;
|
|
const struct ip6_frag *frag = ip6_hdr_get_frag_ext(hdr);
|
|
encap->next_proto = ipv6_frag_get_next_header(frag);
|
|
}
|
|
else
|
|
{
|
|
const struct ip *hdr = (const struct ip *)layer->hdr_ptr;
|
|
encap->next_proto = ip4_hdr_get_proto(hdr);
|
|
}
|
|
}
|
|
|
|
static void frag_encap_clean(struct frag_encap *encap)
|
|
{
|
|
encap->data = NULL;
|
|
encap->len = 0;
|
|
encap->l3_offset = 0;
|
|
encap->l3_len = 0;
|
|
encap->next_proto = 0;
|
|
}
|
|
|
|
static int frag_init(struct frag *frag, struct packet *pkt)
|
|
{
|
|
const struct layer_internal *layer = pkt->frag_layer;
|
|
if (layer->proto == LAYER_PROTO_IPV4)
|
|
{
|
|
const struct ip *hdr = (const struct ip *)layer->hdr_ptr;
|
|
frag->data = layer->pld_ptr;
|
|
frag->len = layer->pld_len;
|
|
if ((char *)frag->data + frag->len > pkt->data_ptr + pkt->data_len)
|
|
{
|
|
return -1;
|
|
}
|
|
frag->offset = ip4_hdr_get_frag_offset(hdr);
|
|
frag->more = ip4_hdr_get_mf_flag(hdr);
|
|
frag->pkt = pkt;
|
|
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
const struct ip6_hdr *hdr = (const struct ip6_hdr *)layer->hdr_ptr;
|
|
const struct ip6_frag *frag_hdr = ip6_hdr_get_frag_ext(hdr);
|
|
if (frag_hdr == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
frag->data = (char *)frag_hdr + sizeof(struct ip6_frag);
|
|
frag->len = ip6_hdr_get_payload_len(hdr) - sizeof(struct ip6_frag);
|
|
if ((char *)frag->data + frag->len > pkt->data_ptr + pkt->data_len)
|
|
{
|
|
return -1;
|
|
}
|
|
frag->offset = ipv6_frag_get_offset(frag_hdr);
|
|
frag->more = ipv6_frag_get_more(frag_hdr);
|
|
frag->pkt = pkt;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void frag_clean(struct frag *frag)
|
|
{
|
|
frag->data = NULL;
|
|
frag->len = 0;
|
|
frag->offset = 0;
|
|
frag->more = false;
|
|
frag->pkt = NULL;
|
|
}
|
|
|
|
static struct frag_queue *ip_reassembly_new_fq(struct ip_reassembly *ip_reass)
|
|
{
|
|
struct frag_queue *fq = TAILQ_FIRST(&ip_reass->free_list);
|
|
if (fq)
|
|
{
|
|
TAILQ_REMOVE(&ip_reass->free_list, fq, tqe);
|
|
return fq;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static void ip_reassembly_free_fq(struct ip_reassembly *ip_reass, struct frag_queue *fq)
|
|
{
|
|
if (fq)
|
|
{
|
|
TAILQ_INSERT_TAIL(&ip_reass->free_list, fq, tqe);
|
|
}
|
|
}
|
|
|
|
static void ip_reassembly_add_fq(struct ip_reassembly *ip_reass, struct frag_queue *fq, const struct frag_key *key, uint64_t now)
|
|
{
|
|
static const struct frag zero = {
|
|
.data = NULL,
|
|
.len = 0,
|
|
.offset = 0,
|
|
.more = false,
|
|
.pkt = NULL,
|
|
};
|
|
|
|
fq->start_time = now;
|
|
fq->expect_size = UINT32_MAX;
|
|
fq->recved_size = 0;
|
|
fq->next_fill = IP_MIN_FRAG_NUM;
|
|
fq->frag_used = 0;
|
|
|
|
fq->key = *key;
|
|
fq->frags[IP_LAST_FRAG_IDX] = zero;
|
|
fq->frags[IP_FIRST_FRAG_IDX] = zero;
|
|
|
|
HASH_ADD(hh, ip_reass->htable, key, sizeof(struct frag_key), fq);
|
|
TAILQ_INSERT_TAIL(&ip_reass->lru_list, fq, tqe);
|
|
|
|
STAT_INC(&ip_reass->stat, defrags_expected, key)
|
|
}
|
|
|
|
static void ip_reassembly_del_fq(struct ip_reassembly *ip_reass, struct frag_queue *fq)
|
|
{
|
|
if (fq)
|
|
{
|
|
HASH_DELETE(hh, ip_reass->htable, fq);
|
|
TAILQ_REMOVE(&ip_reass->lru_list, fq, tqe);
|
|
|
|
frag_encap_clean(&fq->encap);
|
|
for (uint32_t i = 0; i < fq->next_fill; i++)
|
|
{
|
|
struct frag *frag = &fq->frags[i];
|
|
if (frag->pkt)
|
|
{
|
|
TAILQ_INSERT_TAIL(&ip_reass->evict_pkt, frag->pkt, frag_tqe);
|
|
}
|
|
frag_clean(frag);
|
|
}
|
|
STAT_ADD(&ip_reass->stat, frags_freed, &fq->key, fq->frag_used);
|
|
}
|
|
}
|
|
|
|
static struct frag_queue *ip_reassembly_find_fq(struct ip_reassembly *ip_reass, struct frag_key *key)
|
|
{
|
|
struct frag_queue *fq = NULL;
|
|
HASH_FIND(hh, ip_reass->htable, key, sizeof(struct frag_key), fq);
|
|
return fq;
|
|
}
|
|
|
|
static int ip_reassembly_update_fq(struct ip_reassembly *ip_reass, struct frag_queue *fq, struct frag *frag)
|
|
{
|
|
uint32_t idx;
|
|
/*
|
|
* Internet Protocol, Version 6 (IPv6) Specification
|
|
*
|
|
* https://datatracker.ietf.org/doc/html/rfc8200#section-4.5
|
|
*
|
|
* It should be noted that fragments may be duplicated in the
|
|
* network. Instead of treating these exact duplicate fragments
|
|
* as overlapping fragments, an implementation may choose to
|
|
* detect this case and drop exact duplicate fragments while
|
|
* keeping the other fragments belonging to the same packet.
|
|
*/
|
|
if (frag->offset == 0)
|
|
{
|
|
if (fq->frags[IP_FIRST_FRAG_IDX].data != NULL)
|
|
{
|
|
IP_DEFRAG_ERROR_WITH_KEY("duplicate first fragment", &fq->key);
|
|
STAT_INC(&ip_reass->stat, frags_overlap, &fq->key)
|
|
goto error_out;
|
|
}
|
|
|
|
idx = IP_FIRST_FRAG_IDX;
|
|
frag_encap_init(&fq->encap, frag->pkt);
|
|
}
|
|
else if (frag->more == 0)
|
|
{
|
|
if (fq->frags[IP_LAST_FRAG_IDX].data != NULL)
|
|
{
|
|
IP_DEFRAG_ERROR_WITH_KEY("duplicate last fragment", &fq->key);
|
|
STAT_INC(&ip_reass->stat, frags_overlap, &fq->key)
|
|
goto error_out;
|
|
}
|
|
|
|
idx = IP_LAST_FRAG_IDX;
|
|
fq->expect_size = frag->offset + frag->len;
|
|
}
|
|
else
|
|
{
|
|
if (fq->next_fill >= ip_reass->fq_size)
|
|
{
|
|
IP_DEFRAG_ERROR_WITH_KEY("max number of fragment exceeded", &fq->key);
|
|
STAT_INC(&ip_reass->stat, frags_too_many, &fq->key)
|
|
goto error_out;
|
|
}
|
|
|
|
idx = fq->next_fill;
|
|
fq->next_fill++;
|
|
}
|
|
|
|
fq->frag_used++;
|
|
fq->recved_size += frag->len;
|
|
|
|
TAILQ_REMOVE(&ip_reass->lru_list, fq, tqe);
|
|
TAILQ_INSERT_TAIL(&ip_reass->lru_list, fq, tqe);
|
|
|
|
memcpy(&fq->frags[idx], frag, sizeof(struct frag));
|
|
STAT_INC(&ip_reass->stat, frags_buffered, &fq->key)
|
|
|
|
return 0;
|
|
|
|
error_out:
|
|
STAT_INC(&ip_reass->stat, defrags_failed, &fq->key)
|
|
ip_reassembly_del_fq(ip_reass, fq);
|
|
ip_reassembly_free_fq(ip_reass, fq);
|
|
|
|
TAILQ_INSERT_TAIL(&ip_reass->evict_pkt, frag->pkt, frag_tqe);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static struct packet *ip_reassembly_defrag_fq(struct ip_reassembly *ip_reass, struct frag_queue *fq)
|
|
{
|
|
struct frag *frag = NULL;
|
|
struct frag *first = &fq->frags[IP_FIRST_FRAG_IDX];
|
|
struct frag *last = &fq->frags[IP_LAST_FRAG_IDX];
|
|
|
|
uint32_t loop = 0;
|
|
uint16_t last_offset = last->offset;
|
|
|
|
struct ip *ip4_hdr = NULL;
|
|
struct ip6_hdr *ip6_hdr = NULL;
|
|
|
|
// calculate the length of the reassembled packet
|
|
uint32_t total_len = fq->expect_size + fq->encap.len;
|
|
struct packet *pkt = packet_new(total_len);
|
|
if (pkt == NULL)
|
|
{
|
|
IP_REASSEMBLY_LOG_ERROR("unable to allocate memory");
|
|
|
|
// TODO stat
|
|
goto error_out;
|
|
}
|
|
|
|
char *ptr = (char *)packet_get_raw_data(pkt);
|
|
char *end = ptr + packet_get_raw_len(pkt);
|
|
|
|
// copy last frag
|
|
if (last->len > end - ptr)
|
|
{
|
|
IP_DEFRAG_ERROR_WITH_KEY("last frag length not match expected reassembled length", &fq->key);
|
|
STAT_INC(&ip_reass->stat, frags_invalid_length, &fq->key)
|
|
goto error_out;
|
|
}
|
|
end -= last->len;
|
|
memcpy(end, last->data, last->len);
|
|
|
|
while (first->len != last_offset)
|
|
{
|
|
/*
|
|
* https://datatracker.ietf.org/doc/html/rfc791
|
|
*
|
|
* In the case that two or more fragments contain the same data
|
|
* either identically or through a partial overlap, this procedure
|
|
* will use the more recently arrived copy in the data buffer and
|
|
* datagram delivered.
|
|
*/
|
|
for (uint32_t i = fq->next_fill - 1; i >= IP_MIN_FRAG_NUM; i--)
|
|
{
|
|
frag = &fq->frags[i];
|
|
if (frag->offset + frag->len == last_offset)
|
|
{
|
|
if (frag->len > end - ptr)
|
|
{
|
|
IP_DEFRAG_ERROR_WITH_KEY("middle frag length not match expected reassembled length", &fq->key);
|
|
STAT_INC(&ip_reass->stat, frags_invalid_length, &fq->key)
|
|
goto error_out;
|
|
}
|
|
|
|
end -= frag->len;
|
|
memcpy(end, frag->data, frag->len);
|
|
last_offset = frag->offset;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (loop > fq->next_fill - IP_MIN_FRAG_NUM)
|
|
{
|
|
IP_DEFRAG_ERROR_WITH_KEY("overlap appear during frag reassemble", &fq->key);
|
|
STAT_INC(&ip_reass->stat, frags_overlap, &fq->key)
|
|
goto error_out;
|
|
}
|
|
|
|
loop++;
|
|
}
|
|
|
|
// copy fist frag
|
|
if (first->len > end - ptr)
|
|
{
|
|
IP_DEFRAG_ERROR_WITH_KEY("first frag length not match expected reassembled length", &fq->key);
|
|
STAT_INC(&ip_reass->stat, frags_invalid_length, &fq->key)
|
|
goto error_out;
|
|
}
|
|
end -= first->len;
|
|
memcpy(end, first->data, first->len);
|
|
|
|
// copy frag hdr
|
|
if (fq->encap.len > end - ptr)
|
|
{
|
|
IP_DEFRAG_ERROR_WITH_KEY("packet header length not match expected reassembled length", &fq->key);
|
|
STAT_INC(&ip_reass->stat, frags_invalid_length, &fq->key)
|
|
goto error_out;
|
|
}
|
|
end -= fq->encap.len;
|
|
memcpy(end, fq->encap.data, fq->encap.len);
|
|
|
|
// assert
|
|
assert(ptr == end);
|
|
|
|
if (fq->key.ip_version == 4)
|
|
{
|
|
// update ip header length & ip checksum
|
|
ip4_hdr = (struct ip *)(ptr + fq->encap.l3_offset);
|
|
ip4_hdr_set_total_len(ip4_hdr, total_len - fq->encap.l3_offset); // update ip header length
|
|
ip4_hdr_set_mf_flag(ip4_hdr, false); // update more fragment flag
|
|
ip4_hdr_set_frag_offset(ip4_hdr, 0); // update fragment offset
|
|
ip4_hdr->ip_sum = 0; // update checksum
|
|
ip4_hdr->ip_sum = checksum((const void *)ip4_hdr, fq->encap.l3_len);
|
|
}
|
|
else
|
|
{
|
|
// update ipv6 payload length & next header
|
|
ip6_hdr = (struct ip6_hdr *)(ptr + fq->encap.l3_offset);
|
|
ip6_hdr_set_payload_len(ip6_hdr, fq->expect_size); // update payload length
|
|
ip6_hdr_set_next_header(ip6_hdr, fq->encap.next_proto); // update next header
|
|
}
|
|
|
|
// create a new packet
|
|
packet_parse(pkt, ptr, total_len);
|
|
packet_set_defraged(pkt);
|
|
memcpy(&pkt->meta, &first->pkt->meta, sizeof(struct metadata));
|
|
|
|
packet_push_frag(pkt, first->pkt);
|
|
first->pkt = NULL;
|
|
for (uint32_t i = IP_MIN_FRAG_NUM; i < fq->next_fill; i++)
|
|
{
|
|
frag = &fq->frags[i];
|
|
packet_push_frag(pkt, frag->pkt);
|
|
frag->pkt = NULL;
|
|
}
|
|
packet_push_frag(pkt, last->pkt);
|
|
last->pkt = NULL;
|
|
|
|
STAT_INC(&ip_reass->stat, defrags_succeed, &fq->key)
|
|
ip_reassembly_del_fq(ip_reass, fq);
|
|
ip_reassembly_free_fq(ip_reass, fq);
|
|
|
|
return pkt;
|
|
|
|
error_out:
|
|
STAT_INC(&ip_reass->stat, defrags_failed, &fq->key)
|
|
ip_reassembly_del_fq(ip_reass, fq);
|
|
ip_reassembly_free_fq(ip_reass, fq);
|
|
packet_free(pkt);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int frag_queue_is_ready(struct frag_queue *fq)
|
|
{
|
|
return (fq->recved_size == fq->expect_size && fq->frags[IP_FIRST_FRAG_IDX].data != NULL);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* core
|
|
******************************************************************************/
|
|
|
|
struct ip_reassembly *ip_reassembly_new(uint64_t timeout_ms, uint64_t fq_num, uint64_t fq_size)
|
|
{
|
|
struct ip_reassembly *ip_reass = (struct ip_reassembly *)calloc(1, sizeof(struct ip_reassembly));
|
|
if (ip_reass == NULL)
|
|
{
|
|
IP_REASSEMBLY_LOG_ERROR("unable to allocate memory for ip_reassembly");
|
|
return NULL;
|
|
}
|
|
|
|
ip_reass->timeout_ms = timeout_ms;
|
|
ip_reass->fq_num = fq_num;
|
|
ip_reass->fq_size = fq_size;
|
|
ip_reass->htable = NULL;
|
|
TAILQ_INIT(&ip_reass->free_list);
|
|
TAILQ_INIT(&ip_reass->lru_list);
|
|
TAILQ_INIT(&ip_reass->evict_pkt);
|
|
|
|
for (uint64_t i = 0; i < ip_reass->fq_num; i++)
|
|
{
|
|
struct frag_queue *fq = (struct frag_queue *)calloc(1, sizeof(struct frag_queue) + sizeof(struct frag) * ip_reass->fq_size);
|
|
if (fq == NULL)
|
|
{
|
|
IP_REASSEMBLY_LOG_ERROR("unable to allocate memory for frag_queue");
|
|
goto error_out;
|
|
}
|
|
TAILQ_INSERT_TAIL(&ip_reass->free_list, fq, tqe);
|
|
}
|
|
|
|
return ip_reass;
|
|
|
|
error_out:
|
|
ip_reassembly_free(ip_reass);
|
|
return NULL;
|
|
}
|
|
|
|
void ip_reassembly_free(struct ip_reassembly *ip_reass)
|
|
{
|
|
struct packet *pkt;
|
|
struct frag_queue *fq;
|
|
if (ip_reass)
|
|
{
|
|
while ((fq = TAILQ_FIRST(&ip_reass->lru_list)))
|
|
{
|
|
STAT_INC(&ip_reass->stat, defrags_failed, &fq->key)
|
|
ip_reassembly_del_fq(ip_reass, fq);
|
|
ip_reassembly_free_fq(ip_reass, fq);
|
|
}
|
|
|
|
while ((fq = TAILQ_FIRST(&ip_reass->free_list)))
|
|
{
|
|
TAILQ_REMOVE(&ip_reass->free_list, fq, tqe);
|
|
free(fq);
|
|
fq = NULL;
|
|
}
|
|
|
|
assert(HASH_COUNT(ip_reass->htable) == 0);
|
|
|
|
while ((pkt = TAILQ_FIRST(&ip_reass->evict_pkt)))
|
|
{
|
|
TAILQ_REMOVE(&ip_reass->evict_pkt, pkt, frag_tqe);
|
|
packet_free(pkt);
|
|
}
|
|
|
|
free(ip_reass);
|
|
ip_reass = NULL;
|
|
}
|
|
}
|
|
|
|
struct packet *ip_reassembly_defrag(struct ip_reassembly *ip_reass, struct packet *pkt, uint64_t now)
|
|
{
|
|
struct frag_key key;
|
|
if (frag_key_init(&key, pkt) != 0)
|
|
{
|
|
IP_REASSEMBLY_LOG_ERROR("unable to init frag key");
|
|
TAILQ_INSERT_TAIL(&ip_reass->evict_pkt, pkt, frag_tqe);
|
|
return NULL;
|
|
}
|
|
|
|
struct frag frag;
|
|
if (frag_init(&frag, pkt) != 0)
|
|
{
|
|
IP_REASSEMBLY_LOG_ERROR("unable to init frag");
|
|
TAILQ_INSERT_TAIL(&ip_reass->evict_pkt, pkt, frag_tqe);
|
|
return NULL;
|
|
}
|
|
|
|
STAT_INC(&ip_reass->stat, frags, &key)
|
|
struct frag_queue *fq = ip_reassembly_find_fq(ip_reass, &key);
|
|
if (fq == NULL)
|
|
{
|
|
fq = ip_reassembly_new_fq(ip_reass);
|
|
if (fq == NULL)
|
|
{
|
|
TAILQ_INSERT_TAIL(&ip_reass->evict_pkt, pkt, frag_tqe);
|
|
STAT_INC(&ip_reass->stat, frags_no_buffer, &key)
|
|
return NULL;
|
|
}
|
|
ip_reassembly_add_fq(ip_reass, fq, &key, now);
|
|
}
|
|
|
|
if (ip_reassembly_update_fq(ip_reass, fq, &frag) != 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (frag_queue_is_ready(fq))
|
|
{
|
|
return ip_reassembly_defrag_fq(ip_reass, fq);
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
struct packet *ip_reassembly_clean(struct ip_reassembly *ip_reass, uint64_t now_ms)
|
|
{
|
|
struct frag_queue *fq;
|
|
while ((fq = TAILQ_FIRST(&ip_reass->lru_list)))
|
|
{
|
|
if (fq->start_time + ip_reass->timeout_ms > now_ms)
|
|
{
|
|
break;
|
|
}
|
|
|
|
STAT_INC(&ip_reass->stat, defrags_failed, &fq->key)
|
|
STAT_ADD(&ip_reass->stat, frags_timeout, &fq->key, fq->frag_used)
|
|
ip_reassembly_del_fq(ip_reass, fq);
|
|
ip_reassembly_free_fq(ip_reass, fq);
|
|
}
|
|
|
|
struct packet *pkt = TAILQ_FIRST(&ip_reass->evict_pkt);
|
|
if (pkt)
|
|
{
|
|
TAILQ_REMOVE(&ip_reass->evict_pkt, pkt, frag_tqe);
|
|
return pkt;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
struct ip_reassembly_stat *ip_reassembly_get_stat(struct ip_reassembly *ip_reass)
|
|
{
|
|
if (ip_reass)
|
|
{
|
|
return &(ip_reass->stat);
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void ip_reassembly_print_stat(struct ip_reassembly *ip_reass)
|
|
{
|
|
if (ip_reass)
|
|
{
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip4_defrags_expected : %lu", ip_reass, ip_reass->stat.ip4_defrags_expected);
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip4_defrags_succeed : %lu", ip_reass, ip_reass->stat.ip4_defrags_succeed);
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip4_defrags_failed : %lu", ip_reass, ip_reass->stat.ip4_defrags_failed);
|
|
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip4_frags : %lu", ip_reass, ip_reass->stat.ip4_frags);
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip4_frags_freed : %lu", ip_reass, ip_reass->stat.ip4_frags_freed);
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip4_frags_buffered : %lu", ip_reass, ip_reass->stat.ip4_frags_buffered);
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip4_frags_no_buffer : %lu", ip_reass, ip_reass->stat.ip4_frags_no_buffer);
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip4_frags_timeout : %lu", ip_reass, ip_reass->stat.ip4_frags_timeout);
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip4_frags_invalid_length : %lu", ip_reass, ip_reass->stat.ip4_frags_invalid_length);
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip4_frags_overlap : %lu", ip_reass, ip_reass->stat.ip4_frags_overlap);
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip4_frags_too_many : %lu", ip_reass, ip_reass->stat.ip4_frags_too_many);
|
|
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip6_defrags_expected : %lu", ip_reass, ip_reass->stat.ip6_defrags_expected);
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip6_defrags_succeed : %lu", ip_reass, ip_reass->stat.ip6_defrags_succeed);
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip6_defrags_failed : %lu", ip_reass, ip_reass->stat.ip6_defrags_failed);
|
|
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip6_frags : %lu", ip_reass, ip_reass->stat.ip6_frags);
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip6_frags_freed : %lu", ip_reass, ip_reass->stat.ip6_frags_freed);
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip6_frags_buffered : %lu", ip_reass, ip_reass->stat.ip6_frags_buffered);
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip6_frags_no_buffer : %lu", ip_reass, ip_reass->stat.ip6_frags_no_buffer);
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip6_frags_timeout : %lu", ip_reass, ip_reass->stat.ip6_frags_timeout);
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip6_frags_invalid_length : %lu", ip_reass, ip_reass->stat.ip6_frags_invalid_length);
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip6_frags_overlap : %lu", ip_reass, ip_reass->stat.ip6_frags_overlap);
|
|
IP_REASSEMBLY_LOG_INFO("ip_reass: %p, ip6_frags_too_many : %lu", ip_reass, ip_reass->stat.ip6_frags_too_many);
|
|
}
|
|
}
|
|
|
|
uint64_t ip_reassembly_stat_get(struct ip_reassembly_stat *stat, enum ip_reass_stat_type type)
|
|
{
|
|
switch (type)
|
|
{
|
|
#define XX(_type, _name) \
|
|
case _type: \
|
|
return stat->_name;
|
|
IP_REASS_STAT_MAP(XX)
|
|
#undef XX
|
|
default:
|
|
return 0;
|
|
}
|
|
} |