diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2021-03-04 19:22:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2021-03-04 20:43:22 +0000 |
commit | 22c74419e2c258319bc723351876604b3304604b (patch) | |
tree | 8c799a78d53f67388fdf42900657eda617c1306a /src/pcap-thread/pcap_thread_ext_frag.c | |
parent | Initial commit. (diff) | |
download | dnscap-upstream/2.0.0+debian.tar.xz dnscap-upstream/2.0.0+debian.zip |
Adding upstream version 2.0.0+debian.upstream/2.0.0+debian
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/pcap-thread/pcap_thread_ext_frag.c | 1013 |
1 files changed, 1013 insertions, 0 deletions
diff --git a/src/pcap-thread/pcap_thread_ext_frag.c b/src/pcap-thread/pcap_thread_ext_frag.c new file mode 100644 index 0000000..6593e92 --- /dev/null +++ b/src/pcap-thread/pcap_thread_ext_frag.c @@ -0,0 +1,1013 @@ +/* + * Author Jerry Lundström <jerry@dns-oarc.net> + * Copyright (c) 2016-2017, OARC, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "pcap_thread_ext_frag.h" + +#include <stdlib.h> +#include <string.h> +#ifdef HAVE_PTHREAD +#include <pthread.h> +#endif + +#ifndef PCAP_THREAD_EXT_FRAG_TRACE +#define PCAP_THREAD_EXT_FRAG_TRACE 0 +#endif + +/* + * Forward declares for callbacks + */ + +static void* pcap_thread_layer_callback_frag_new(void* conf, u_char* user); +static void pcap_thread_layer_callback_frag_free(void* _ctx); +static pcap_thread_packet_state_t pcap_thread_layer_callback_frag_reassemble(void* _ctx, const pcap_thread_packet_t* packet, const u_char* payload, size_t length, pcap_thread_packet_t** whole_packet, const u_char** whole_payload, size_t* whole_length); +static void pcap_thread_layer_callback_frag_release(void* _ctx, const pcap_thread_packet_t* packet, const u_char* payload, size_t length); + +/* + * Create/Free + */ + +static pcap_thread_ext_frag_conf_t _conf_defaults = PCAP_THREAD_EXT_FRAG_CONF_T_INIT; + +pcap_thread_ext_frag_conf_t* pcap_thread_ext_frag_conf_new(void) +{ + pcap_thread_ext_frag_conf_t* conf = calloc(1, sizeof(pcap_thread_ext_frag_conf_t)); + if (conf) { + memcpy(conf, &_conf_defaults, sizeof(pcap_thread_ext_frag_conf_t)); + } + + return conf; +} + +void pcap_thread_ext_frag_conf_free(pcap_thread_ext_frag_conf_t* conf) +{ + if (conf) { + free(conf); + } +} + +/* + * Get/Set + */ + +int pcap_thread_ext_frag_conf_reject_overlap(const pcap_thread_ext_frag_conf_t* conf) +{ + if (!conf) { + return 0; + } + + return conf->reject_overlap; +} + +int pcap_thread_ext_frag_conf_set_reject_overlap(pcap_thread_ext_frag_conf_t* conf, const int reject_overlap) +{ + if (!conf) { + return PCAP_THREAD_EINVAL; + } + + conf->reject_overlap = reject_overlap ? 1 : 0; + + return PCAP_THREAD_OK; +} + +int pcap_thread_ext_frag_conf_check_timeout(const pcap_thread_ext_frag_conf_t* conf) +{ + if (!conf) { + return 0; + } + + return conf->check_timeout; +} + +int pcap_thread_ext_frag_conf_set_check_timeout(pcap_thread_ext_frag_conf_t* conf, const int check_timeout) +{ + if (!conf) { + return PCAP_THREAD_EINVAL; + } + + conf->check_timeout = check_timeout ? 1 : 0; + + return PCAP_THREAD_OK; +} + +pcap_thread_ext_frag_reassemble_mode_t pcap_thread_ext_frag_conf_reassemble_mode(const pcap_thread_ext_frag_conf_t* conf) +{ + if (!conf) { + return PCAP_THREAD_EXT_FRAG_REASSEMBLE_RFC791; + } + + return conf->reassemble_mode; +} + +int pcap_thread_ext_frag_conf_set_reassemble_mode(pcap_thread_ext_frag_conf_t* conf, const pcap_thread_ext_frag_reassemble_mode_t reassemble_mode) +{ + if (!conf) { + return PCAP_THREAD_EINVAL; + } + + switch (reassemble_mode) { + case PCAP_THREAD_EXT_FRAG_REASSEMBLE_RFC791: + case PCAP_THREAD_EXT_FRAG_REASSEMBLE_BSD: + break; + case PCAP_THREAD_EXT_FRAG_REASSEMBLE_RFC815: + /* TODO: Implement */ + default: + return PCAP_THREAD_EINVAL; + } + + conf->reassemble_mode = reassemble_mode; + + return PCAP_THREAD_OK; +} + +size_t pcap_thread_ext_frag_conf_fragments(const pcap_thread_ext_frag_conf_t* conf) +{ + if (!conf) { + return -1; + } + + return conf->fragments; +} + +int pcap_thread_ext_frag_conf_set_fragments(pcap_thread_ext_frag_conf_t* conf, const size_t fragments) +{ + if (!conf) { + return PCAP_THREAD_EINVAL; + } + + conf->fragments = fragments; + + return PCAP_THREAD_OK; +} + +size_t pcap_thread_ext_frag_conf_per_packet(const pcap_thread_ext_frag_conf_t* conf) +{ + if (!conf) { + return -1; + } + + return conf->per_packet; +} + +int pcap_thread_ext_frag_conf_set_per_packet(pcap_thread_ext_frag_conf_t* conf, const size_t per_packet) +{ + if (!conf) { + return PCAP_THREAD_EINVAL; + } + + conf->per_packet = per_packet; + + return PCAP_THREAD_OK; +} + +struct timeval pcap_thread_ext_frag_conf_timeout(const pcap_thread_ext_frag_conf_t* conf) +{ + if (!conf) { + struct timeval ret = { 0, 0 }; + return ret; + } + + return conf->timeout; +} + +int pcap_thread_ext_frag_conf_set_timeout(pcap_thread_ext_frag_conf_t* conf, const struct timeval timeout) +{ + if (!conf) { + return PCAP_THREAD_EINVAL; + } + + conf->timeout = timeout; + + return PCAP_THREAD_OK; +} + +pcap_thread_ext_frag_callback_t pcap_thread_ext_frag_conf_overlap_callback(const pcap_thread_ext_frag_conf_t* conf) +{ + if (!conf) { + return 0; + } + + return conf->overlap_callback; +} + +int pcap_thread_ext_frag_conf_set_overlap_callback(pcap_thread_ext_frag_conf_t* conf, pcap_thread_ext_frag_callback_t overlap_callback) +{ + if (!conf) { + return PCAP_THREAD_EINVAL; + } + + conf->overlap_callback = overlap_callback; + + return PCAP_THREAD_OK; +} + +pcap_thread_ext_frag_callback_t pcap_thread_ext_frag_conf_timeout_callback(const pcap_thread_ext_frag_conf_t* conf) +{ + if (!conf) { + return 0; + } + + return conf->timeout_callback; +} + +int pcap_thread_ext_frag_conf_set_timeout_callback(pcap_thread_ext_frag_conf_t* conf, pcap_thread_ext_frag_callback_t timeout_callback) +{ + if (!conf) { + return PCAP_THREAD_EINVAL; + } + + conf->timeout_callback = timeout_callback; + + return PCAP_THREAD_OK; +} + +/* + * Init + */ + +pcap_thread_layer_callback_frag_t pcap_thread_ext_frag_layer_callback(pcap_thread_ext_frag_conf_t* conf) +{ + pcap_thread_layer_callback_frag_t callback = PCAP_THREAD_LAYER_CALLBACK_FRAG_T_INIT; + + if (conf) { + callback.conf = (void*)conf; + callback.new = pcap_thread_layer_callback_frag_new; + callback.free = pcap_thread_layer_callback_frag_free; + callback.reassemble = pcap_thread_layer_callback_frag_reassemble; + callback.release = pcap_thread_layer_callback_frag_release; + } + + return callback; +} + +/* + * Callbacks + */ + +#if PCAP_THREAD_EXT_FRAG_TRACE +#include <stdio.h> +#define layer_trace(msg) printf("LT %s:%d: " msg "\n", __FILE__, __LINE__) +#define layer_tracef(msg, args...) printf("LT %s:%d: " msg "\n", __FILE__, __LINE__, args) +#else +#define layer_trace(msg) +#define layer_tracef(msg, args...) +#endif + +/* TODO: +typedef struct _hole _hole_t; +struct _hole { + _hole_t* next; + + size_t first, last; +}; +*/ + +#ifdef HAVE_PTHREAD +#define PCAP_THREAD_EXT_FRAG_CTX_T_INIT_MUTEX PTHREAD_MUTEX_INITIALIZER, +#else +#define PCAP_THREAD_EXT_FRAG_CTX_T_INIT_MUTEX +#endif + +/* clang-format off */ +#define PCAP_THREAD_EXT_FRAG_CTX_T_INIT { \ + PCAP_THREAD_EXT_FRAG_CTX_T_INIT_MUTEX \ + PCAP_THREAD_EXT_FRAG_CONF_T_INIT, 0, 0 \ +} +/* clang-format on */ + +typedef struct _ctx _ctx_t; +struct _ctx { +#ifdef HAVE_PTHREAD + pthread_mutex_t mutex; +#endif + pcap_thread_ext_frag_conf_t conf; + pcap_thread_ext_frag_fragments_t* fragments; + size_t num_fragments; +}; + +static _ctx_t _ctx_defaults = PCAP_THREAD_EXT_FRAG_CTX_T_INIT; + +static void* pcap_thread_layer_callback_frag_new(void* conf, u_char* user) +{ + _ctx_t* ctx = calloc(1, sizeof(_ctx_t)); + if (ctx) { + layer_tracef("new ctx %p", ctx); + memcpy(ctx, &_ctx_defaults, sizeof(_ctx_t)); + if (conf) { + memcpy(&(ctx->conf), conf, sizeof(pcap_thread_ext_frag_conf_t)); + } + } + + return ctx; +} + +static void pcap_thread_layer_callback_frag_free(void* _ctx) +{ + _ctx_t* ctx = (_ctx_t*)_ctx; + if (ctx) { + layer_tracef("free ctx %p", ctx); + while (ctx->fragments) { + pcap_thread_ext_frag_fragments_t* frags = ctx->fragments; + ctx->fragments = frags->next; + + while (frags->fragments) { + pcap_thread_ext_frag_fragment_t* frag = frags->fragments; + frags->fragments = frag->next; + + if (frag->payload) { + free(frag->payload); + } + free(frag); + } + + if (frags->payload) { + free(frags->payload); + } + free(frags); + } + } +} + +static pcap_thread_packet_state_t reassemble(_ctx_t* ctx, const pcap_thread_packet_t* packet, pcap_thread_packet_t** whole_packet, const u_char** whole_payload, size_t* whole_length, pcap_thread_ext_frag_fragments_t* frags, pcap_thread_ext_frag_fragment_t* frag) +{ + pcap_thread_ext_frag_fragment_t *f, *f_prev; + int missing_frag = 0; + /* TODO: + int rfc815_seen_no_more_frags = 0; + */ + + if ((frag->offset + frag->length) > frags->length) { + frags->length = frag->offset + frag->length; + } + + layer_tracef("new frag len %lu off %lu mf %d (frags len %lu)", frag->length, frag->offset, frag->flag_more_fragments, frags->length); + + /* Place the fragment in the fragments list */ + switch (ctx->conf.reassemble_mode) { + case PCAP_THREAD_EXT_FRAG_REASSEMBLE_RFC791: + for (f_prev = 0, f = frags->fragments; f; f_prev = f, f = f->next) { + layer_tracef("checking frag %p len %lu off %lu mf %d next %p", f, f->length, f->offset, f->flag_more_fragments, f->next); + + if (f->offset > frag->offset) { + if (f_prev) { + f_prev->next = frag; + } else { + frags->fragments = frag; + } + frag->next = f; + f = frag; + break; + } + if (f_prev && (f_prev->offset + f_prev->length) < f->offset) { + missing_frag = 1; + } + } + if (!f) { + if (f_prev) { + f_prev->next = frag; + if ((f_prev->offset + f_prev->length) < frag->offset) { + missing_frag = 1; + } + } else { + frags->fragments = frag; + } + /* New frag is now last frag */ + f_prev = frag; + } else if (!missing_frag) { + for (; f; f_prev = f, f = f->next) { + layer_tracef("checking frag %p len %lu off %lu mf %d next %p", f, f->length, f->offset, f->flag_more_fragments, f->next); + if (f_prev && (f_prev->offset + f_prev->length) < f->offset) { + missing_frag = 1; + break; + } + } + } + /* + * If first is not offset zero or last have more fragments flag, + * we are missing fragments. + */ + if (!missing_frag && (frags->fragments->offset || (f_prev && f_prev->flag_more_fragments))) { + missing_frag = 1; + } + break; + case PCAP_THREAD_EXT_FRAG_REASSEMBLE_RFC815: + /* TODO: + for (f_prev = 0, f = frags->fragments; f; f_prev = f, f = f->next) { + layer_tracef("checking frag %p len %lu off %lu mf %d next %p", f, f->length, f->offset, f->flag_more_fragments, f->next); + + if (!f->flag_more_fragments) { + rfc815_seen_no_more_frags = 1; + } + } + */ + free(frag->payload); + free(frag); + return PCAP_THREAD_EINVAL; + break; + case PCAP_THREAD_EXT_FRAG_REASSEMBLE_BSD: + for (f_prev = 0, f = frags->fragments; f; f_prev = f, f = f->next) { + layer_tracef("checking frag %p len %lu off %lu mf %d next %p", f, f->length, f->offset, f->flag_more_fragments, f->next); + + if (f->offset > frag->offset) { + if (f_prev) { + f_prev->next = frag; + } else { + frags->fragments = frag; + } + frag->next = f; + f = frag; + break; + } + if (f_prev && (f->offset + f->length) < f_prev->offset) { + missing_frag = 1; + } + } + if (!f) { + if (f_prev) { + f_prev->next = frag; + if ((frag->offset + frag->length) < f_prev->offset) { + missing_frag = 1; + } + } else { + frags->fragments = frag; + } + } else if (!missing_frag) { + for (; f; f_prev = f, f = f->next) { + layer_tracef("checking frag %p len %lu off %lu mf %d next %p", f, f->length, f->offset, f->flag_more_fragments, f->next); + if (f_prev && (f->offset + f->length) < f_prev->offset) { + missing_frag = 1; + break; + } + } + } + /* + * If first (last on list) is not offset zero or last (first on + * list) have more fragments flag, we are missing fragments. + */ + if (!missing_frag && ((f_prev && f_prev->offset) || frags->fragments->flag_more_fragments)) { + missing_frag = 1; + } + break; + } + frags->num_fragments++; + + if (missing_frag) { + layer_trace("need more frags"); + return PCAP_THREAD_PACKET_OK; + } + + if (!frags->length) { + layer_trace("frags complete but no size"); + return PCAP_THREAD_PACKET_INVALID_FRAGMENT; + } + + if (ctx->conf.reject_overlap) { + switch (ctx->conf.reassemble_mode) { + case PCAP_THREAD_EXT_FRAG_REASSEMBLE_RFC791: + for (f_prev = 0, f = frags->fragments; f; f_prev = f, f = f->next) { + layer_tracef("checking frag %p len %lu off %lu mf %d next %p", f, f->length, f->offset, f->flag_more_fragments, f->next); + if (f_prev && (f_prev->offset + f_prev->length) > f->offset) { + layer_trace("overlapping fragment"); + if (ctx->conf.overlap_callback) + ctx->conf.overlap_callback(packet, frag->payload, frag->length, frags); + return PCAP_THREAD_PACKET_INVALID_FRAGMENT; + } + } + break; + case PCAP_THREAD_EXT_FRAG_REASSEMBLE_RFC815: + /* TODO: + */ + break; + case PCAP_THREAD_EXT_FRAG_REASSEMBLE_BSD: + for (f_prev = 0, f = frags->fragments; f; f_prev = f, f = f->next) { + layer_tracef("checking frag %p len %lu off %lu mf %d next %p", f, f->length, f->offset, f->flag_more_fragments, f->next); + if (f_prev && (f->offset + f->length) > f_prev->offset) { + layer_trace("overlapping fragment"); + if (ctx->conf.overlap_callback) + ctx->conf.overlap_callback(packet, frag->payload, frag->length, frags); + return PCAP_THREAD_PACKET_INVALID_FRAGMENT; + } + } + break; + } + } + + /* + * Reassemble packet + */ + if (!(frags->payload = calloc(1, frags->length))) { + layer_trace("nomem frags payload"); + return PCAP_THREAD_PACKET_ENOMEM; + } + for (f = frags->fragments; f; f = f->next) { + memcpy(frags->payload + f->offset, f->payload, f->length); + } + + frags->packet.name = packet->name; + frags->packet.dlt = packet->dlt; + frags->packet.pkthdr = packet->pkthdr; + /* + * We add the total payload length minus current fragment, since it is + * already included, to the pkthdr lengths in order to return correct + * total packet length (header + payload). + */ + frags->packet.pkthdr.len += frags->length - frag->length; + frags->packet.pkthdr.caplen += frags->length - frag->length; + frags->packet.have_pkthdr = packet->have_pkthdr; + + *whole_packet = &(frags->packet); + *whole_payload = frags->payload; + *whole_length = frags->length; + + return PCAP_THREAD_PACKET_OK; +} + +static pcap_thread_packet_state_t reassemble_ipv4(_ctx_t* ctx, const pcap_thread_packet_t* packet, const u_char* payload, size_t length, pcap_thread_packet_t** whole_packet, const u_char** whole_payload, size_t* whole_length) +{ + pcap_thread_ext_frag_fragments_t *frags, *frags_prev; + pcap_thread_ext_frag_fragment_t* frag; + + if (!packet->have_pkthdr) { + layer_trace("no pkthdr"); + return PCAP_THREAD_PACKET_INVALID; + } + + layer_tracef("ipv4 ctx %p", ctx); + + /* Find packet fragments */ + for (frags_prev = 0, frags = ctx->fragments; frags; frags_prev = frags, frags = frags->next) { + if (frags->packet.have_iphdr + && packet->iphdr.ip_id == frags->packet.iphdr.ip_id + && packet->iphdr.ip_p == frags->packet.iphdr.ip_p + && packet->iphdr.ip_src.s_addr == frags->packet.iphdr.ip_src.s_addr + && packet->iphdr.ip_dst.s_addr == frags->packet.iphdr.ip_dst.s_addr) { + + layer_tracef("frags %d found", packet->iphdr.ip_id); + + /* Found it, remove from list */ + if (frags_prev) { + frags_prev->next = frags->next; + } + if (ctx->fragments == frags) { + ctx->fragments = frags->next; + } + frags->next = 0; + break; + } + } + + /* Check if frags is timed out */ + if (ctx->conf.check_timeout && frags) { + struct timeval ts; + + ts = frags->packet.pkthdr.ts; + ts.tv_sec += ctx->conf.timeout.tv_sec; + ts.tv_usec += ctx->conf.timeout.tv_usec; + ts.tv_usec %= 1000000; + if (packet->pkthdr.ts.tv_sec > ts.tv_sec + || (packet->pkthdr.ts.tv_sec == ts.tv_sec + && packet->pkthdr.ts.tv_usec > ts.tv_usec)) { + + pcap_thread_ext_frag_fragment_t* f; + + layer_tracef("frags timed out (last: %lu.%lu, this: %lu.%lu)", + frags->packet.pkthdr.ts.tv_sec, frags->packet.pkthdr.ts.tv_usec, + packet->pkthdr.ts.tv_sec, packet->pkthdr.ts.tv_usec); + + if (ctx->conf.timeout_callback) + ctx->conf.timeout_callback(packet, payload, length, frags); + + for (f = frags->fragments; f;) { + frag = f; + f = f->next; + if (frag->payload) { + free(frag->payload); + } + free(frag); + } + + if (frags->payload) { + free(frags->payload); + } + free(frags); + frags = 0; + } else { + frags->packet.pkthdr.ts = packet->pkthdr.ts; + } + } + + /* No fragments found, create new */ + if (!frags) { + if (ctx->num_fragments >= ctx->conf.fragments) { + layer_trace("too many frags"); + return PCAP_THREAD_PACKET_INVALID_FRAGMENT; + } + + if (!(frags = calloc(1, sizeof(pcap_thread_ext_frag_fragments_t)))) { + layer_trace("nomem frags"); + return PCAP_THREAD_PACKET_ENOMEM; + } + + layer_tracef("new frags %d", packet->iphdr.ip_id); + + // TODO: How to handle prevpkt + memcpy(&(frags->packet.iphdr), &(packet->iphdr), sizeof(struct ip)); + frags->packet.have_iphdr = 1; + frags->packet.pkthdr.ts = packet->pkthdr.ts; + + ctx->num_fragments++; + } + /* Put the fragments first on the list */ + frags->next = ctx->fragments; + ctx->fragments = frags; + + if (frags->payload) { + layer_trace("already reassembled"); + return PCAP_THREAD_PACKET_INVALID_FRAGMENT; + } + + if (frags->num_fragments >= ctx->conf.per_packet) { + layer_trace("too many frags frag"); + return PCAP_THREAD_PACKET_INVALID_FRAGMENT; + } + + /* Allocate for the new fragment */ + if (!(frag = calloc(1, sizeof(pcap_thread_ext_frag_fragment_t)))) { + layer_trace("nomem frag"); + return PCAP_THREAD_PACKET_ENOMEM; + } + if (!(frag->payload = calloc(1, length))) { + free(frag); + layer_trace("nomem frag"); + return PCAP_THREAD_PACKET_ENOMEM; + } + memcpy(frag->payload, payload, length); + frag->length = length; + frag->offset = (packet->iphdr.ip_off & 0x1fff) * 8; + frag->flag_more_fragments = packet->iphdr.ip_off & 0x2000 ? 1 : 0; + + return reassemble(ctx, packet, whole_packet, whole_payload, whole_length, frags, frag); +} + +static pcap_thread_packet_state_t reassemble_ipv6(_ctx_t* ctx, const pcap_thread_packet_t* packet, const u_char* payload, size_t length, pcap_thread_packet_t** whole_packet, const u_char** whole_payload, size_t* whole_length) +{ + pcap_thread_ext_frag_fragments_t *frags, *frags_prev; + pcap_thread_ext_frag_fragment_t* frag; + + layer_tracef("ipv6 ctx %p", ctx); + + /* Find packet fragments */ + for (frags_prev = 0, frags = ctx->fragments; frags; frags_prev = frags, frags = frags->next) { + if (frags->packet.have_ip6hdr + && packet->ip6frag.ip6f_ident == frags->packet.ip6frag.ip6f_ident + && !memcmp(&(packet->ip6hdr.ip6_src), &(frags->packet.ip6hdr.ip6_src), sizeof(struct in6_addr)) + && ((!packet->have_ip6rtdst && !memcmp(&(packet->ip6hdr.ip6_dst), &(frags->packet.ip6hdr.ip6_dst), sizeof(struct in6_addr))) + || (packet->have_ip6rtdst && !memcmp(&(packet->ip6rtdst), &(frags->packet.ip6hdr.ip6_dst), sizeof(struct in6_addr))))) { + + layer_tracef("frags %x found", packet->ip6frag.ip6f_ident); + + /* Found it, remove from list */ + if (frags_prev) { + frags_prev->next = frags->next; + } + if (ctx->fragments == frags) { + ctx->fragments = frags->next; + } + frags->next = 0; + break; + } + } + + /* Check if frags is timed out */ + if (ctx->conf.check_timeout && frags) { + struct timeval ts; + + ts = frags->packet.pkthdr.ts; + ts.tv_sec += ctx->conf.timeout.tv_sec; + ts.tv_usec += ctx->conf.timeout.tv_usec; + ts.tv_usec %= 1000000; + if (packet->pkthdr.ts.tv_sec > ts.tv_sec + || (packet->pkthdr.ts.tv_sec == ts.tv_sec + && packet->pkthdr.ts.tv_usec > ts.tv_usec)) { + + pcap_thread_ext_frag_fragment_t* f; + + layer_tracef("frags timed out (last: %lu.%lu, this: %lu.%lu)", + frags->packet.pkthdr.ts.tv_sec, frags->packet.pkthdr.ts.tv_usec, + packet->pkthdr.ts.tv_sec, packet->pkthdr.ts.tv_usec); + + if (ctx->conf.timeout_callback) + ctx->conf.timeout_callback(packet, payload, length, frags); + + for (f = frags->fragments; f;) { + frag = f; + f = f->next; + if (frag->payload) { + free(frag->payload); + } + free(frag); + } + + if (frags->payload) { + free(frags->payload); + } + free(frags); + frags = 0; + } else { + frags->packet.pkthdr.ts = packet->pkthdr.ts; + } + } + + /* No fragments found, create new */ + if (!frags) { + if (ctx->num_fragments >= ctx->conf.fragments) { + layer_trace("too many frags"); + return PCAP_THREAD_PACKET_INVALID_FRAGMENT; + } + + if (!(frags = calloc(1, sizeof(pcap_thread_ext_frag_fragments_t)))) { + layer_trace("nomem frags"); + return PCAP_THREAD_PACKET_ENOMEM; + } + + layer_tracef("new frags %x", packet->ip6frag.ip6f_ident); + + // TODO: How to handle prevpkt + memcpy(&(frags->packet.ip6hdr), &(packet->ip6hdr), sizeof(struct ip6_hdr)); + frags->packet.have_ip6hdr = 1; + memcpy(&(frags->packet.ip6frag), &(packet->ip6frag), sizeof(struct ip6_frag)); + frags->packet.have_ip6frag = 1; + frags->packet.ip6frag_payload = packet->ip6frag_payload; + if (packet->have_ip6rtdst) { + frags->packet.ip6hdr.ip6_dst = packet->ip6rtdst; + } + frags->packet.pkthdr.ts = packet->pkthdr.ts; + + ctx->num_fragments++; + } else { + if (frags->packet.ip6frag_payload != packet->ip6frag_payload) { + layer_trace("wrong payload"); + return PCAP_THREAD_PACKET_INVALID_FRAGMENT; + } + } + /* Put the fragments first on the list */ + frags->next = ctx->fragments; + ctx->fragments = frags; + + if (frags->payload) { + layer_trace("already reassembled"); + return PCAP_THREAD_PACKET_INVALID_FRAGMENT; + } + + if (frags->num_fragments >= ctx->conf.per_packet) { + layer_trace("too many frags frag"); + return PCAP_THREAD_PACKET_INVALID_FRAGMENT; + } + + /* Allocate for the new fragment */ + if (!(frag = calloc(1, sizeof(pcap_thread_ext_frag_fragment_t)))) { + layer_trace("nomem frag"); + return PCAP_THREAD_PACKET_ENOMEM; + } + if (!(frag->payload = calloc(1, length))) { + free(frag); + layer_trace("nomem frag"); + return PCAP_THREAD_PACKET_ENOMEM; + } + memcpy(frag->payload, payload, length); + frag->length = length; + frag->offset = ((packet->ip6frag.ip6f_offlg & 0xfff8) >> 3) * 8; + frag->flag_more_fragments = packet->ip6frag.ip6f_offlg & 0x1 ? 1 : 0; + + return reassemble(ctx, packet, whole_packet, whole_payload, whole_length, frags, frag); +} + +#ifdef HAVE_PTHREAD /* _release() is only used when mutex functions fails */ +static void _release(_ctx_t* ctx, const pcap_thread_packet_t* packet) +{ + pcap_thread_ext_frag_fragments_t *frags, *frags_prev; + + layer_tracef("release ctx %p", ctx); + + /* Find packet fragments */ + for (frags_prev = 0, frags = ctx->fragments; frags; frags_prev = frags, frags = frags->next) { + if (frags->packet.have_iphdr + && packet->iphdr.ip_id == frags->packet.iphdr.ip_id + && packet->iphdr.ip_p == frags->packet.iphdr.ip_p + && packet->iphdr.ip_src.s_addr == frags->packet.iphdr.ip_src.s_addr + && packet->iphdr.ip_dst.s_addr == frags->packet.iphdr.ip_dst.s_addr) { + + layer_tracef("release frags %d", packet->iphdr.ip_id); + break; + } else if (frags->packet.have_ip6hdr + && packet->ip6frag.ip6f_ident == frags->packet.ip6frag.ip6f_ident + && !memcmp(&(packet->ip6hdr.ip6_src), &(frags->packet.ip6hdr.ip6_src), sizeof(struct in6_addr)) + && ((!packet->have_ip6rtdst && !memcmp(&(packet->ip6hdr.ip6_dst), &(frags->packet.ip6hdr.ip6_dst), sizeof(struct in6_addr))) + || (packet->have_ip6rtdst && !memcmp(&(packet->ip6rtdst), &(frags->packet.ip6hdr.ip6_dst), sizeof(struct in6_addr))))) { + + layer_tracef("release frags %x", packet->ip6frag.ip6f_ident); + break; + } + } + + if (frags) { + pcap_thread_ext_frag_fragment_t *frag, *f; + + /* Found it, remove from list */ + if (frags_prev) { + frags_prev->next = frags->next; + } + if (ctx->fragments == frags) { + ctx->fragments = frags->next; + } + frags->next = 0; + ctx->num_fragments--; + + for (f = frags->fragments; f;) { + frag = f; + f = f->next; + if (frag->payload) { + free(frag->payload); + } + free(frag); + } + + if (frags->payload) { + free(frags->payload); + } + free(frags); + } +} +#endif + +static pcap_thread_packet_state_t pcap_thread_layer_callback_frag_reassemble(void* _ctx, const pcap_thread_packet_t* packet, const u_char* payload, size_t length, pcap_thread_packet_t** whole_packet, const u_char** whole_payload, size_t* whole_length) +{ + _ctx_t* ctx = (_ctx_t*)_ctx; + pcap_thread_packet_state_t state = PCAP_THREAD_PACKET_INVALID; + + if (!ctx) { + return PCAP_THREAD_PACKET_INVALID; + } + if (!packet) { + return PCAP_THREAD_PACKET_INVALID; + } + if (!payload) { + return PCAP_THREAD_PACKET_INVALID; + } + if (!length) { + return PCAP_THREAD_PACKET_INVALID; + } + if (!whole_packet) { + return PCAP_THREAD_PACKET_INVALID; + } + if (!whole_payload) { + return PCAP_THREAD_PACKET_INVALID; + } + if (!whole_length) { + return PCAP_THREAD_PACKET_INVALID; + } + + if (ctx && packet && payload && length + && whole_packet && whole_payload && whole_length) { + if (packet->have_iphdr) { +#ifdef HAVE_PTHREAD + if (pthread_mutex_lock(&(ctx->mutex))) { + return PCAP_THREAD_PACKET_EMUTEX; + } +#endif + state = reassemble_ipv4(ctx, packet, payload, length, whole_packet, whole_payload, whole_length); +#ifdef HAVE_PTHREAD + if (pthread_mutex_unlock(&(ctx->mutex))) { + if (state == PCAP_THREAD_PACKET_OK && *whole_packet && *whole_payload && *whole_length) { + _release(ctx, *whole_packet); + } + return PCAP_THREAD_PACKET_EMUTEX; + } +#endif + } else if (packet->have_ip6hdr && packet->have_ip6frag) { +#ifdef HAVE_PTHREAD + if (pthread_mutex_lock(&(ctx->mutex))) { + return PCAP_THREAD_PACKET_EMUTEX; + } +#endif + state = reassemble_ipv6(ctx, packet, payload, length, whole_packet, whole_payload, whole_length); +#ifdef HAVE_PTHREAD + if (pthread_mutex_unlock(&(ctx->mutex))) { + if (state == PCAP_THREAD_PACKET_OK && *whole_packet && *whole_payload && *whole_length) { + _release(ctx, *whole_packet); + } + return PCAP_THREAD_PACKET_EMUTEX; + } +#endif + } + } + + return state; +} + +static void pcap_thread_layer_callback_frag_release(void* _ctx, const pcap_thread_packet_t* packet, const u_char* payload, size_t length) +{ + _ctx_t* ctx = (_ctx_t*)_ctx; + pcap_thread_ext_frag_fragments_t *frags, *frags_prev; + + if (!ctx) { + return; + } + if (!packet) { + return; + } + if (packet->have_ip6hdr) { + if (!packet->have_ip6frag) { + return; + } + } else if (!packet->have_iphdr) { + return; + } + +#ifdef HAVE_PTHREAD + if (pthread_mutex_lock(&(ctx->mutex))) { + return; + } +#endif + + /* Find packet fragments */ + for (frags_prev = 0, frags = ctx->fragments; frags; frags_prev = frags, frags = frags->next) { + if ((frags->packet.have_iphdr + && packet->iphdr.ip_id == frags->packet.iphdr.ip_id + && packet->iphdr.ip_p == frags->packet.iphdr.ip_p + && packet->iphdr.ip_src.s_addr == frags->packet.iphdr.ip_src.s_addr + && packet->iphdr.ip_dst.s_addr == frags->packet.iphdr.ip_dst.s_addr) + || (frags->packet.have_ip6hdr + && packet->ip6frag.ip6f_ident == frags->packet.ip6frag.ip6f_ident + && !memcmp(&(packet->ip6hdr.ip6_src), &(frags->packet.ip6hdr.ip6_src), sizeof(struct in6_addr)) + && ((!packet->have_ip6rtdst && !memcmp(&(packet->ip6hdr.ip6_dst), &(frags->packet.ip6hdr.ip6_dst), sizeof(struct in6_addr))) + || (packet->have_ip6rtdst && !memcmp(&(packet->ip6rtdst), &(frags->packet.ip6hdr.ip6_dst), sizeof(struct in6_addr)))))) { + + /* Found it, remove from list */ + if (frags_prev) { + frags_prev->next = frags->next; + } + if (ctx->fragments == frags) { + ctx->fragments = frags->next; + } + frags->next = 0; + ctx->num_fragments--; + break; + } + } + +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&(ctx->mutex)); +#endif + + if (frags) { + pcap_thread_ext_frag_fragment_t *frag, *f; + + for (f = frags->fragments; f;) { + frag = f; + f = f->next; + if (frag->payload) { + free(frag->payload); + } + free(frag); + } + + if (frags->payload) { + free(frags->payload); + } + free(frags); + } +} |