summaryrefslogtreecommitdiff
path: root/network/ngrep/ngrep-1.45-reasm.diff
diff options
context:
space:
mode:
Diffstat (limited to 'network/ngrep/ngrep-1.45-reasm.diff')
-rw-r--r--network/ngrep/ngrep-1.45-reasm.diff939
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 0000000000..ed33fb6f65
--- /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);
+ }
+