]> git.saurik.com Git - apple/xnu.git/blob - bsd/netinet/ip_frag.c
ddb4f1ab4fc617dbe8f86686085f643f21c43647
[apple/xnu.git] / bsd / netinet / ip_frag.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 * Copyright (C) 1993-1997 by Darren Reed.
24 *
25 * Redistribution and use in source and binary forms are permitted
26 * provided that this notice is preserved and due credit is given
27 * to the original author and the contributors.
28 */
29 #if !defined(lint)
30 /* static const char sccsid[] = "@(#)ip_frag.c 1.11 3/24/96 (C) 1993-1995 Darren Reed"; */
31 #endif
32
33
34 #if !defined(KERNEL)
35 # include <string.h>
36 # include <stdlib.h>
37 #endif
38 #include <sys/errno.h>
39 #include <sys/types.h>
40 #include <sys/param.h>
41 #include <sys/time.h>
42 #include <sys/file.h>
43 #if defined(KERNEL)
44 #include <sys/filio.h>
45 #include <sys/fcntl.h>
46 #include <sys/malloc.h>
47 #else
48 #include <sys/ioctl.h>
49 #endif
50 #include <sys/uio.h>
51 #ifndef linux
52 #include <sys/protosw.h>
53 #endif
54 #include <sys/socket.h>
55 #if defined(KERNEL)
56 # include <sys/systm.h>
57 #endif
58 #if !defined(__SVR4) && !defined(__svr4__)
59 # ifndef linux
60 # include <sys/mbuf.h>
61 # endif
62 #else
63 # include <sys/byteorder.h>
64 # include <sys/dditypes.h>
65 # include <sys/stream.h>
66 # include <sys/kmem.h>
67 #endif
68 #if defined(KERNEL)
69 #include <sys/malloc.h>
70 #endif
71
72 #include <net/if.h>
73 #ifdef sun
74 #include <net/af.h>
75 #endif
76 #include <net/route.h>
77 #include <netinet/in.h>
78 #include <netinet/in_systm.h>
79 #include <netinet/ip.h>
80 #ifndef linux
81 #include <netinet/ip_var.h>
82 #endif
83 #include <netinet/tcp.h>
84 #include <netinet/udp.h>
85 #include <netinet/ip_icmp.h>
86 #include "netinet/ip_compat.h"
87 #include <netinet/tcpip.h>
88 #include "netinet/ip_fil.h"
89 #include "netinet/ip_proxy.h"
90 #include "netinet/ip_nat.h"
91 #include "netinet/ip_frag.h"
92 #include "netinet/ip_state.h"
93 #include "netinet/ip_auth.h"
94
95 static ipfr_t *ipfr_heads[IPFT_SIZE];
96 static ipfr_t *ipfr_nattab[IPFT_SIZE];
97 static ipfrstat_t ipfr_stats;
98 static int ipfr_inuse = 0;
99 int fr_ipfrttl = 120; /* 60 seconds */
100 #ifdef KERNEL
101 extern int ipfr_timer_id;
102 #endif
103 #if (SOLARIS || defined(__sgi)) && defined(KERNEL)
104 extern kmutex_t ipf_frag;
105 extern kmutex_t ipf_natfrag;
106 extern kmutex_t ipf_nat;
107 #endif
108
109
110 static ipfr_t *ipfr_new __P((ip_t *, fr_info_t *, int, ipfr_t **));
111 static ipfr_t *ipfr_lookup __P((ip_t *, fr_info_t *, ipfr_t **));
112
113
114 ipfrstat_t *ipfr_fragstats()
115 {
116 ipfr_stats.ifs_table = ipfr_heads;
117 ipfr_stats.ifs_nattab = ipfr_nattab;
118 ipfr_stats.ifs_inuse = ipfr_inuse;
119 return &ipfr_stats;
120 }
121
122
123 /*
124 * add a new entry to the fragment cache, registering it as having come
125 * through this box, with the result of the filter operation.
126 */
127 static ipfr_t *ipfr_new(ip, fin, pass, table)
128 ip_t *ip;
129 fr_info_t *fin;
130 int pass;
131 ipfr_t *table[];
132 {
133 ipfr_t **fp, *fr, frag;
134 u_int idx;
135
136 frag.ipfr_p = ip->ip_p;
137 idx = ip->ip_p;
138 frag.ipfr_id = ip->ip_id;
139 idx += ip->ip_id;
140 frag.ipfr_tos = ip->ip_tos;
141 frag.ipfr_src.s_addr = ip->ip_src.s_addr;
142 idx += ip->ip_src.s_addr;
143 frag.ipfr_dst.s_addr = ip->ip_dst.s_addr;
144 idx += ip->ip_dst.s_addr;
145 idx *= 127;
146 idx %= IPFT_SIZE;
147
148 /*
149 * first, make sure it isn't already there...
150 */
151 for (fp = &table[idx]; (fr = *fp); fp = &fr->ipfr_next)
152 if (!bcmp((char *)&frag.ipfr_src, (char *)&fr->ipfr_src,
153 IPFR_CMPSZ)) {
154 ipfr_stats.ifs_exists++;
155 return NULL;
156 }
157
158 /*
159 * allocate some memory, if possible, if not, just record that we
160 * failed to do so.
161 */
162 KMALLOC(fr, ipfr_t *, sizeof(*fr));
163 if (fr == NULL) {
164 ipfr_stats.ifs_nomem++;
165 return NULL;
166 }
167
168 /*
169 * Instert the fragment into the fragment table, copy the struct used
170 * in the search using bcopy rather than reassign each field.
171 * Set the ttl to the default and mask out logging from "pass"
172 */
173 if ((fr->ipfr_next = table[idx]))
174 table[idx]->ipfr_prev = fr;
175 fr->ipfr_prev = NULL;
176 fr->ipfr_data = NULL;
177 table[idx] = fr;
178 bcopy((char *)&frag.ipfr_src, (char *)&fr->ipfr_src, IPFR_CMPSZ);
179 fr->ipfr_ttl = fr_ipfrttl;
180 fr->ipfr_pass = pass & ~(FR_LOGFIRST|FR_LOG);
181 /*
182 * Compute the offset of the expected start of the next packet.
183 */
184 fr->ipfr_off = (ip->ip_off & 0x1fff) + (fin->fin_dlen >> 3);
185 ipfr_stats.ifs_new++;
186 ipfr_inuse++;
187 return fr;
188 }
189
190
191 int ipfr_newfrag(ip, fin, pass)
192 ip_t *ip;
193 fr_info_t *fin;
194 int pass;
195 {
196 ipfr_t *ipf;
197
198 MUTEX_ENTER(&ipf_frag);
199 ipf = ipfr_new(ip, fin, pass, ipfr_heads);
200 MUTEX_EXIT(&ipf_frag);
201 return ipf ? 0 : -1;
202 }
203
204
205 int ipfr_nat_newfrag(ip, fin, pass, nat)
206 ip_t *ip;
207 fr_info_t *fin;
208 int pass;
209 nat_t *nat;
210 {
211 ipfr_t *ipf;
212
213 MUTEX_ENTER(&ipf_natfrag);
214 if ((ipf = ipfr_new(ip, fin, pass, ipfr_nattab))) {
215 ipf->ipfr_data = nat;
216 nat->nat_data = ipf;
217 }
218 MUTEX_EXIT(&ipf_natfrag);
219 return ipf ? 0 : -1;
220 }
221
222
223 /*
224 * check the fragment cache to see if there is already a record of this packet
225 * with its filter result known.
226 */
227 static ipfr_t *ipfr_lookup(ip, fin, table)
228 ip_t *ip;
229 fr_info_t *fin;
230 ipfr_t *table[];
231 {
232 ipfr_t *f, frag;
233 u_int idx;
234
235 /*
236 * For fragments, we record protocol, packet id, TOS and both IP#'s
237 * (these should all be the same for all fragments of a packet).
238 *
239 * build up a hash value to index the table with.
240 */
241 frag.ipfr_p = ip->ip_p;
242 idx = ip->ip_p;
243 frag.ipfr_id = ip->ip_id;
244 idx += ip->ip_id;
245 frag.ipfr_tos = ip->ip_tos;
246 frag.ipfr_src.s_addr = ip->ip_src.s_addr;
247 idx += ip->ip_src.s_addr;
248 frag.ipfr_dst.s_addr = ip->ip_dst.s_addr;
249 idx += ip->ip_dst.s_addr;
250 idx *= 127;
251 idx %= IPFT_SIZE;
252
253 /*
254 * check the table, careful to only compare the right amount of data
255 */
256 for (f = table[idx]; f; f = f->ipfr_next)
257 if (!bcmp((char *)&frag.ipfr_src, (char *)&f->ipfr_src,
258 IPFR_CMPSZ)) {
259 u_short atoff, off;
260
261 if (f != table[idx]) {
262 /*
263 * move fragment info. to the top of the list
264 * to speed up searches.
265 */
266 if ((f->ipfr_prev->ipfr_next = f->ipfr_next))
267 f->ipfr_next->ipfr_prev = f->ipfr_prev;
268 f->ipfr_next = table[idx];
269 table[idx]->ipfr_prev = f;
270 f->ipfr_prev = NULL;
271 table[idx] = f;
272 }
273 off = ip->ip_off;
274 atoff = off + (fin->fin_dlen >> 3);
275 /*
276 * If we've follwed the fragments, and this is the
277 * last (in order), shrink expiration time.
278 */
279 if ((off & 0x1fff) == f->ipfr_off) {
280 if (!(off & IP_MF))
281 f->ipfr_ttl = 1;
282 else
283 f->ipfr_off = atoff;
284 }
285 ipfr_stats.ifs_hits++;
286 return f;
287 }
288 return NULL;
289 }
290
291
292 /*
293 * functional interface for NAT lookups of the NAT fragment cache
294 */
295 nat_t *ipfr_nat_knownfrag(ip, fin)
296 ip_t *ip;
297 fr_info_t *fin;
298 {
299 nat_t *nat;
300 ipfr_t *ipf;
301
302 MUTEX_ENTER(&ipf_natfrag);
303 ipf = ipfr_lookup(ip, fin, ipfr_nattab);
304 if (ipf) {
305 nat = ipf->ipfr_data;
306 /*
307 * This is the last fragment for this packet.
308 */
309 if (ipf->ipfr_ttl == 1) {
310 nat->nat_data = NULL;
311 ipf->ipfr_data = NULL;
312 }
313 } else
314 nat = NULL;
315 MUTEX_EXIT(&ipf_natfrag);
316 return nat;
317 }
318
319
320 /*
321 * functional interface for normal lookups of the fragment cache
322 */
323 int ipfr_knownfrag(ip, fin)
324 ip_t *ip;
325 fr_info_t *fin;
326 {
327 int ret;
328 ipfr_t *ipf;
329
330 MUTEX_ENTER(&ipf_frag);
331 ipf = ipfr_lookup(ip, fin, ipfr_heads);
332 ret = ipf ? ipf->ipfr_pass : 0;
333 MUTEX_EXIT(&ipf_frag);
334 return ret;
335 }
336
337
338 /*
339 * forget any references to this external object.
340 */
341 void ipfr_forget(nat)
342 void *nat;
343 {
344 ipfr_t *fr;
345 int idx;
346
347 MUTEX_ENTER(&ipf_natfrag);
348 for (idx = IPFT_SIZE - 1; idx >= 0; idx--)
349 for (fr = ipfr_heads[idx]; fr; fr = fr->ipfr_next)
350 if (fr->ipfr_data == nat)
351 fr->ipfr_data = NULL;
352
353 MUTEX_EXIT(&ipf_natfrag);
354 }
355
356
357 /*
358 * Free memory in use by fragment state info. kept.
359 */
360 void ipfr_unload()
361 {
362 ipfr_t **fp, *fr;
363 nat_t *nat;
364 int idx;
365
366 MUTEX_ENTER(&ipf_frag);
367 for (idx = IPFT_SIZE - 1; idx >= 0; idx--)
368 for (fp = &ipfr_heads[idx]; (fr = *fp); ) {
369 *fp = fr->ipfr_next;
370 KFREE(fr);
371 }
372 MUTEX_EXIT(&ipf_frag);
373
374 MUTEX_ENTER(&ipf_nat);
375 MUTEX_ENTER(&ipf_natfrag);
376 for (idx = IPFT_SIZE - 1; idx >= 0; idx--)
377 for (fp = &ipfr_nattab[idx]; (fr = *fp); ) {
378 *fp = fr->ipfr_next;
379 if ((nat = (nat_t *)fr->ipfr_data)) {
380 if (nat->nat_data == fr)
381 nat->nat_data = NULL;
382 }
383 KFREE(fr);
384 }
385 MUTEX_EXIT(&ipf_natfrag);
386 MUTEX_EXIT(&ipf_nat);
387 }
388
389
390 #ifdef KERNEL
391 /*
392 * Slowly expire held state for fragments. Timeouts are set * in expectation
393 * of this being called twice per second.
394 */
395 # if (BSD >= 199306) || SOLARIS || defined(__sgi)
396 void ipfr_slowtimer()
397 # else
398 int ipfr_slowtimer()
399 # endif
400 {
401 ipfr_t **fp, *fr;
402 nat_t *nat;
403 int s, idx;
404 boolean_t funnel_state;
405
406 funnel_state = thread_funnel_set(network_flock, TRUE);
407 #ifdef __sgi
408 ipfilter_sgi_intfsync();
409 #endif
410
411 SPL_NET(s);
412 MUTEX_ENTER(&ipf_frag);
413
414 /*
415 * Go through the entire table, looking for entries to expire,
416 * decreasing the ttl by one for each entry. If it reaches 0,
417 * remove it from the chain and free it.
418 */
419 for (idx = IPFT_SIZE - 1; idx >= 0; idx--)
420 for (fp = &ipfr_heads[idx]; (fr = *fp); ) {
421 --fr->ipfr_ttl;
422 if (fr->ipfr_ttl == 0) {
423 if (fr->ipfr_prev)
424 fr->ipfr_prev->ipfr_next =
425 fr->ipfr_next;
426 if (fr->ipfr_next)
427 fr->ipfr_next->ipfr_prev =
428 fr->ipfr_prev;
429 *fp = fr->ipfr_next;
430 ipfr_stats.ifs_expire++;
431 ipfr_inuse--;
432 KFREE(fr);
433 } else
434 fp = &fr->ipfr_next;
435 }
436 MUTEX_EXIT(&ipf_frag);
437
438 /*
439 * Same again for the NAT table, except that if the structure also
440 * still points to a NAT structure, and the NAT structure points back
441 * at the one to be free'd, NULL the reference from the NAT struct.
442 * NOTE: We need to grab both mutex's early, and in this order so as
443 * to prevent a deadlock if both try to expire at the same time.
444 */
445 MUTEX_ENTER(&ipf_nat);
446 MUTEX_ENTER(&ipf_natfrag);
447 for (idx = IPFT_SIZE - 1; idx >= 0; idx--)
448 for (fp = &ipfr_nattab[idx]; (fr = *fp); ) {
449 --fr->ipfr_ttl;
450 if (fr->ipfr_ttl == 0) {
451 if (fr->ipfr_prev)
452 fr->ipfr_prev->ipfr_next =
453 fr->ipfr_next;
454 if (fr->ipfr_next)
455 fr->ipfr_next->ipfr_prev =
456 fr->ipfr_prev;
457 *fp = fr->ipfr_next;
458 ipfr_stats.ifs_expire++;
459 ipfr_inuse--;
460 if ((nat = (nat_t *)fr->ipfr_data)) {
461 if (nat->nat_data == fr)
462 nat->nat_data = NULL;
463 }
464 KFREE(fr);
465 } else
466 fp = &fr->ipfr_next;
467 }
468 MUTEX_EXIT(&ipf_natfrag);
469 MUTEX_EXIT(&ipf_nat);
470 SPL_X(s);
471 fr_timeoutstate();
472 ip_natexpire();
473 fr_authexpire();
474 # if SOLARIS
475 ipfr_timer_id = timeout(ipfr_slowtimer, NULL, drv_usectohz(500000));
476 # else
477 # ifndef linux
478 ip_slowtimo();
479 # endif
480 # if (BSD < 199306) && !defined(__sgi)
481 (void) thread_funnel_set(network_flock, FALSE);
482 return 0;
483 # endif
484 # endif
485 (void) thread_funnel_set(network_flock, FALSE);
486 }
487 #endif /* defined(KERNEL) */