1 /*
2  * mod_ip_frag.c
3  *
4  * Copyright (c) 2001 Dug Song <dugsong@monkey.org>
5  *
6  * $Id: mod_ip_frag.c,v 1.18 2002/04/11 16:37:42 dugsong Exp $
7  */
8 
9 #include "config.h"
10 
11 #include <err.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 
16 #include "mod.h"
17 #include "pkt.h"
18 #include "randutil.h"
19 
20 #ifndef MAX
21 #define MAX(a,b)	(((a)>(b))?(a):(b))
22 #endif
23 
24 #define FAVOR_OLD	1
25 #define FAVOR_NEW	2
26 
27 static struct ip_frag_data {
28 	rand_t	*rnd;
29 	int	 size;
30 	int	 overlap;
31 } ip_frag_data;
32 
33 void *
ip_frag_close(void * d)34 ip_frag_close(void *d)
35 {
36 	if (ip_frag_data.rnd != NULL)
37 		rand_close(ip_frag_data.rnd);
38 	ip_frag_data.size = 0;
39 	return (NULL);
40 }
41 
42 void *
ip_frag_open(int argc,char * argv[])43 ip_frag_open(int argc, char *argv[])
44 {
45 	if (argc < 2) {
46 		warnx("need fragment <size> in bytes");
47 		return (NULL);
48 	}
49 	ip_frag_data.rnd = rand_open();
50 	ip_frag_data.size = atoi(argv[1]);
51 
52 	if (ip_frag_data.size == 0 || (ip_frag_data.size % 8) != 0) {
53 		warnx("fragment size must be a multiple of 8");
54 		return (ip_frag_close(&ip_frag_data));
55 	}
56 	if (argc == 3) {
57 		if (strcmp(argv[2], "old") == 0 ||
58 		    strcmp(argv[2], "win32") == 0)
59 			ip_frag_data.overlap = FAVOR_OLD;
60 		else if (strcmp(argv[2], "new") == 0 ||
61 		    strcmp(argv[2], "unix") == 0)
62 			ip_frag_data.overlap = FAVOR_NEW;
63 		else
64 			return (ip_frag_close(&ip_frag_data));
65 	}
66 	return (&ip_frag_data);
67 }
68 
69 int
ip_frag_apply(void * d,struct pktq * pktq)70 ip_frag_apply(void *d, struct pktq *pktq)
71 {
72 	struct pkt *pkt, *new, *next, tmp;
73 	int hl, fraglen, off;
74 	u_char *p, *p1, *p2;
75 
76 	for (pkt = TAILQ_FIRST(pktq); pkt != TAILQ_END(pktq); pkt = next) {
77 		next = TAILQ_NEXT(pkt, pkt_next);
78 
79 		if (pkt->pkt_ip == NULL || pkt->pkt_ip_data == NULL)
80 			continue;
81 
82 		hl = pkt->pkt_ip->ip_hl << 2;
83 
84 		/*
85 		 * Preserve transport protocol header in first frag,
86 		 * to bypass filters that block `short' fragments.
87 		 */
88 		switch (pkt->pkt_ip->ip_p) {
89 		case IP_PROTO_ICMP:
90 			fraglen = MAX(ICMP_LEN_MIN, ip_frag_data.size);
91 			break;
92 		case IP_PROTO_UDP:
93 			fraglen = MAX(UDP_HDR_LEN, ip_frag_data.size);
94 			break;
95 		case IP_PROTO_TCP:
96 			fraglen = MAX(pkt->pkt_tcp->th_off << 2,
97 			    ip_frag_data.size);
98 			break;
99 		default:
100 			fraglen = ip_frag_data.size;
101 			break;
102 		}
103 		if (fraglen & 7)
104 			fraglen = (fraglen & ~7) + 8;
105 
106 		if (pkt->pkt_end - pkt->pkt_ip_data < fraglen)
107 			continue;
108 
109 		for (p = pkt->pkt_ip_data; p < pkt->pkt_end; ) {
110 			new = pkt_new();
111 			memcpy(new->pkt_ip, pkt->pkt_ip, hl);
112 			new->pkt_ip_data = new->pkt_eth_data + hl;
113 
114 			p1 = p, p2 = NULL;
115 			off = (p - pkt->pkt_ip_data) >> 3;
116 
117 			if (ip_frag_data.overlap != 0 && (off & 1) != 0 &&
118 			    p + (fraglen << 1) < pkt->pkt_end) {
119 				rand_strset(ip_frag_data.rnd, tmp.pkt_buf,
120 				    fraglen);
121 				if (ip_frag_data.overlap == FAVOR_OLD) {
122 					p1 = p + fraglen;
123 					p2 = tmp.pkt_buf;
124 				} else if (ip_frag_data.overlap == FAVOR_NEW) {
125 					p1 = tmp.pkt_buf;
126 					p2 = p + fraglen;
127 				}
128 				new->pkt_ip->ip_off = htons(IP_MF |
129 				    (off + (fraglen >> 3)));
130 			} else {
131 				new->pkt_ip->ip_off = htons(off |
132 				    ((p + fraglen < pkt->pkt_end) ? IP_MF: 0));
133 			}
134 			new->pkt_ip->ip_len = htons(hl + fraglen);
135 			ip_checksum(new->pkt_ip, hl + fraglen);
136 
137 			memcpy(new->pkt_ip_data, p1, fraglen);
138 			new->pkt_end = new->pkt_ip_data + fraglen;
139 			TAILQ_INSERT_BEFORE(pkt, new, pkt_next);
140 
141 			if (p2 != NULL) {
142 				new = pkt_dup(new);
143 				new->pkt_ts.tv_usec = 1;
144 				new->pkt_ip->ip_off = htons(IP_MF | off);
145 				new->pkt_ip->ip_len = htons(hl + (fraglen<<1));
146 				ip_checksum(new->pkt_ip, hl + (fraglen<<1));
147 
148 				memcpy(new->pkt_ip_data, p, fraglen);
149 				memcpy(new->pkt_ip_data+fraglen, p2, fraglen);
150 				new->pkt_end = new->pkt_ip_data + (fraglen<<1);
151 				TAILQ_INSERT_BEFORE(pkt, new, pkt_next);
152 				p += (fraglen << 1);
153 			} else
154 				p += fraglen;
155 
156 			if ((fraglen = pkt->pkt_end - p) > ip_frag_data.size)
157 				fraglen = ip_frag_data.size;
158 		}
159 		TAILQ_REMOVE(pktq, pkt, pkt_next);
160 		pkt_free(pkt);
161 	}
162 	return (0);
163 }
164 
165 struct mod mod_ip_frag = {
166 	"ip_frag",				/* name */
167 	"ip_frag <size> [old|new]",		/* usage */
168 	ip_frag_open,				/* open */
169 	ip_frag_apply,				/* apply */
170 	ip_frag_close				/* close */
171 };
172