]> git.saurik.com Git - apple/xnu.git/blame - bsd/netinet/ip_frag.c
xnu-124.13.tar.gz
[apple/xnu.git] / bsd / netinet / ip_frag.c
CommitLineData
1c79356b
A
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
95static ipfr_t *ipfr_heads[IPFT_SIZE];
96static ipfr_t *ipfr_nattab[IPFT_SIZE];
97static ipfrstat_t ipfr_stats;
98static int ipfr_inuse = 0;
99 int fr_ipfrttl = 120; /* 60 seconds */
100#ifdef KERNEL
101extern int ipfr_timer_id;
102#endif
103#if (SOLARIS || defined(__sgi)) && defined(KERNEL)
104extern kmutex_t ipf_frag;
105extern kmutex_t ipf_natfrag;
106extern kmutex_t ipf_nat;
107#endif
108
109
110static ipfr_t *ipfr_new __P((ip_t *, fr_info_t *, int, ipfr_t **));
111static ipfr_t *ipfr_lookup __P((ip_t *, fr_info_t *, ipfr_t **));
112
113
114ipfrstat_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 */
127static ipfr_t *ipfr_new(ip, fin, pass, table)
128ip_t *ip;
129fr_info_t *fin;
130int pass;
131ipfr_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
191int ipfr_newfrag(ip, fin, pass)
192ip_t *ip;
193fr_info_t *fin;
194int 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
205int ipfr_nat_newfrag(ip, fin, pass, nat)
206ip_t *ip;
207fr_info_t *fin;
208int pass;
209nat_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 */
227static ipfr_t *ipfr_lookup(ip, fin, table)
228ip_t *ip;
229fr_info_t *fin;
230ipfr_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 */
295nat_t *ipfr_nat_knownfrag(ip, fin)
296ip_t *ip;
297fr_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 */
323int ipfr_knownfrag(ip, fin)
324ip_t *ip;
325fr_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 */
341void ipfr_forget(nat)
342void *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 */
360void 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)
396void ipfr_slowtimer()
397# else
398int 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) */