diff options
author | Larry Hajali <larryhaja[at]gmail[dot]com> | 2010-05-12 23:32:35 +0200 |
---|---|---|
committer | Robby Workman <rworkman@slackbuilds.org> | 2010-05-12 23:32:35 +0200 |
commit | 3a3ed9f8e4c726078706bf6c6301d63bf5f58f48 (patch) | |
tree | 60d4a00b3473836fcfe935557d7ad49c40b10954 /network/ngrep/ngrep-1.45-reasm.diff | |
parent | 819a1f0296029fb2ab22fbeaae8ed264c5a8f957 (diff) |
network/ngrep: Added to 12.2 repository
Diffstat (limited to 'network/ngrep/ngrep-1.45-reasm.diff')
-rw-r--r-- | network/ngrep/ngrep-1.45-reasm.diff | 939 |
1 files changed, 939 insertions, 0 deletions
diff --git a/network/ngrep/ngrep-1.45-reasm.diff b/network/ngrep/ngrep-1.45-reasm.diff new file mode 100644 index 0000000000000..ed33fb6f651e5 --- /dev/null +++ b/network/ngrep/ngrep-1.45-reasm.diff @@ -0,0 +1,939 @@ +diff -urN ngrep-1.45/ipreasm.c ngrep-1.45-reasm/ipreasm.c +--- ngrep-1.45/ipreasm.c 1970-01-01 01:00:00.000000000 +0100 ++++ ngrep-1.45-reasm/ipreasm.c 2007-06-16 19:17:20.124795623 +0200 +@@ -0,0 +1,717 @@ ++/* ++ * ipreasm -- Routines for reassembly of fragmented IPv4 and IPv6 packets. ++ * ++ * Copyright (c) 2007 Jan Andres <jandres@gmx.net> ++ * ++ */ ++ ++#include <stdio.h> ++#include <stdlib.h> ++#include <string.h> ++#include <stddef.h> ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif /* HAVE_CONFIG_H */ ++ ++#include <netinet/ip.h> ++#include <netinet/udp.h> ++#if USE_IPv6 ++#include <netinet/ip6.h> ++#endif /* USE_IPv6 */ ++ ++#include "ipreasm.h" ++ ++ ++#define REASM_IP_HASH_SIZE 1021U ++ ++ ++enum entry_state { ++ STATE_ACTIVE, ++ STATE_INVALID, ++}; ++ ++ ++enum reasm_proto { ++ PROTO_IPV4, ++#if USE_IPv6 ++ PROTO_IPV6, ++#endif /* USE_IPv6 */ ++}; ++ ++ ++/* ++ * This tuple uniquely identifies all fragments belonging to ++ * the same IPv4 packet. ++ */ ++struct reasm_id_ipv4 { ++ uint8_t ip_src[4], ip_dst[4]; ++ uint16_t ip_id; ++ uint8_t ip_proto; ++}; ++ ++ ++/* ++ * Same for IPv6. ++ */ ++struct reasm_id_ipv6 { ++ uint8_t ip_src[16], ip_dst[16]; ++ uint32_t ip_id; ++}; ++ ++ ++union reasm_id { ++ struct reasm_id_ipv4 ipv4; ++ struct reasm_id_ipv6 ipv6; ++}; ++ ++ ++struct reasm_frag_entry { ++ unsigned len; /* payload length of this fragment */ ++ unsigned offset; /* offset of this fragment into the payload of the reassembled packet */ ++ unsigned data_offset; /* offset to the data pointer where payload starts */ ++ unsigned char *data; /* payload starts at data + data_offset */ ++ struct reasm_frag_entry *next; ++}; ++ ++ ++/* ++ * Reception of a complete packet is detected by counting the number ++ * of "holes" that remain between the cached fragments. A hole is ++ * assumed to exist at the upper end of the packet until the final ++ * fragment has been received. When the number of holes drops to 0, ++ * all fragments have been received and the packet can be reassembled. ++ */ ++struct reasm_ip_entry { ++ union reasm_id id; ++ unsigned len, holes, frag_count, hash; ++ reasm_time_t timeout; ++ enum entry_state state; ++ enum reasm_proto protocol; ++ struct reasm_frag_entry *frags; ++ struct reasm_ip_entry *prev, *next; ++ struct reasm_ip_entry *time_prev, *time_next; ++}; ++ ++ ++/* ++ * This struct contains some metadata, the main hash table, and a pointer ++ * to the first entry that will time out. A linked list is kept in the ++ * order in which packets will time out. Using a linked list for this ++ * purpose requires that packets are input in chronological order, and ++ * that a constant timeout value is used, which doesn't change even when ++ * the entry's state transitions from active to invalid. ++ */ ++struct reasm_ip { ++ struct reasm_ip_entry *table[REASM_IP_HASH_SIZE]; ++ struct reasm_ip_entry *time_first, *time_last; ++ unsigned waiting, max_waiting, timed_out, dropped_frags; ++ reasm_time_t timeout; ++}; ++ ++ ++/* ++ * Hash functions. ++ */ ++static unsigned reasm_ipv4_hash (const struct reasm_id_ipv4 *id); ++#if USE_IPv6 ++static unsigned reasm_ipv6_hash (const struct reasm_id_ipv6 *id); ++#endif /* USE_IPv6 */ ++ ++/* ++ * Insert a new fragment to the correct position in the list of fragments. ++ * Check for fragment overlap and other error conditions. Update the ++ * "hole count". ++ */ ++static bool add_fragment (struct reasm_ip_entry *entry, struct reasm_frag_entry *frag, bool last_frag); ++ ++/* ++ * Is the entry complete, ready for reassembly? ++ */ ++static bool is_complete (struct reasm_ip_entry *entry); ++ ++/* ++ * Create the reassembled packet. ++ */ ++static unsigned char *assemble (struct reasm_ip_entry *entry, unsigned *output_len); ++ ++/* ++ * Drop and free entries. ++ */ ++static void drop_entry (struct reasm_ip *reasm, struct reasm_ip_entry *entry); ++static void free_entry (struct reasm_ip_entry *entry); ++ ++/* ++ * Dispose of any entries which have expired before "now". ++ */ ++static void process_timeouts (struct reasm_ip *reasm, reasm_time_t now); ++ ++/* ++ * Create fragment structure from IPv6 packet. Returns NULL if the input ++ * is not a fragment. ++ * This function is called by parse_packet(), don't call it directly. ++ */ ++#if USE_IPv6 ++static struct reasm_frag_entry *frag_from_ipv6 (unsigned char *packet, uint32_t *ip_id, bool *last_frag); ++#endif /* USE_IPv6 */ ++ ++/* ++ * Compare packet identification tuples for specified protocol. ++ */ ++static bool reasm_id_equal (enum reasm_proto proto, const union reasm_id *left, const union reasm_id *right); ++ ++/* ++ * Create fragment structure from an IPv4 or IPv6 packet. Returns NULL ++ * if the input is not a fragment. ++ */ ++static struct reasm_frag_entry *parse_packet (unsigned char *packet, unsigned len, enum reasm_proto *protocol, union reasm_id *id, unsigned *hash, bool *last_frag); ++ ++ ++static unsigned ++reasm_ipv4_hash (const struct reasm_id_ipv4 *id) ++{ ++ unsigned hash = 0; ++ int i; ++ ++ for (i = 0; i < 4; i++) { ++ hash = 37U * hash + id->ip_src[i]; ++ hash = 37U * hash + id->ip_dst[i]; ++ } ++ ++ hash = 59U * hash + id->ip_id; ++ ++ hash = 47U * hash + id->ip_proto; ++ ++ return hash; ++} ++ ++ ++#if USE_IPv6 ++static unsigned ++reasm_ipv6_hash (const struct reasm_id_ipv6 *id) ++{ ++ unsigned hash = 0; ++ int i; ++ ++ for (i = 0; i < 16; i++) { ++ hash = 37U * hash + id->ip_src[i]; ++ hash = 37U * hash + id->ip_dst[i]; ++ } ++ ++ hash = 59U * hash + id->ip_id; ++ ++ return hash; ++} ++#endif /* USE_IPv6 */ ++ ++ ++unsigned char * ++reasm_ip_next (struct reasm_ip *reasm, unsigned char *packet, unsigned len, reasm_time_t timestamp, unsigned *output_len) ++{ ++ enum reasm_proto proto; ++ union reasm_id id; ++ unsigned hash; ++ bool last_frag; ++ ++ process_timeouts (reasm, timestamp); ++ ++ struct reasm_frag_entry *frag = parse_packet (packet, len, &proto, &id, &hash, &last_frag); ++ if (frag == NULL) { ++ *output_len = len; ++ return packet; /* some packet that we don't recognize as a fragment */ ++ } ++ ++ hash %= REASM_IP_HASH_SIZE; ++ struct reasm_ip_entry *entry = reasm->table[hash]; ++ while (entry != NULL && (proto != entry->protocol || !reasm_id_equal (proto, &id, &entry->id))) ++ entry = entry->next; ++ ++ if (entry == NULL) { ++ entry = malloc (sizeof (*entry)); ++ if (entry == NULL) { ++ free (frag); ++ abort (); ++ } ++ ++ struct reasm_frag_entry *list_head = malloc (sizeof (*list_head)); ++ if (list_head == NULL) { ++ free (frag); ++ free (entry); ++ abort (); ++ } ++ ++ *entry = (struct reasm_ip_entry) { ++ .id = id, ++ .len = 0, ++ .holes = 1, ++ .frags = list_head, ++ .hash = hash, ++ .protocol = proto, ++ .timeout = timestamp + reasm->timeout, ++ .state = STATE_ACTIVE, ++ .prev = NULL, ++ .next = reasm->table[hash], ++ .time_prev = reasm->time_last, ++ .time_next = NULL, ++ }; ++ ++ *list_head = (struct reasm_frag_entry) { ++ .len = 0, ++ .offset = 0, ++ .data_offset = 0, ++ .data = NULL, ++ }; ++ ++ if (entry->next != NULL) ++ entry->next->prev = entry; ++ reasm->table[hash] = entry; ++ ++ if (reasm->time_last != NULL) ++ reasm->time_last->time_next = entry; ++ else ++ reasm->time_first = entry; ++ reasm->time_last = entry; ++ ++ reasm->waiting++; ++ if (reasm->waiting > reasm->max_waiting) ++ reasm->max_waiting = reasm->waiting; ++ } ++ ++ if (entry->state != STATE_ACTIVE) { ++ reasm->dropped_frags++; ++ return NULL; ++ } ++ ++ if (!add_fragment (entry, frag, last_frag)) { ++ entry->state = STATE_INVALID; ++ reasm->dropped_frags += entry->frag_count + 1; ++ return NULL; ++ } ++ ++ if (!is_complete (entry)) ++ return NULL; ++ ++ unsigned char *r = assemble (entry, output_len); ++ drop_entry (reasm, entry); ++ return r; ++} ++ ++ ++static bool ++add_fragment (struct reasm_ip_entry *entry, struct reasm_frag_entry *frag, bool last_frag) ++{ ++ /* ++ * When a fragment is inserted into the list, different cases can occur ++ * concerning the number of holes. ++ * - The new fragment can be inserted in the middle of a hole, such that ++ * it will split the hole in two. The number of holes increases by 1. ++ * - The new fragment can be attached to one end of a hole, such that ++ * the rest of the hole remains at the opposite side of the fragment. ++ * The number of holes remains constant. ++ * - The new fragment can fill a hole completely. The number of holes ++ * decreases by 1. ++ */ ++ ++ /* ++ * If more fragments follow and the payload size is not an integer ++ * multiple of 8, the packet will never be reassembled completely. ++ */ ++ if (!last_frag && (frag->len & 7) != 0) ++ return false; ++ ++ if (entry->len != 0 && frag->len + frag->offset > entry->len) ++ return false; /* fragment extends past end of packet */ ++ ++ bool fit_left = false, fit_right = false; ++ ++ if (last_frag) { ++ if (entry->len != 0) { ++ fprintf (stderr, "* ERROR: Multiple final fragments.\n"); ++ return false; ++ } ++ entry->len = frag->offset + frag->len; ++ fit_right = true; ++ } ++ ++ struct reasm_frag_entry *cur = entry->frags, *next = cur->next; ++ ++ while (cur->next != NULL && cur->next->offset <= frag->offset) ++ cur = cur->next; ++ next = cur->next; ++ ++ /* Fragment is to be inserted between cur and next; next may be NULL. */ ++ ++ /* Overlap checks. */ ++ if (cur->offset + cur->len > frag->offset) ++ return false; /* overlaps with cur */ ++ else if (cur->offset + cur->len == frag->offset) ++ fit_left = true; ++ ++ if (next != NULL) { ++ if (last_frag) ++ return false; /* next extends past end of packet */ ++ if (frag->offset + frag->len > next->offset) ++ return false; /* overlaps with next */ ++ else if (frag->offset + frag->len == next->offset) ++ fit_right = true; ++ } ++ ++ /* ++ * Everything's fine, insert it. ++ */ ++ if (frag->len != 0) { ++ frag->next = cur->next; ++ cur->next = frag; ++ ++ if (fit_left && fit_right) ++ entry->holes--; ++ else if (!fit_left && !fit_right) ++ entry->holes++; ++ ++ entry->frag_count++; ++ } else { ++ /* ++ * If the fragment has zero size, we don't insert it into the list, ++ * but one case remains to be handled: If the zero-size fragment ++ * is the last fragment, and fits exactly with the fragment to its ++ * left, the number of holes decreases. ++ */ ++ if (last_frag && fit_left) ++ entry->holes--; ++ } ++ ++ ++ return true; ++} ++ ++ ++struct reasm_ip * ++reasm_ip_new (void) ++{ ++ struct reasm_ip *reasm = malloc (sizeof (*reasm)); ++ if (reasm == NULL) ++ return NULL; ++ ++ memset (reasm, 0, sizeof (*reasm)); ++ return reasm; ++} ++ ++ ++void ++reasm_ip_free (struct reasm_ip *reasm) ++{ ++ while (reasm->time_first != NULL) ++ drop_entry (reasm, reasm->time_first); ++ free (reasm); ++} ++ ++ ++static bool ++is_complete (struct reasm_ip_entry *entry) ++{ ++ return entry->holes == 0; ++} ++ ++ ++static unsigned char * ++assemble (struct reasm_ip_entry *entry, unsigned *output_len) ++{ ++ struct reasm_frag_entry *frag = entry->frags->next; /* skip list head */ ++ unsigned offset0 = frag->data_offset; ++ unsigned char *p = malloc (entry->len + offset0); ++ if (p == NULL) ++ abort (); ++ ++ switch (entry->protocol) { ++ case PROTO_IPV4: ++ break; ++ ++#if USE_IPv6 ++ case PROTO_IPV6: ++ offset0 -= 8; /* size of frag header */ ++ break; ++#endif /* USE_IPv6 */ ++ ++ default: ++ abort (); ++ } ++ ++ *output_len = entry->len + offset0; ++ ++ /* copy the (unfragmentable) header from the first fragment received */ ++ memcpy (p, frag->data, offset0); ++ ++ /* join all the payload fragments together */ ++ while (frag != NULL) { ++ memcpy (p + offset0 + frag->offset, frag->data + frag->data_offset, frag->len); ++ frag = frag->next; ++ } ++ ++ /* some cleanups, e.g. update the length field of reassembled packet */ ++ switch (entry->protocol) { ++ case PROTO_IPV4: { ++ struct ip *ip_header = (struct ip *) p; ++ ip_header->ip_len = htons (offset0 + entry->len); ++ ip_header->ip_off = 0; ++ // XXX recompute the checksum ++ break; ++ } ++ ++#if USE_IPv6 ++ case PROTO_IPV6: { ++ struct ip6_hdr *ip6_header = (struct ip6_hdr *) p; ++ ip6_header->ip6_plen = htons (offset0 + entry->len - 40); ++ break; ++ } ++#endif /* USE_IPv6 */ ++ ++ default: ++ abort (); ++ } ++ ++ return p; ++} ++ ++ ++static void ++drop_entry (struct reasm_ip *reasm, struct reasm_ip_entry *entry) ++{ ++ if (entry->prev != NULL) ++ entry->prev->next = entry->next; ++ else ++ reasm->table[entry->hash] = entry->next; ++ ++ if (entry->next != NULL) ++ entry->next->prev = entry->prev; ++ ++ if (entry->time_prev != NULL) ++ entry->time_prev->time_next = entry->time_next; ++ else ++ reasm->time_first = entry->time_next; ++ ++ if (entry->time_next != NULL) ++ entry->time_next->time_prev = entry->time_prev; ++ else ++ reasm->time_last = entry->time_prev; ++ ++ reasm->waiting--; ++ ++ free_entry (entry); ++} ++ ++ ++static void ++free_entry (struct reasm_ip_entry *entry) ++{ ++ struct reasm_frag_entry *frag = entry->frags, *next; ++ while (frag != NULL) { ++ next = frag->next; ++ if (frag->data != NULL) ++ free (frag->data); ++ free (frag); ++ frag = next; ++ } ++ ++ free (entry); ++} ++ ++ ++unsigned ++reasm_ip_waiting (const struct reasm_ip *reasm) ++{ ++ return reasm->waiting; ++} ++ ++ ++unsigned ++reasm_ip_max_waiting (const struct reasm_ip *reasm) ++{ ++ return reasm->max_waiting; ++} ++ ++ ++unsigned ++reasm_ip_timed_out (const struct reasm_ip *reasm) ++{ ++ return reasm->timed_out; ++} ++ ++ ++unsigned ++reasm_ip_dropped_frags (const struct reasm_ip *reasm) ++{ ++ return reasm->dropped_frags; ++} ++ ++ ++bool ++reasm_ip_set_timeout (struct reasm_ip *reasm, reasm_time_t timeout) ++{ ++ if (reasm->time_first != NULL) ++ return false; ++ ++ reasm->timeout = timeout; ++ return true; ++} ++ ++ ++static void ++process_timeouts (struct reasm_ip *reasm, reasm_time_t now) ++{ ++ while (reasm->time_first != NULL && reasm->time_first->timeout < now) { ++ reasm->timed_out++; ++ drop_entry (reasm, reasm->time_first); ++ } ++} ++ ++ ++#if USE_IPv6 ++static struct reasm_frag_entry * ++frag_from_ipv6 (unsigned char *packet, uint32_t *ip_id, bool *last_frag) ++{ ++ struct ip6_hdr *ip6_header = (struct ip6_hdr *) packet; ++ unsigned offset = 40; /* IPv6 header size */ ++ uint8_t nxt = ip6_header->ip6_nxt; ++ unsigned total_len = 40 + ntohs (ip6_header->ip6_plen); ++ unsigned last_nxt = offsetof (struct ip6_hdr, ip6_nxt); ++ ++ /* ++ * IPv6 extension headers from RFC 2460: ++ * 0 Hop-by-Hop Options ++ * 43 Routing ++ * 44 Fragment ++ * 60 Destination Options ++ * ++ * We look out for the Fragment header; the other 3 header ++ * types listed above are recognized and considered safe to ++ * skip over if they occur before the Fragment header. ++ * Any unrecognized header will cause processing to stop and ++ * a subsequent Fragment header to stay unrecognized. ++ */ ++ while (nxt == IPPROTO_HOPOPTS || nxt == IPPROTO_ROUTING || nxt == IPPROTO_DSTOPTS) { ++ if (offset + 2 > total_len) ++ return NULL; /* header extends past end of packet */ ++ ++ unsigned exthdr_len = 8 + 8 * packet[offset + 1]; ++ if (offset + exthdr_len > total_len) ++ return NULL; /* header extends past end of packet */ ++ ++ nxt = packet[offset]; ++ last_nxt = offset; ++ offset += exthdr_len; ++ } ++ ++ if (nxt != IPPROTO_FRAGMENT) ++ return NULL; ++ ++ if (offset + 8 > total_len) ++ return NULL; /* Fragment header extends past end of packet */ ++ ++ struct reasm_frag_entry *frag = malloc (sizeof (*frag)); ++ if (frag == NULL) ++ abort (); ++ ++ struct ip6_frag *frag_header = (struct ip6_frag *) (packet + offset); ++ offset += 8; ++ ++ /* ++ * The Fragment header will be removed on reassembly, so we have to ++ * replace the Next Header field of the previous header (which is ++ * currently IPPROTO_FRAGMENT), with the Next Header field of the ++ * Fragment header. ++ * ++ * XXX We really shouldn't manipulate the input packet in-place. ++ */ ++ packet[last_nxt] = frag_header->ip6f_nxt; ++ ++ *frag = (struct reasm_frag_entry) { ++ .len = total_len - offset, ++ .data_offset = offset, ++ .offset = ntohs (frag_header->ip6f_offlg & IP6F_OFF_MASK), ++ .data = packet, ++ }; ++ ++ *ip_id = ntohl (frag_header->ip6f_ident); ++ *last_frag = (frag_header->ip6f_offlg & IP6F_MORE_FRAG) == 0; ++ ++ return frag; ++} ++#endif /* USE_IPv6 */ ++ ++ ++static bool ++reasm_id_equal (enum reasm_proto proto, const union reasm_id *left, const union reasm_id *right) ++{ ++ switch (proto) { ++ case PROTO_IPV4: ++ return memcmp (left->ipv4.ip_src, right->ipv4.ip_src, 4) == 0 ++ && memcmp (left->ipv4.ip_dst, right->ipv4.ip_dst, 4) == 0 ++ && left->ipv4.ip_id == right->ipv4.ip_id ++ && left->ipv4.ip_proto == right->ipv4.ip_proto; ++#if USE_IPv6 ++ case PROTO_IPV6: ++ return memcmp (left->ipv6.ip_src, right->ipv6.ip_src, 16) == 0 ++ && memcmp (left->ipv6.ip_dst, right->ipv6.ip_dst, 16) == 0 ++ && left->ipv6.ip_id == right->ipv6.ip_id; ++#endif /* USE_IPv6 */ ++ default: ++ abort (); ++ } ++} ++ ++ ++static struct reasm_frag_entry * ++parse_packet (unsigned char *packet, unsigned len, enum reasm_proto *protocol, union reasm_id *id, unsigned *hash, bool *last_frag) ++{ ++ struct ip *ip_header = (struct ip *) packet; ++ struct reasm_frag_entry *frag = NULL; ++ ++ switch (ip_header->ip_v) { ++ case 4: { ++ *protocol = PROTO_IPV4; ++ uint16_t offset = ntohs (ip_header->ip_off); ++ if (len >= ntohs (ip_header->ip_len) && (offset & (IP_MF | IP_OFFMASK)) != 0) { ++ frag = malloc (sizeof (*frag)); ++ if (frag == NULL) ++ abort (); ++ ++ *frag = (struct reasm_frag_entry) { ++ .len = ntohs (ip_header->ip_len) - ip_header->ip_hl * 4, ++ .offset = (offset & IP_OFFMASK) * 8, ++ .data_offset = ip_header->ip_hl * 4, ++ .data = packet, ++ }; ++ ++ *last_frag = (offset & IP_MF) == 0; ++ ++ memcpy (id->ipv4.ip_src, &ip_header->ip_src, 4); ++ memcpy (id->ipv4.ip_dst, &ip_header->ip_dst, 4); ++ id->ipv4.ip_id = ntohs (ip_header->ip_id); ++ id->ipv4.ip_proto = ip_header->ip_p; ++ ++ *hash = reasm_ipv4_hash (&id->ipv4); ++ } ++ break; ++ } ++ ++#if USE_IPv6 ++ case 6: { ++ struct ip6_hdr *ip6_header = (struct ip6_hdr *) packet; ++ *protocol = PROTO_IPV6; ++ if (len >= ntohs (ip6_header->ip6_plen) + 40) ++ frag = frag_from_ipv6 (packet, &id->ipv6.ip_id, last_frag); ++ if (frag != NULL) { ++ memcpy (id->ipv6.ip_src, &ip6_header->ip6_src, 16); ++ memcpy (id->ipv6.ip_dst, &ip6_header->ip6_dst, 16); ++ *hash = reasm_ipv6_hash (&id->ipv6); ++ } ++ break; ++ } ++#endif /* USE_IPv6 */ ++ ++ default: ++ break; ++ } ++ ++ return frag; ++} +diff -urN ngrep-1.45/ipreasm.h ngrep-1.45-reasm/ipreasm.h +--- ngrep-1.45/ipreasm.h 1970-01-01 01:00:00.000000000 +0100 ++++ ngrep-1.45-reasm/ipreasm.h 2007-06-16 19:17:20.124795623 +0200 +@@ -0,0 +1,57 @@ ++#ifndef _IPREASM_H ++#define _IPREASM_H ++ ++#include <stdbool.h> ++ ++#include <pcap.h> ++ ++ ++/* ++ * This is an abstract time stamp. ipreasm doesn't care whether it is ++ * in seconds, milliseconds, or nanodecades. All it does it add the ++ * configured timeout value to it, and then compare it to the timstamps ++ * of subsequent packets to decide whether a fragment has expired. ++ */ ++typedef uint64_t reasm_time_t; ++ ++struct reasm_ip; ++ ++/* ++ * Functions to create and destroy the reassembly environment. ++ */ ++struct reasm_ip *reasm_ip_new (void); ++void reasm_ip_free (struct reasm_ip *reasm); ++ ++/* ++ * This is the main packet processing function. It inputs one packet, ++ * and MAY output one packet in turn. If the input was not a fragment, ++ * it is passed unmodified. If the input was a fragment that completed ++ * reassembly of a packet, the reassembled packet is output. ++ * If more fragments are required for reassembly, or the input packet ++ * is invalid for some reason, a NULL pointer is returned. ++ * ++ * The input must be a pointer allocated by malloc(). The output will ++ * be a pointer allocated by malloc(). ++ * ++ * Note that in the case of an IPv6 fragment, the input buffer will be ++ * modified in-place. This is considered a bug and should be fixed in ++ * the future. ++ */ ++unsigned char *reasm_ip_next (struct reasm_ip *reasm, unsigned char *packet, unsigned len, reasm_time_t timestamp, unsigned *output_len); ++ ++/* ++ * Set the timeout after which a noncompleted reassembly expires, in ++ * abstract time units (see above for the definition of reasm_time_t). ++ */ ++bool reasm_ip_set_timeout (struct reasm_ip *reasm, reasm_time_t timeout); ++ ++/* ++ * Query certain information about the current state. ++ */ ++unsigned reasm_ip_waiting (const struct reasm_ip *reasm); ++unsigned reasm_ip_max_waiting (const struct reasm_ip *reasm); ++unsigned reasm_ip_timed_out (const struct reasm_ip *reasm); ++unsigned reasm_ip_dropped_frags (const struct reasm_ip *reasm); ++ ++ ++#endif /* _IPREASM_H */ +diff -urN ngrep-1.45/Makefile.in ngrep-1.45-reasm/Makefile.in +--- ngrep-1.45/Makefile.in 2006-11-28 14:35:37.000000000 +0100 ++++ ngrep-1.45-reasm/Makefile.in 2007-06-16 15:41:22.859876074 +0200 +@@ -15,8 +15,8 @@ + + STRIPFLAG=@STRIPFLAG@ + +-SRC=ngrep.c +-OBJS=ngrep.o ++SRC=ngrep.c ipreasm.c ++OBJS=ngrep.o ipreasm.o + TARGET=ngrep + MANPAGE=ngrep.8 + +@@ -65,7 +65,7 @@ + $(REGEX_OBJS): $(REGEX_OBJS:.o=.c) $(REGEX_DIR)/*.h + $(MAKE) $(MAKEFLAGS) -C $(REGEX_DIR) $(notdir $(REGEX_OBJS)) + +-$(OBJS): Makefile ngrep.c ngrep.h ++$(OBJS): Makefile ngrep.c ngrep.h ipreasm.c ipreasm.h + + tardist: + @( VERSION=`perl -ne '/VERSION\s+"(.*)"/ && print "$$1\n"' ngrep.h` ; \ +diff -urN ngrep-1.45/ngrep.c ngrep-1.45-reasm/ngrep.c +--- ngrep-1.45/ngrep.c 2006-11-28 14:38:43.000000000 +0100 ++++ ngrep-1.45-reasm/ngrep.c 2007-06-16 21:41:43.142117150 +0200 +@@ -98,6 +98,7 @@ + #endif + + #include "ngrep.h" ++#include "ipreasm.h" + + + static char rcsver[] = "$Revision: 1.93 $"; +@@ -156,7 +157,7 @@ + uint8_t link_offset; + uint8_t radiotap_present = 0; + +-pcap_t *pd = NULL; ++pcap_t *pd = NULL, *pd_dumppcap = NULL; + pcap_dumper_t *pd_dump = NULL; + struct bpf_program pcapfilter; + struct in_addr net, mask; +@@ -183,6 +184,12 @@ + uint32_t ws_row, ws_col = 80, ws_col_forced = 0; + + ++/* ++ * Reassembly ++ */ ++struct reasm_ip *reasm = NULL; ++ ++ + int main(int argc, char **argv) { + int32_t c; + +@@ -195,7 +202,7 @@ + signal(SIGWINCH, update_windowsize); + #endif + +- while ((c = getopt(argc, argv, "LNhXViwqpevxlDtTRMs:n:c:d:A:I:O:S:P:F:W:")) != EOF) { ++ while ((c = getopt(argc, argv, "LNhXViwqpevxlDtTrRMs:n:c:d:A:I:O:S:P:F:W:")) != EOF) { + switch (c) { + case 'W': { + if (!strcasecmp(optarg, "normal")) +@@ -260,6 +267,10 @@ + case 'M': + re_multiline_match = 0; + break; ++ case 'r': ++ reasm = reasm_ip_new (); ++ reasm_ip_set_timeout (reasm, 30000000); ++ break; + case 'R': + dont_dropprivs = 1; + break; +@@ -585,7 +596,15 @@ + } + + if (dump_file) { +- if (!(pd_dump = pcap_dump_open(pd, dump_file))) { ++ if (reasm != NULL) { ++ if (!(pd_dumppcap = pcap_open_dead(DLT_RAW, 65535))) { ++ fprintf(stderr, "fatal: pcap_open_dead failed\n"); ++ clean_exit(-1); ++ } ++ pd_dump = pcap_dump_open(pd_dumppcap, dump_file); ++ } else ++ pd_dump = pcap_dump_open(pd, dump_file); ++ if (!pd_dump) { + fprintf(stderr, "fatal: %s\n", pcap_geterr(pd)); + clean_exit(-1); + } else printf("output: %s\n", dump_file); +@@ -641,6 +660,23 @@ + } + #endif + ++ if (reasm != NULL) { ++ unsigned new_len; ++ u_char *new_p = malloc(len - link_offset); ++ memcpy(new_p, ip4_pkt, len - link_offset); ++ p = reasm_ip_next(reasm, new_p, len - link_offset, (reasm_time_t) 1000000UL * h->ts.tv_sec + h->ts.tv_usec, &new_len); ++ if (p == NULL) ++ return; ++ len = new_len + link_offset; ++ h->len = new_len; ++ h->caplen = new_len; ++ ++ ip4_pkt = (struct ip *) p; ++#if USE_IPv6 ++ ip6_pkt = (struct ip6_hdr*)p; ++#endif ++ } ++ + ip_ver = ip4_pkt->ip_v; + + switch (ip_ver) { +@@ -802,6 +838,9 @@ + + if (match_after && keep_matching) + keep_matching--; ++ ++ if (reasm != NULL) ++ free(p); + } + + void dump_packet(struct pcap_pkthdr *h, u_char *p, uint8_t proto, unsigned char *data, uint32_t len, +@@ -1242,6 +1281,7 @@ + " -s is set the bpf caplen\n" + " -S is set the limitlen on matched packets\n" + " -W is set the dump format (normal, byline, single, none)\n" ++ " -r is reassemble any fragmented IPV4 or IPV6 packets\n" + " -c is force the column width to the specified size\n" + " -P is set the non-printable display char to what is specified\n" + " -F is read the bpf filter from the specified file\n" +@@ -1292,8 +1331,9 @@ + && pd && !pcap_stats(pd, &s)) + printf("%u received, %u dropped\n", s.ps_recv, s.ps_drop); + +- if (pd) pcap_close(pd); +- if (pd_dump) pcap_dump_close(pd_dump); ++ if (pd) pcap_close(pd); ++ if (pd_dumppcap) pcap_close(pd_dumppcap); ++ if (pd_dump) pcap_dump_close(pd_dump); + + #if defined(_WIN32) + if (delay_socket) closesocket(delay_socket); +@@ -1301,6 +1341,9 @@ + if (usedev) free(usedev); + #endif + ++ if (reasm != NULL) ++ reasm_ip_free(reasm); ++ + exit(sig); + } + |