1 /*
2  * ipreasm -- Routines for reassembly of fragmented IPv4 and IPv6 packets.
3  *
4  * Copyright (c) 2007  Jan Andres <jandres@gmx.net>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  */
10 
11 #include "nmsg_port_net.h"
12 
13 #include <assert.h>
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <stddef.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 
22 #include <pcap.h>
23 
24 #include "ipreasm.h"
25 
26 #define REASM_IP_HASH_SIZE 1021U
27 
28 
29 enum entry_state {
30 	STATE_ACTIVE,
31 	STATE_INVALID
32 };
33 
34 
35 enum reasm_proto {
36 	PROTO_IPV4,
37 	PROTO_IPV6
38 };
39 
40 
41 /*
42  * This tuple uniquely identifies all fragments belonging to
43  * the same IPv4 packet.
44  */
45 struct reasm_id_ipv4 {
46 	uint8_t ip_src[4], ip_dst[4];
47 	uint16_t ip_id;
48 	uint8_t ip_proto;
49 };
50 
51 
52 /*
53  * Same for IPv6.
54  */
55 struct reasm_id_ipv6 {
56 	uint8_t ip_src[16], ip_dst[16];
57 	uint32_t ip_id;
58 };
59 
60 
61 union reasm_id {
62 	struct reasm_id_ipv4 ipv4;
63 	struct reasm_id_ipv6 ipv6;
64 };
65 
66 
67 struct reasm_frag_entry {
68 	unsigned len;  /* payload length of this fragment */
69 	unsigned offset; /* offset of this fragment into the payload of the reassembled packet */
70 	unsigned data_offset; /* offset to the data pointer where payload starts */
71 	unsigned char *data; /* payload starts at data + data_offset */
72 	struct reasm_frag_entry *next;
73 };
74 
75 
76 /*
77  * Reception of a complete packet is detected by counting the number
78  * of "holes" that remain between the cached fragments. A hole is
79  * assumed to exist at the upper end of the packet until the final
80  * fragment has been received. When the number of holes drops to 0,
81  * all fragments have been received and the packet can be reassembled.
82  */
83 struct reasm_ip_entry {
84 	union reasm_id id;
85 	unsigned len, holes, frag_count, hash;
86 	reasm_time_t timeout;
87 	enum entry_state state;
88 	enum reasm_proto protocol;
89 	struct reasm_frag_entry *frags;
90 	struct reasm_ip_entry *prev, *next;
91 	struct reasm_ip_entry *time_prev, *time_next;
92 };
93 
94 
95 /*
96  * This struct contains some metadata, the main hash table, and a pointer
97  * to the first entry that will time out. A linked list is kept in the
98  * order in which packets will time out. Using a linked list for this
99  * purpose requires that packets are input in chronological order, and
100  * that a constant timeout value is used, which doesn't change even when
101  * the entry's state transitions from active to invalid.
102  */
103 struct reasm_ip {
104 	struct reasm_ip_entry *table[REASM_IP_HASH_SIZE];
105 	struct reasm_ip_entry *time_first, *time_last;
106 	unsigned waiting, max_waiting, timed_out, dropped_frags;
107 	reasm_time_t timeout;
108 };
109 
110 
111 /*
112  * Hash functions.
113  */
114 static unsigned reasm_ipv4_hash (const struct reasm_id_ipv4 *id);
115 static unsigned reasm_ipv6_hash (const struct reasm_id_ipv6 *id);
116 
117 /*
118  * Insert a new fragment to the correct position in the list of fragments.
119  * Check for fragment overlap and other error conditions. Update the
120  * "hole count".
121  */
122 static bool add_fragment (struct reasm_ip_entry *entry, struct reasm_frag_entry *frag, bool last_frag);
123 
124 /*
125  * Is the entry complete, ready for reassembly?
126  */
127 static bool is_complete (struct reasm_ip_entry *entry);
128 
129 /*
130  * Create the reassembled packet.
131  */
132 static void assemble (struct reasm_ip_entry *entry, unsigned char *out_packet, unsigned *output_len);
133 
134 /*
135  * Drop and free entries.
136  */
137 static void drop_entry (struct reasm_ip *reasm, struct reasm_ip_entry *entry);
138 static void free_entry (struct reasm_ip_entry *entry);
139 
140 /*
141  * Dispose of any entries which have expired before "now".
142  */
143 static void process_timeouts (struct reasm_ip *reasm, reasm_time_t now);
144 
145 /*
146  * Create fragment structure from IPv6 packet. Returns NULL if the input
147  * is not a fragment.
148  * This function is called by parse_packet(), don't call it directly.
149  */
150 static struct reasm_frag_entry *frag_from_ipv6 (const unsigned char *packet, unsigned frag_hdr_offset, uint32_t *ip_id, bool *last_frag);
151 
152 /*
153  * Compare packet identification tuples for specified protocol.
154  */
155 static bool reasm_id_equal (enum reasm_proto proto, const union reasm_id *left, const union reasm_id *right);
156 
157 /*
158  * Create fragment structure from an IPv4 or IPv6 packet. Returns NULL
159  * if the input is not a fragment.
160  */
161 static struct reasm_frag_entry *parse_packet (const unsigned char *packet, unsigned len, unsigned frag_hdr_offset, enum reasm_proto *protocol, union reasm_id *id, unsigned *hash, bool *last_frag);
162 
163 
164 static unsigned
reasm_ipv4_hash(const struct reasm_id_ipv4 * id)165 reasm_ipv4_hash (const struct reasm_id_ipv4 *id)
166 {
167 	unsigned hash = 0;
168 	int i;
169 
170 	for (i = 0; i < 4; i++) {
171 		hash = 37U * hash + id->ip_src[i];
172 		hash = 37U * hash + id->ip_dst[i];
173 	}
174 
175 	hash = 59U * hash + id->ip_id;
176 
177 	hash = 47U * hash + id->ip_proto;
178 
179 	return hash;
180 }
181 
182 
183 static unsigned
reasm_ipv6_hash(const struct reasm_id_ipv6 * id)184 reasm_ipv6_hash (const struct reasm_id_ipv6 *id)
185 {
186 	unsigned hash = 0;
187 	int i;
188 
189 	for (i = 0; i < 16; i++) {
190 		hash = 37U * hash + id->ip_src[i];
191 		hash = 37U * hash + id->ip_dst[i];
192 	}
193 
194 	hash = 59U * hash + id->ip_id;
195 
196 	return hash;
197 }
198 
199 
200 bool
reasm_ip_next(struct reasm_ip * reasm,const unsigned char * packet,unsigned len,unsigned frag_hdr_offset,reasm_time_t timestamp,unsigned char * out_packet,unsigned * output_len)201 reasm_ip_next (struct reasm_ip *reasm, const unsigned char *packet, unsigned len, unsigned frag_hdr_offset, reasm_time_t timestamp, unsigned char *out_packet, unsigned *output_len)
202 {
203 	enum reasm_proto proto;
204 	union reasm_id id;
205 	unsigned hash = 0;
206 	bool last_frag = 0;
207 	struct reasm_frag_entry *frag;
208 	struct reasm_ip_entry *entry;
209 
210 	process_timeouts (reasm, timestamp);
211 
212 	frag = parse_packet (packet, len, frag_hdr_offset, &proto, &id, &hash, &last_frag);
213 	if (frag == NULL) {
214 		*output_len = 0;
215 		return false; /* some packet that we don't recognize as a fragment */
216 	}
217 
218 	hash %= REASM_IP_HASH_SIZE;
219 	entry = reasm->table[hash];
220 	while (entry != NULL && (proto != entry->protocol || !reasm_id_equal (proto, &id, &entry->id)))
221 		entry = entry->next;
222 
223 	if (entry == NULL) {
224 		struct reasm_frag_entry *list_head;
225 
226 		entry = malloc (sizeof (*entry));
227 		if (entry == NULL) {
228 			free (frag);
229 			abort ();
230 		}
231 
232 		list_head = malloc (sizeof (*list_head));
233 		if (list_head == NULL) {
234 			free (frag);
235 			free (entry);
236 			abort ();
237 		}
238 
239 		memset(entry, 0, sizeof *entry);
240 		entry->id = id;
241 		entry->len = 0;
242 		entry->holes = 1;
243 		entry->frags = list_head;
244 		entry->hash = hash;
245 		entry->protocol = proto;
246 		entry->timeout = timestamp + reasm->timeout;
247 		entry->state = STATE_ACTIVE;
248 		entry->prev = NULL;
249 		entry->next = reasm->table[hash];
250 		entry->time_prev = reasm->time_last;
251 		entry->time_next = NULL;
252 
253 		memset(list_head, 0, sizeof *list_head);
254 		list_head->len = 0;
255 		list_head->offset = 0;
256 		list_head->data_offset = 0;
257 		list_head->data = NULL;
258 
259 		if (entry->next != NULL)
260 			entry->next->prev = entry;
261 		reasm->table[hash] = entry;
262 
263 		if (reasm->time_last != NULL)
264 			reasm->time_last->time_next = entry;
265 		else
266 			reasm->time_first = entry;
267 		reasm->time_last = entry;
268 
269 		reasm->waiting++;
270 		if (reasm->waiting > reasm->max_waiting)
271 			reasm->max_waiting = reasm->waiting;
272 	}
273 
274 	if (entry->state != STATE_ACTIVE) {
275 		reasm->dropped_frags++;
276 		*output_len = 0;
277 		free(frag->data);
278 		free(frag);
279 		return true;
280 	}
281 
282 	if (!add_fragment (entry, frag, last_frag)) {
283 		entry->state = STATE_INVALID;
284 		reasm->dropped_frags += entry->frag_count + 1;
285 		*output_len = 0;
286 		free(frag->data);
287 		free(frag);
288 		return true;
289 	}
290 
291 	if (!is_complete (entry)) {
292 		*output_len = 0;
293 		return true;
294 	}
295 
296 	assemble (entry, out_packet, output_len);
297 	drop_entry (reasm, entry);
298 	return true;
299 }
300 
301 
302 static bool
add_fragment(struct reasm_ip_entry * entry,struct reasm_frag_entry * frag,bool last_frag)303 add_fragment (struct reasm_ip_entry *entry, struct reasm_frag_entry *frag, bool last_frag)
304 {
305 	bool fit_left, fit_right;
306 	struct reasm_frag_entry *cur, *next;
307 
308 	/*
309 	 * When a fragment is inserted into the list, different cases can occur
310 	 * concerning the number of holes.
311 	 * - The new fragment can be inserted in the middle of a hole, such that
312 	 *   it will split the hole in two. The number of holes increases by 1.
313 	 * - The new fragment can be attached to one end of a hole, such that
314 	 *   the rest of the hole remains at the opposite side of the fragment.
315 	 *   The number of holes remains constant.
316 	 * - The new fragment can fill a hole completely. The number of holes
317 	 *   decreases by 1.
318 	 */
319 
320 	/*
321 	 * If more fragments follow and the payload size is not an integer
322 	 * multiple of 8, the packet will never be reassembled completely.
323 	 */
324 	if (!last_frag && (frag->len & 7) != 0)
325 		return false;
326 
327 	if (entry->len != 0 && frag->len + frag->offset > entry->len)
328 		return false; /* fragment extends past end of packet */
329 
330 	fit_left = false;
331 	fit_right = false;
332 
333 	if (last_frag) {
334 		if (entry->len != 0)
335 			return false;
336 		entry->len = frag->offset + frag->len;
337 		fit_right = true;
338 	}
339 
340 	cur = entry->frags;
341 
342 	while (cur->next != NULL && cur->next->offset <= frag->offset)
343 		cur = cur->next;
344 	next = cur->next;
345 
346 	/* Fragment is to be inserted between cur and next; next may be NULL. */
347 
348 	/* Overlap checks. */
349 	if (cur->offset + cur->len > frag->offset)
350 		return false; /* overlaps with cur */
351 	else if (cur->offset + cur->len == frag->offset)
352 		fit_left = true;
353 
354 	if (next != NULL) {
355 		if (last_frag)
356 			return false; /* next extends past end of packet */
357 		if (frag->offset + frag->len > next->offset)
358 			return false; /* overlaps with next */
359 		else if (frag->offset + frag->len == next->offset)
360 			fit_right = true;
361 	}
362 
363 	/*
364 	 * Everything's fine, insert it.
365 	 */
366 	if (frag->len != 0) {
367 		frag->next = cur->next;
368 		cur->next = frag;
369 
370 		if (fit_left && fit_right)
371 			entry->holes--;
372 		else if (!fit_left && !fit_right)
373 			entry->holes++;
374 
375 		entry->frag_count++;
376 	} else {
377 		/*
378 		 * If the fragment has zero size, we don't insert it into the list,
379 		 * but one case remains to be handled: If the zero-size fragment
380 		 * is the last fragment, and fits exactly with the fragment to its
381 		 * left, the number of holes decreases.
382 		 */
383 		if (last_frag && fit_left)
384 			entry->holes--;
385 	}
386 
387 
388 	return true;
389 }
390 
391 
392 struct reasm_ip *
reasm_ip_new(void)393 reasm_ip_new (void)
394 {
395 	struct reasm_ip *reasm = malloc (sizeof (*reasm));
396 	if (reasm == NULL)
397 		return NULL;
398 
399 	memset (reasm, 0, sizeof (*reasm));
400 	return reasm;
401 }
402 
403 
404 void
reasm_ip_free(struct reasm_ip * reasm)405 reasm_ip_free (struct reasm_ip *reasm)
406 {
407 	while (reasm->time_first != NULL)
408 		drop_entry (reasm, reasm->time_first);
409 	free (reasm);
410 }
411 
412 
413 static bool
is_complete(struct reasm_ip_entry * entry)414 is_complete (struct reasm_ip_entry *entry)
415 {
416 	return entry->holes == 0;
417 }
418 
419 
420 static void
assemble(struct reasm_ip_entry * entry,unsigned char * out_packet,unsigned * output_len)421 assemble (struct reasm_ip_entry *entry, unsigned char *out_packet, unsigned *output_len)
422 {
423 	struct reasm_frag_entry *frag = entry->frags->next; /* skip list head */
424 	unsigned offset0 = frag->data_offset;
425 
426 	switch (entry->protocol) {
427 		case PROTO_IPV4:
428 			break;
429 		case PROTO_IPV6:
430 			offset0 -= 8; /* size of frag header */
431 			break;
432 		default:
433 			abort ();
434 	}
435 
436 	if (entry->len + offset0 > *output_len) {
437 		/* The output buffer is too small. */
438 		*output_len = 0;
439 		return;
440 	}
441 
442 	*output_len = entry->len + offset0;
443 
444 	/* copy the (unfragmentable) header from the first fragment received */
445 	memcpy (out_packet, frag->data, offset0);
446 
447 	/* join all the payload fragments together */
448 	while (frag != NULL) {
449 		memcpy (out_packet + offset0 + frag->offset, frag->data + frag->data_offset, frag->len);
450 		frag = frag->next;
451 	}
452 
453 	/* some cleanups, e.g. update the length field of reassembled packet */
454 	switch (entry->protocol) {
455 		case PROTO_IPV4: {
456 			struct nmsg_iphdr *ip_header = (struct nmsg_iphdr *) out_packet;
457 			unsigned i, hl = 4 * ip_header->ip_hl;
458 			int32_t sum = 0;
459 			ip_header->ip_len = htons (offset0 + entry->len);
460 			ip_header->ip_off = 0;
461 			ip_header->ip_sum = 0;
462 
463 			/* Recompute checksum. */
464 			for (i = 0; i < hl; i += 2) {
465 				uint16_t cur = (uint16_t) out_packet[i] << 8 | out_packet[i + 1];
466 				sum += cur;
467 				if ((sum & 0x80000000) != 0)
468 					sum = (sum & 0xffff) + (sum >> 16);
469 			}
470 			while ((sum >> 16) != 0)
471 				sum = (sum & 0xffff) + (sum >> 16);
472 			ip_header->ip_sum = htons (~sum);
473 			break;
474 		}
475 		case PROTO_IPV6: {
476 			struct ip6_hdr *ip6_header = (struct ip6_hdr *) out_packet;
477 			ip6_header->ip6_plen = htons (offset0 + entry->len - 40);
478 			break;
479 		}
480 		default:
481 			abort ();
482 	}
483 }
484 
485 
486 static void
drop_entry(struct reasm_ip * reasm,struct reasm_ip_entry * entry)487 drop_entry (struct reasm_ip *reasm, struct reasm_ip_entry *entry)
488 {
489 	if (entry->prev != NULL)
490 		entry->prev->next = entry->next;
491 	else
492 		reasm->table[entry->hash] = entry->next;
493 
494 	if (entry->next != NULL)
495 		entry->next->prev = entry->prev;
496 
497 	if (entry->time_prev != NULL)
498 		entry->time_prev->time_next = entry->time_next;
499 	else
500 		reasm->time_first = entry->time_next;
501 
502 	if (entry->time_next != NULL)
503 		entry->time_next->time_prev = entry->time_prev;
504 	else
505 		reasm->time_last = entry->time_prev;
506 
507 	reasm->waiting--;
508 
509 	free_entry (entry);
510 }
511 
512 
513 static void
free_entry(struct reasm_ip_entry * entry)514 free_entry (struct reasm_ip_entry *entry)
515 {
516 	struct reasm_frag_entry *frag = entry->frags, *next;
517 	while (frag != NULL) {
518 		next = frag->next;
519 		if (frag->data != NULL)
520 			free (frag->data);
521 		free (frag);
522 		frag = next;
523 	}
524 
525 	free (entry);
526 }
527 
528 
529 unsigned
reasm_ip_waiting(const struct reasm_ip * reasm)530 reasm_ip_waiting (const struct reasm_ip *reasm)
531 {
532 	return reasm->waiting;
533 }
534 
535 
536 unsigned
reasm_ip_max_waiting(const struct reasm_ip * reasm)537 reasm_ip_max_waiting (const struct reasm_ip *reasm)
538 {
539 	return reasm->max_waiting;
540 }
541 
542 
543 unsigned
reasm_ip_timed_out(const struct reasm_ip * reasm)544 reasm_ip_timed_out (const struct reasm_ip *reasm)
545 {
546 	return reasm->timed_out;
547 }
548 
549 
550 unsigned
reasm_ip_dropped_frags(const struct reasm_ip * reasm)551 reasm_ip_dropped_frags (const struct reasm_ip *reasm)
552 {
553 	return reasm->dropped_frags;
554 }
555 
556 
557 bool
reasm_ip_set_timeout(struct reasm_ip * reasm,reasm_time_t timeout)558 reasm_ip_set_timeout (struct reasm_ip *reasm, reasm_time_t timeout)
559 {
560 	if (reasm->time_first != NULL)
561 		return false;
562 
563 	reasm->timeout = timeout;
564 	return true;
565 }
566 
567 
568 static void
process_timeouts(struct reasm_ip * reasm,reasm_time_t now)569 process_timeouts (struct reasm_ip *reasm, reasm_time_t now)
570 {
571 	while (reasm->time_first != NULL && reasm->time_first->timeout < now) {
572 		reasm->timed_out++;
573 		drop_entry (reasm, reasm->time_first);
574 	}
575 }
576 
577 
578 static struct reasm_frag_entry *
frag_from_ipv6(const unsigned char * packet,unsigned frag_hdr_offset,uint32_t * ip_id,bool * last_frag)579 frag_from_ipv6 (const unsigned char *packet, unsigned frag_hdr_offset, uint32_t *ip_id, bool *last_frag)
580 {
581 	const struct ip6_hdr *ip6_header = (const struct ip6_hdr *) packet;
582 	unsigned offset = 40; /* IPv6 header size */
583 	uint8_t nxt = ip6_header->ip6_nxt;
584 	unsigned total_len = 40 + ntohs (ip6_header->ip6_plen);
585 	unsigned last_nxt = offsetof (struct ip6_hdr, ip6_nxt);
586 	struct reasm_frag_entry *frag;
587 	const struct ip6_frag *frag_header;
588 	unsigned char *frag_data;
589 
590 	/*
591 	 * IPv6 extension headers from RFC 2460:
592 	 *   0 Hop-by-Hop Options
593 	 *  43 Routing
594 	 *  44 Fragment
595 	 *  60 Destination Options
596 	 *
597 	 * We look out for the Fragment header; the other 3 header
598 	 * types listed above are recognized and considered safe to
599 	 * skip over if they occur before the Fragment header.
600 	 * Any unrecognized header will cause processing to stop and
601 	 * a subsequent Fragment header to stay unrecognized.
602 	 */
603 	if (frag_hdr_offset != 0)
604 		offset = frag_hdr_offset;
605 	else {
606 		while (nxt == IPPROTO_HOPOPTS || nxt == IPPROTO_ROUTING || nxt == IPPROTO_DSTOPTS) {
607 			unsigned exthdr_len;
608 
609 			if (offset + 2 > total_len)
610 				return NULL;  /* header extends past end of packet */
611 
612 			exthdr_len = 8 + 8 * packet[offset + 1];
613 			if (offset + exthdr_len > total_len)
614 				return NULL;  /* header extends past end of packet */
615 
616 			nxt = packet[offset];
617 			last_nxt = offset;
618 			offset += exthdr_len;
619 		}
620 
621 		if (nxt != IPPROTO_FRAGMENT)
622 			return NULL;
623 	}
624 
625 	if (offset + 8 > total_len)
626 		return NULL;  /* Fragment header extends past end of packet */
627 
628 	frag = malloc (sizeof (*frag));
629 	if (frag == NULL)
630 		abort ();
631 
632 	frag_header = (const struct ip6_frag *) (packet + offset);
633 	offset += 8;
634 
635 	frag_data = malloc (total_len);
636 	if (frag_data == NULL)
637 		abort ();
638 	memcpy (frag_data, packet, total_len);
639 
640 	/*
641 	 * The Fragment header will be removed on reassembly, so we have to
642 	 * replace the Next Header field of the previous header (which is
643 	 * currently IPPROTO_FRAGMENT), with the Next Header field of the
644 	 * Fragment header.
645 	 *
646 	 * XXX We really shouldn't manipulate the input packet in-place.
647 	 */
648 	frag_data[last_nxt] = frag_header->ip6f_nxt;
649 
650 	memset(frag, 0, sizeof *frag);
651 	frag->len = total_len - offset;
652 	frag->data_offset = offset;
653 	frag->offset = ntohs (frag_header->ip6f_offlg & IP6F_OFF_MASK);
654 	frag->data = frag_data;
655 
656 	*ip_id = ntohl (frag_header->ip6f_ident);
657 	*last_frag = (frag_header->ip6f_offlg & IP6F_MORE_FRAG) == 0;
658 
659 	return frag;
660 }
661 
662 
663 static bool
reasm_id_equal(enum reasm_proto proto,const union reasm_id * left,const union reasm_id * right)664 reasm_id_equal (enum reasm_proto proto, const union reasm_id *left, const union reasm_id *right)
665 {
666 	switch (proto) {
667 		case PROTO_IPV4:
668 			return memcmp (left->ipv4.ip_src, right->ipv4.ip_src, 4) == 0
669 				&& memcmp (left->ipv4.ip_dst, right->ipv4.ip_dst, 4) == 0
670 				&& left->ipv4.ip_id == right->ipv4.ip_id
671 				&& left->ipv4.ip_proto == right->ipv4.ip_proto;
672 		case PROTO_IPV6:
673 			return memcmp (left->ipv6.ip_src, right->ipv6.ip_src, 16) == 0
674 				&& memcmp (left->ipv6.ip_dst, right->ipv6.ip_dst, 16) == 0
675 				&& left->ipv6.ip_id == right->ipv6.ip_id;
676 		default:
677 			abort ();
678 	}
679 }
680 
681 
682 static struct reasm_frag_entry *
parse_packet(const unsigned char * packet,unsigned len,unsigned frag_hdr_offset,enum reasm_proto * protocol,union reasm_id * id,unsigned * hash,bool * last_frag)683 parse_packet (const unsigned char *packet, unsigned len, unsigned frag_hdr_offset, enum reasm_proto *protocol, union reasm_id *id, unsigned *hash, bool *last_frag)
684 {
685 	const struct nmsg_iphdr *ip_header = (const struct nmsg_iphdr *) packet;
686 	struct reasm_frag_entry *frag = NULL;
687 
688 	switch (ip_header->ip_v) {
689 		case 4: {
690 			uint16_t offset = ntohs (ip_header->ip_off);
691 
692 			*protocol = PROTO_IPV4;
693 			if (len >= (unsigned) ntohs (ip_header->ip_len) &&
694 			    (offset & (IP_MF | IP_OFFMASK)) != 0)
695 			{
696 				unsigned pl_hl, pl_len, pl_off;
697 				u_char *frag_data;
698 
699 				frag = malloc (sizeof (*frag));
700 				if (frag == NULL)
701 					abort ();
702 
703 				pl_hl = ip_header->ip_hl * 4;
704 				pl_len = ntohs (ip_header->ip_len);
705 				pl_off = (offset & IP_OFFMASK) * 8;
706 				frag_data = malloc (pl_len);
707 				if (frag_data == NULL)
708 					abort ();
709 				memcpy (frag_data, packet, pl_len);
710 
711 				frag->len = pl_len - pl_hl;
712 				frag->offset = pl_off;
713 				frag->data_offset = ip_header->ip_hl * 4;
714 				frag->data = frag_data;
715 
716 				*last_frag = (offset & IP_MF) == 0;
717 
718 				memcpy (id->ipv4.ip_src, &ip_header->ip_src, 4);
719 				memcpy (id->ipv4.ip_dst, &ip_header->ip_dst, 4);
720 				id->ipv4.ip_id = ntohs (ip_header->ip_id);
721 				id->ipv4.ip_proto = ip_header->ip_p;
722 
723 				*hash = reasm_ipv4_hash (&id->ipv4);
724 			}
725 			break;
726 		}
727 
728 		case 6: {
729 			const struct ip6_hdr *ip6_header =
730 				(const struct ip6_hdr *) packet;
731 			*protocol = PROTO_IPV6;
732 			if (len >= (unsigned) ntohs (ip6_header->ip6_plen) + 40)
733 				frag = frag_from_ipv6 (packet, frag_hdr_offset, &id->ipv6.ip_id, last_frag);
734 			if (frag != NULL) {
735 				memcpy (id->ipv6.ip_src, &ip6_header->ip6_src, 16);
736 				memcpy (id->ipv6.ip_dst, &ip6_header->ip6_dst, 16);
737 				*hash = reasm_ipv6_hash (&id->ipv6);
738 			}
739 			break;
740 		}
741 
742 		default:
743 			break;
744 	}
745 
746 	return frag;
747 }
748