]>
Commit | Line | Data |
---|---|---|
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) 1995-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_state.c 1.8 6/5/96 (C) 1993-1995 Darren Reed"; */ | |
31 | #endif | |
32 | ||
33 | #include "opt_ipfilter.h" | |
34 | #if defined(KERNEL) && !defined(_KERNEL) | |
35 | #define _KERNEL | |
36 | #endif | |
37 | #define __FreeBSD_version 300000 /* it's a hack, but close enough */ | |
38 | ||
39 | #if !defined(_KERNEL) && !defined(KERNEL) && !defined(__KERNEL__) | |
40 | # include <stdlib.h> | |
41 | # include <string.h> | |
42 | #else | |
43 | # ifdef linux | |
44 | # include <linux/kernel.h> | |
45 | # include <linux/module.h> | |
46 | # endif | |
47 | #endif | |
48 | #include <sys/errno.h> | |
49 | #include <sys/types.h> | |
50 | #include <sys/param.h> | |
51 | #include <sys/file.h> | |
52 | #if defined(KERNEL) && (__FreeBSD_version >= 220000) | |
53 | # include <sys/filio.h> | |
54 | # include <sys/fcntl.h> | |
55 | # include <sys/malloc.h> | |
56 | #else | |
57 | # include <sys/ioctl.h> | |
58 | #endif | |
59 | #include <sys/time.h> | |
60 | #include <sys/uio.h> | |
61 | #ifndef linux | |
62 | #include <sys/protosw.h> | |
63 | #endif | |
64 | #include <sys/socket.h> | |
65 | #if defined(_KERNEL) && !defined(linux) | |
66 | # include <sys/systm.h> | |
67 | #endif | |
68 | #if !defined(__SVR4) && !defined(__svr4__) | |
69 | # ifndef linux | |
70 | # include <sys/mbuf.h> | |
71 | # endif | |
72 | #else | |
73 | # include <sys/filio.h> | |
74 | # include <sys/byteorder.h> | |
75 | # include <sys/dditypes.h> | |
76 | # include <sys/stream.h> | |
77 | # include <sys/kmem.h> | |
78 | #endif | |
79 | ||
80 | #include <net/if.h> | |
81 | #if sun | |
82 | #include <net/af.h> | |
83 | #endif | |
84 | #include <net/route.h> | |
85 | #include <netinet/in.h> | |
86 | #include <netinet/in_systm.h> | |
87 | #include <netinet/ip.h> | |
88 | #include <netinet/tcp.h> | |
89 | #ifndef linux | |
90 | # include <netinet/ip_var.h> | |
91 | # include <netinet/tcp_fsm.h> | |
92 | #endif | |
93 | #include <netinet/udp.h> | |
94 | #include <netinet/ip_icmp.h> | |
95 | #include "netinet/ip_compat.h" | |
96 | #include <netinet/tcpip.h> | |
97 | #include "netinet/ip_fil.h" | |
98 | #include "netinet/ip_nat.h" | |
99 | #include "netinet/ip_frag.h" | |
100 | #include "netinet/ip_proxy.h" | |
101 | #include "netinet/ip_state.h" | |
102 | #ifndef MIN | |
103 | #define MIN(a,b) (((a)<(b))?(a):(b)) | |
104 | #endif | |
105 | ||
106 | #define TCP_CLOSE (TH_FIN|TH_RST) | |
107 | ||
108 | static ipstate_t *ips_table[IPSTATE_SIZE]; | |
109 | static int ips_num = 0; | |
110 | static ips_stat_t ips_stats; | |
111 | #if (SOLARIS || defined(__sgi)) && defined(_KERNEL) | |
112 | extern kmutex_t ipf_state; | |
113 | #endif | |
114 | ||
115 | static int fr_matchsrcdst __P((ipstate_t *, struct in_addr, struct in_addr, | |
116 | fr_info_t *, void *, u_short, u_short)); | |
117 | static int fr_state_flush __P((int)); | |
118 | static ips_stat_t *fr_statetstats __P((void)); | |
119 | ||
120 | ||
121 | #define FIVE_DAYS (2 * 5 * 86400) /* 5 days: half closed session */ | |
122 | ||
123 | u_long fr_tcpidletimeout = FIVE_DAYS, | |
124 | fr_tcpclosewait = 60, | |
125 | fr_tcplastack = 20, | |
126 | fr_tcptimeout = 120, | |
127 | fr_tcpclosed = 1, | |
128 | fr_udptimeout = 120, | |
129 | fr_icmptimeout = 120; | |
130 | ||
131 | ||
132 | static ips_stat_t *fr_statetstats() | |
133 | { | |
134 | ips_stats.iss_active = ips_num; | |
135 | ips_stats.iss_table = ips_table; | |
136 | return &ips_stats; | |
137 | } | |
138 | ||
139 | ||
140 | /* | |
141 | * flush state tables. two actions currently defined: | |
142 | * which == 0 : flush all state table entries | |
143 | * which == 1 : flush TCP connections which have started to close but are | |
144 | * stuck for some reason. | |
145 | */ | |
146 | static int fr_state_flush(which) | |
147 | int which; | |
148 | { | |
149 | register int i; | |
150 | register ipstate_t *is, **isp; | |
151 | #if defined(_KERNEL) && !SOLARIS | |
152 | int s; | |
153 | #endif | |
154 | int delete, removed = 0; | |
155 | ||
156 | SPL_NET(s); | |
157 | MUTEX_ENTER(&ipf_state); | |
158 | for (i = 0; i < IPSTATE_SIZE; i++) | |
159 | for (isp = &ips_table[i]; (is = *isp); ) { | |
160 | delete = 0; | |
161 | ||
162 | switch (which) | |
163 | { | |
164 | case 0 : | |
165 | delete = 1; | |
166 | break; | |
167 | case 1 : | |
168 | if ((is->is_p == IPPROTO_TCP) && | |
169 | (((is->is_state[0] <= TCPS_ESTABLISHED) && | |
170 | (is->is_state[1] > TCPS_ESTABLISHED)) || | |
171 | ((is->is_state[1] <= TCPS_ESTABLISHED) && | |
172 | (is->is_state[0] > TCPS_ESTABLISHED)))) | |
173 | delete = 1; | |
174 | break; | |
175 | } | |
176 | ||
177 | if (delete) { | |
178 | *isp = is->is_next; | |
179 | if (is->is_p == IPPROTO_TCP) | |
180 | ips_stats.iss_fin++; | |
181 | else | |
182 | ips_stats.iss_expire++; | |
183 | #if IPFILTER_LOG | |
184 | ipstate_log(is, ISL_FLUSH); | |
185 | #endif | |
186 | KFREE(is); | |
187 | ips_num--; | |
188 | removed++; | |
189 | } else | |
190 | isp = &is->is_next; | |
191 | } | |
192 | MUTEX_EXIT(&ipf_state); | |
193 | SPL_X(s); | |
194 | return removed; | |
195 | } | |
196 | ||
197 | ||
198 | int fr_state_ioctl(data, cmd, mode) | |
199 | caddr_t data; | |
200 | #if defined(__NetBSD__) || defined(__OpenBSD__) | |
201 | u_long cmd; | |
202 | #else | |
203 | int cmd; | |
204 | #endif | |
205 | int mode; | |
206 | { | |
207 | int arg, ret, error = 0; | |
208 | ||
209 | switch (cmd) | |
210 | { | |
211 | case SIOCIPFFL : | |
212 | IRCOPY(data, (caddr_t)&arg, sizeof(arg)); | |
213 | if (arg == 0 || arg == 1) { | |
214 | ret = fr_state_flush(arg); | |
215 | IWCOPY((caddr_t)&ret, data, sizeof(ret)); | |
216 | } else | |
217 | error = EINVAL; | |
218 | break; | |
219 | case SIOCGIPST : | |
220 | IWCOPY((caddr_t)fr_statetstats(), data, sizeof(ips_stat_t)); | |
221 | break; | |
222 | case FIONREAD : | |
223 | #if IPFILTER_LOG | |
224 | IWCOPY((caddr_t)&iplused[IPL_LOGSTATE], (caddr_t)data, | |
225 | sizeof(iplused[IPL_LOGSTATE])); | |
226 | #endif | |
227 | break; | |
228 | default : | |
229 | return EINVAL; | |
230 | } | |
231 | return error; | |
232 | } | |
233 | ||
234 | ||
235 | /* | |
236 | * Create a new ipstate structure and hang it off the hash table. | |
237 | */ | |
238 | int fr_addstate(ip, fin, pass) | |
239 | ip_t *ip; | |
240 | fr_info_t *fin; | |
241 | u_int pass; | |
242 | { | |
243 | ipstate_t ips; | |
244 | register ipstate_t *is = &ips; | |
245 | register u_int hv; | |
246 | ||
247 | if ((ip->ip_off & 0x1fff) || (fin->fin_fi.fi_fl & FI_SHORT)) | |
248 | return -1; | |
249 | if (ips_num == IPSTATE_MAX) { | |
250 | ips_stats.iss_max++; | |
251 | return -1; | |
252 | } | |
253 | ips.is_age = 1; | |
254 | ips.is_state[0] = 0; | |
255 | ips.is_state[1] = 0; | |
256 | /* | |
257 | * Copy and calculate... | |
258 | */ | |
259 | hv = (is->is_p = ip->ip_p); | |
260 | hv += (is->is_src.s_addr = ip->ip_src.s_addr); | |
261 | hv += (is->is_dst.s_addr = ip->ip_dst.s_addr); | |
262 | ||
263 | switch (ip->ip_p) | |
264 | { | |
265 | case IPPROTO_ICMP : | |
266 | { | |
267 | struct icmp *ic = (struct icmp *)fin->fin_dp; | |
268 | ||
269 | switch (ic->icmp_type) | |
270 | { | |
271 | case ICMP_ECHO : | |
272 | is->is_icmp.ics_type = ICMP_ECHOREPLY; /* XXX */ | |
273 | hv += (is->is_icmp.ics_id = ic->icmp_id); | |
274 | hv += (is->is_icmp.ics_seq = ic->icmp_seq); | |
275 | break; | |
276 | case ICMP_TSTAMP : | |
277 | case ICMP_IREQ : | |
278 | case ICMP_MASKREQ : | |
279 | is->is_icmp.ics_type = ic->icmp_type + 1; | |
280 | break; | |
281 | default : | |
282 | return -1; | |
283 | } | |
284 | ips_stats.iss_icmp++; | |
285 | is->is_age = fr_icmptimeout; | |
286 | break; | |
287 | } | |
288 | case IPPROTO_TCP : | |
289 | { | |
290 | register tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp; | |
291 | ||
292 | /* | |
293 | * The endian of the ports doesn't matter, but the ack and | |
294 | * sequence numbers do as we do mathematics on them later. | |
295 | */ | |
296 | hv += (is->is_dport = tcp->th_dport); | |
297 | hv += (is->is_sport = tcp->th_sport); | |
298 | is->is_seq = ntohl(tcp->th_seq); | |
299 | is->is_ack = ntohl(tcp->th_ack); | |
300 | is->is_swin = ntohs(tcp->th_win); | |
301 | is->is_dwin = is->is_swin; /* start them the same */ | |
302 | ips_stats.iss_tcp++; | |
303 | /* | |
304 | * If we're creating state for a starting connection, start the | |
305 | * timer on it as we'll never see an error if it fails to | |
306 | * connect. | |
307 | */ | |
308 | if ((tcp->th_flags & (TH_SYN|TH_ACK)) == TH_SYN) | |
309 | is->is_ack = 0; /* Trumpet WinSock 'ism */ | |
310 | fr_tcp_age(&is->is_age, is->is_state, ip, fin, | |
311 | tcp->th_sport == is->is_sport); | |
312 | break; | |
313 | } | |
314 | case IPPROTO_UDP : | |
315 | { | |
316 | register tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp; | |
317 | ||
318 | hv += (is->is_dport = tcp->th_dport); | |
319 | hv += (is->is_sport = tcp->th_sport); | |
320 | ips_stats.iss_udp++; | |
321 | is->is_age = fr_udptimeout; | |
322 | break; | |
323 | } | |
324 | default : | |
325 | return -1; | |
326 | } | |
327 | ||
328 | KMALLOC(is, ipstate_t *, sizeof(*is)); | |
329 | if (is == NULL) { | |
330 | ips_stats.iss_nomem++; | |
331 | return -1; | |
332 | } | |
333 | bcopy((char *)&ips, (char *)is, sizeof(*is)); | |
334 | hv %= IPSTATE_SIZE; | |
335 | MUTEX_ENTER(&ipf_state); | |
336 | ||
337 | is->is_pass = pass; | |
338 | is->is_pkts = 1; | |
339 | is->is_bytes = ip->ip_len; | |
340 | /* | |
341 | * Copy these from the rule itself. | |
342 | */ | |
343 | is->is_opt = fin->fin_fr->fr_ip.fi_optmsk; | |
344 | is->is_optmsk = fin->fin_fr->fr_mip.fi_optmsk; | |
345 | is->is_sec = fin->fin_fr->fr_ip.fi_secmsk; | |
346 | is->is_secmsk = fin->fin_fr->fr_mip.fi_secmsk; | |
347 | is->is_auth = fin->fin_fr->fr_ip.fi_auth; | |
348 | is->is_authmsk = fin->fin_fr->fr_mip.fi_auth; | |
349 | is->is_flags = fin->fin_fr->fr_ip.fi_fl; | |
350 | is->is_flags |= fin->fin_fr->fr_mip.fi_fl << 4; | |
351 | /* | |
352 | * add into table. | |
353 | */ | |
354 | is->is_next = ips_table[hv]; | |
355 | ips_table[hv] = is; | |
356 | if (fin->fin_out) { | |
357 | is->is_ifpin = NULL; | |
358 | is->is_ifpout = fin->fin_ifp; | |
359 | } else { | |
360 | is->is_ifpin = fin->fin_ifp; | |
361 | is->is_ifpout = NULL; | |
362 | } | |
363 | if (pass & FR_LOGFIRST) | |
364 | is->is_pass &= ~(FR_LOGFIRST|FR_LOG); | |
365 | ips_num++; | |
366 | #if IPFILTER_LOG | |
367 | ipstate_log(is, ISL_NEW); | |
368 | #endif | |
369 | MUTEX_EXIT(&ipf_state); | |
370 | if (fin->fin_fi.fi_fl & FI_FRAG) | |
371 | ipfr_newfrag(ip, fin, pass ^ FR_KEEPSTATE); | |
372 | return 0; | |
373 | } | |
374 | ||
375 | ||
376 | /* | |
377 | * check to see if a packet with TCP headers fits within the TCP window. | |
378 | * change timeout depending on whether new packet is a SYN-ACK returning for a | |
379 | * SYN or a RST or FIN which indicate time to close up shop. | |
380 | */ | |
381 | int fr_tcpstate(is, fin, ip, tcp) | |
382 | register ipstate_t *is; | |
383 | fr_info_t *fin; | |
384 | ip_t *ip; | |
385 | tcphdr_t *tcp; | |
386 | { | |
387 | register int seqskew, ackskew; | |
388 | register u_short swin, dwin; | |
389 | register tcp_seq seq, ack; | |
390 | int source; | |
391 | ||
392 | /* | |
393 | * Find difference between last checked packet and this packet. | |
394 | */ | |
395 | seq = ntohl(tcp->th_seq); | |
396 | ack = ntohl(tcp->th_ack); | |
397 | source = (ip->ip_src.s_addr == is->is_src.s_addr); | |
398 | ||
399 | if (!(tcp->th_flags & TH_ACK)) /* Pretend an ack was sent */ | |
400 | ack = source ? is->is_ack : is->is_seq; | |
401 | ||
402 | if (source) { | |
403 | if (!is->is_seq) | |
404 | /* | |
405 | * Must be an outgoing SYN-ACK in reply to a SYN. | |
406 | */ | |
407 | is->is_seq = seq; | |
408 | seqskew = seq - is->is_seq; | |
409 | ackskew = ack - is->is_ack; | |
410 | } else { | |
411 | if (!is->is_ack) | |
412 | /* | |
413 | * Must be a SYN-ACK in reply to a SYN. | |
414 | */ | |
415 | is->is_ack = seq; | |
416 | ackskew = seq - is->is_ack; | |
417 | seqskew = ack - is->is_seq; | |
418 | } | |
419 | ||
420 | /* | |
421 | * Make skew values absolute | |
422 | */ | |
423 | if (seqskew < 0) | |
424 | seqskew = -seqskew; | |
425 | if (ackskew < 0) | |
426 | ackskew = -ackskew; | |
427 | ||
428 | /* | |
429 | * If the difference in sequence and ack numbers is within the | |
430 | * window size of the connection, store these values and match | |
431 | * the packet. | |
432 | */ | |
433 | if (source) { | |
434 | swin = is->is_swin; | |
435 | dwin = is->is_dwin; | |
436 | } else { | |
437 | dwin = is->is_swin; | |
438 | swin = is->is_dwin; | |
439 | } | |
440 | ||
441 | if ((seqskew <= dwin) && (ackskew <= swin)) { | |
442 | if (source) { | |
443 | is->is_seq = seq; | |
444 | is->is_ack = ack; | |
445 | is->is_swin = ntohs(tcp->th_win); | |
446 | } else { | |
447 | is->is_seq = ack; | |
448 | is->is_ack = seq; | |
449 | is->is_dwin = ntohs(tcp->th_win); | |
450 | } | |
451 | ips_stats.iss_hits++; | |
452 | is->is_pkts++; | |
453 | is->is_bytes += ip->ip_len; | |
454 | /* | |
455 | * Nearing end of connection, start timeout. | |
456 | */ | |
457 | fr_tcp_age(&is->is_age, is->is_state, ip, fin, source); | |
458 | return 1; | |
459 | } | |
460 | return 0; | |
461 | } | |
462 | ||
463 | ||
464 | static int fr_matchsrcdst(is, src, dst, fin, tcp, sp, dp) | |
465 | ipstate_t *is; | |
466 | struct in_addr src, dst; | |
467 | fr_info_t *fin; | |
468 | void *tcp; | |
469 | u_short sp, dp; | |
470 | { | |
471 | int ret = 0, rev, out; | |
472 | void *ifp; | |
473 | ||
474 | rev = (is->is_dst.s_addr != dst.s_addr); | |
475 | ifp = fin->fin_ifp; | |
476 | out = fin->fin_out; | |
477 | ||
478 | if (!rev) { | |
479 | if (out) { | |
480 | if (!is->is_ifpout) | |
481 | is->is_ifpout = ifp; | |
482 | } else { | |
483 | if (!is->is_ifpin) | |
484 | is->is_ifpin = ifp; | |
485 | } | |
486 | } else { | |
487 | if (out) { | |
488 | if (!is->is_ifpin) | |
489 | is->is_ifpin = ifp; | |
490 | } else { | |
491 | if (!is->is_ifpout) | |
492 | is->is_ifpout = ifp; | |
493 | } | |
494 | } | |
495 | ||
496 | if (!rev) { | |
497 | if (((out && is->is_ifpout == ifp) || | |
498 | (!out && is->is_ifpin == ifp)) && | |
499 | (is->is_dst.s_addr == dst.s_addr) && | |
500 | (is->is_src.s_addr == src.s_addr) && | |
501 | (!tcp || (sp == is->is_sport) && | |
502 | (dp == is->is_dport))) { | |
503 | ret = 1; | |
504 | } | |
505 | } else { | |
506 | if (((out && is->is_ifpin == ifp) || | |
507 | (!out && is->is_ifpout == ifp)) && | |
508 | (is->is_dst.s_addr == src.s_addr) && | |
509 | (is->is_src.s_addr == dst.s_addr) && | |
510 | (!tcp || (sp == is->is_dport) && | |
511 | (dp == is->is_sport))) { | |
512 | ret = 1; | |
513 | } | |
514 | } | |
515 | ||
516 | /* | |
517 | * Whether or not this should be here, is questionable, but the aim | |
518 | * is to get this out of the main line. | |
519 | */ | |
520 | if (ret) { | |
521 | if (((fin->fin_fi.fi_optmsk & is->is_optmsk) != is->is_opt) || | |
522 | ((fin->fin_fi.fi_secmsk & is->is_secmsk) != is->is_sec) || | |
523 | ((fin->fin_fi.fi_auth & is->is_authmsk) != is->is_auth) || | |
524 | ((fin->fin_fi.fi_fl & (is->is_flags >> 4)) != | |
525 | (is->is_flags & 0xf))) | |
526 | ret = 0; | |
527 | } | |
528 | return ret; | |
529 | } | |
530 | ||
531 | ||
532 | /* | |
533 | * Check if a packet has a registered state. | |
534 | */ | |
535 | int fr_checkstate(ip, fin) | |
536 | ip_t *ip; | |
537 | fr_info_t *fin; | |
538 | { | |
539 | register struct in_addr dst, src; | |
540 | register ipstate_t *is, **isp; | |
541 | register u_char pr; | |
542 | struct icmp *ic; | |
543 | tcphdr_t *tcp; | |
544 | u_int hv, hlen, pass; | |
545 | ||
546 | if ((ip->ip_off & 0x1fff) || (fin->fin_fi.fi_fl & FI_SHORT)) | |
547 | return 0; | |
548 | ||
549 | hlen = fin->fin_hlen; | |
550 | tcp = (tcphdr_t *)((char *)ip + hlen); | |
551 | ic = (struct icmp *)tcp; | |
552 | hv = (pr = ip->ip_p); | |
553 | hv += (src.s_addr = ip->ip_src.s_addr); | |
554 | hv += (dst.s_addr = ip->ip_dst.s_addr); | |
555 | ||
556 | /* | |
557 | * Search the hash table for matching packet header info. | |
558 | */ | |
559 | switch (ip->ip_p) | |
560 | { | |
561 | case IPPROTO_ICMP : | |
562 | hv += ic->icmp_id; | |
563 | hv += ic->icmp_seq; | |
564 | hv %= IPSTATE_SIZE; | |
565 | MUTEX_ENTER(&ipf_state); | |
566 | for (isp = &ips_table[hv]; (is = *isp); isp = &is->is_next) | |
567 | if ((is->is_p == pr) && | |
568 | (ic->icmp_id == is->is_icmp.ics_id) && | |
569 | (ic->icmp_seq == is->is_icmp.ics_seq) && | |
570 | fr_matchsrcdst(is, src, dst, fin, NULL, 0, 0)) { | |
571 | if (is->is_icmp.ics_type != ic->icmp_type) | |
572 | continue; | |
573 | is->is_age = fr_icmptimeout; | |
574 | is->is_pkts++; | |
575 | is->is_bytes += ip->ip_len; | |
576 | ips_stats.iss_hits++; | |
577 | pass = is->is_pass; | |
578 | MUTEX_EXIT(&ipf_state); | |
579 | return pass; | |
580 | } | |
581 | MUTEX_EXIT(&ipf_state); | |
582 | break; | |
583 | case IPPROTO_TCP : | |
584 | { | |
585 | register u_short dport = tcp->th_dport, sport = tcp->th_sport; | |
586 | ||
587 | hv += dport; | |
588 | hv += sport; | |
589 | hv %= IPSTATE_SIZE; | |
590 | MUTEX_ENTER(&ipf_state); | |
591 | for (isp = &ips_table[hv]; (is = *isp); isp = &is->is_next) | |
592 | if ((is->is_p == pr) && | |
593 | fr_matchsrcdst(is, src, dst, fin, tcp, | |
594 | sport, dport)) { | |
595 | if (fr_tcpstate(is, fin, ip, tcp)) { | |
596 | pass = is->is_pass; | |
597 | #ifdef _KERNEL | |
598 | MUTEX_EXIT(&ipf_state); | |
599 | #else | |
600 | ||
601 | if (tcp->th_flags & TCP_CLOSE) { | |
602 | *isp = is->is_next; | |
603 | isp = &ips_table[hv]; | |
604 | KFREE(is); | |
605 | } | |
606 | #endif | |
607 | return pass; | |
608 | } | |
609 | } | |
610 | MUTEX_EXIT(&ipf_state); | |
611 | break; | |
612 | } | |
613 | case IPPROTO_UDP : | |
614 | { | |
615 | register u_short dport = tcp->th_dport, sport = tcp->th_sport; | |
616 | ||
617 | hv += dport; | |
618 | hv += sport; | |
619 | hv %= IPSTATE_SIZE; | |
620 | /* | |
621 | * Nothing else to match on but ports. and IP#'s | |
622 | */ | |
623 | MUTEX_ENTER(&ipf_state); | |
624 | for (is = ips_table[hv]; is; is = is->is_next) | |
625 | if ((is->is_p == pr) && | |
626 | fr_matchsrcdst(is, src, dst, fin, | |
627 | tcp, sport, dport)) { | |
628 | ips_stats.iss_hits++; | |
629 | is->is_pkts++; | |
630 | is->is_bytes += ip->ip_len; | |
631 | is->is_age = fr_udptimeout; | |
632 | pass = is->is_pass; | |
633 | MUTEX_EXIT(&ipf_state); | |
634 | return pass; | |
635 | } | |
636 | MUTEX_EXIT(&ipf_state); | |
637 | break; | |
638 | } | |
639 | default : | |
640 | break; | |
641 | } | |
642 | ips_stats.iss_miss++; | |
643 | return 0; | |
644 | } | |
645 | ||
646 | ||
647 | /* | |
648 | * Free memory in use by all state info. kept. | |
649 | */ | |
650 | void fr_stateunload() | |
651 | { | |
652 | register int i; | |
653 | register ipstate_t *is, **isp; | |
654 | ||
655 | MUTEX_ENTER(&ipf_state); | |
656 | for (i = 0; i < IPSTATE_SIZE; i++) | |
657 | for (isp = &ips_table[i]; (is = *isp); ) { | |
658 | *isp = is->is_next; | |
659 | KFREE(is); | |
660 | } | |
661 | MUTEX_EXIT(&ipf_state); | |
662 | } | |
663 | ||
664 | ||
665 | /* | |
666 | * Slowly expire held state for thingslike UDP and ICMP. Timeouts are set | |
667 | * in expectation of this being called twice per second. | |
668 | */ | |
669 | void fr_timeoutstate() | |
670 | { | |
671 | register int i; | |
672 | register ipstate_t *is, **isp; | |
673 | #if defined(_KERNEL) && !SOLARIS | |
674 | int s; | |
675 | #endif | |
676 | ||
677 | SPL_NET(s); | |
678 | MUTEX_ENTER(&ipf_state); | |
679 | for (i = 0; i < IPSTATE_SIZE; i++) | |
680 | for (isp = &ips_table[i]; (is = *isp); ) | |
681 | if (is->is_age && !--is->is_age) { | |
682 | *isp = is->is_next; | |
683 | if (is->is_p == IPPROTO_TCP) | |
684 | ips_stats.iss_fin++; | |
685 | else | |
686 | ips_stats.iss_expire++; | |
687 | #if IPFILTER_LOG | |
688 | ipstate_log(is, ISL_EXPIRE); | |
689 | #endif | |
690 | KFREE(is); | |
691 | ips_num--; | |
692 | } else | |
693 | isp = &is->is_next; | |
694 | MUTEX_EXIT(&ipf_state); | |
695 | SPL_X(s); | |
696 | } | |
697 | ||
698 | ||
699 | /* | |
700 | * Original idea freom Pradeep Krishnan for use primarily with NAT code. | |
701 | * (pkrishna@netcom.com) | |
702 | */ | |
703 | void fr_tcp_age(age, state, ip, fin, dir) | |
704 | u_long *age; | |
705 | u_char *state; | |
706 | ip_t *ip; | |
707 | fr_info_t *fin; | |
708 | int dir; | |
709 | { | |
710 | tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp; | |
711 | u_char flags = tcp->th_flags; | |
712 | int dlen, ostate; | |
713 | ||
714 | ostate = state[1 - dir]; | |
715 | ||
716 | dlen = ip->ip_len - fin->fin_hlen - (tcp->th_off << 2); | |
717 | ||
718 | if (flags & TH_RST) { | |
719 | if (!(tcp->th_flags & TH_PUSH) && !dlen) { | |
720 | *age = fr_tcpclosed; | |
721 | state[dir] = TCPS_CLOSED; | |
722 | } else { | |
723 | *age = fr_tcpclosewait; | |
724 | state[dir] = TCPS_CLOSE_WAIT; | |
725 | } | |
726 | return; | |
727 | } | |
728 | ||
729 | *age = fr_tcptimeout; /* 1 min */ | |
730 | ||
731 | switch(state[dir]) | |
732 | { | |
733 | case TCPS_FIN_WAIT_2: | |
734 | case TCPS_CLOSED: | |
735 | if ((flags & TH_OPENING) == TH_OPENING) | |
736 | state[dir] = TCPS_SYN_RECEIVED; | |
737 | else if (flags & TH_SYN) | |
738 | state[dir] = TCPS_SYN_SENT; | |
739 | break; | |
740 | case TCPS_SYN_RECEIVED: | |
741 | if ((flags & (TH_FIN|TH_ACK)) == TH_ACK) { | |
742 | state[dir] = TCPS_ESTABLISHED; | |
743 | current_active_connections++; | |
744 | *age = fr_tcpidletimeout; | |
745 | } | |
746 | break; | |
747 | case TCPS_SYN_SENT: | |
748 | if ((flags & (TH_FIN|TH_ACK)) == TH_ACK) { | |
749 | state[dir] = TCPS_ESTABLISHED; | |
750 | current_active_connections++; | |
751 | *age = fr_tcpidletimeout; | |
752 | } | |
753 | break; | |
754 | case TCPS_ESTABLISHED: | |
755 | if (flags & TH_FIN) { | |
756 | state[dir] = TCPS_CLOSE_WAIT; | |
757 | if (!(flags & TH_PUSH) && !dlen && | |
758 | ostate > TCPS_ESTABLISHED) | |
759 | *age = fr_tcplastack; | |
760 | else | |
761 | *age = fr_tcpclosewait; | |
762 | } else | |
763 | *age = fr_tcpidletimeout; | |
764 | break; | |
765 | case TCPS_CLOSE_WAIT: | |
766 | if ((flags & TH_FIN) && !(flags & TH_PUSH) && !dlen && | |
767 | ostate > TCPS_ESTABLISHED) { | |
768 | *age = fr_tcplastack; | |
769 | state[dir] = TCPS_LAST_ACK; | |
770 | } else | |
771 | *age = fr_tcpclosewait; | |
772 | break; | |
773 | case TCPS_LAST_ACK: | |
774 | if (flags & TH_ACK) { | |
775 | state[dir] = TCPS_FIN_WAIT_2; | |
776 | if (!(flags & TH_PUSH) && !dlen && | |
777 | ostate > TCPS_ESTABLISHED) | |
778 | *age = fr_tcplastack; | |
779 | else { | |
780 | *age = fr_tcpclosewait; | |
781 | state[dir] = TCPS_CLOSE_WAIT; | |
782 | } | |
783 | } | |
784 | break; | |
785 | } | |
786 | } | |
787 | ||
788 | ||
789 | #if IPFILTER_LOG | |
790 | void ipstate_log(is, type) | |
791 | struct ipstate *is; | |
792 | u_short type; | |
793 | { | |
794 | struct ipslog ipsl; | |
795 | void *items[1]; | |
796 | size_t sizes[1]; | |
797 | int types[1]; | |
798 | ||
799 | ipsl.isl_pkts = is->is_pkts; | |
800 | ipsl.isl_bytes = is->is_bytes; | |
801 | ipsl.isl_src = is->is_src; | |
802 | ipsl.isl_dst = is->is_dst; | |
803 | ipsl.isl_p = is->is_p; | |
804 | ipsl.isl_flags = is->is_flags; | |
805 | ipsl.isl_type = type; | |
806 | if (ipsl.isl_p == IPPROTO_TCP || ipsl.isl_p == IPPROTO_UDP) { | |
807 | ipsl.isl_sport = is->is_sport; | |
808 | ipsl.isl_dport = is->is_dport; | |
809 | } else if (ipsl.isl_p == IPPROTO_ICMP) | |
810 | ipsl.isl_itype = is->is_icmp.ics_type; | |
811 | else { | |
812 | ipsl.isl_ps.isl_filler[0] = 0; | |
813 | ipsl.isl_ps.isl_filler[1] = 0; | |
814 | } | |
815 | items[0] = &ipsl; | |
816 | sizes[0] = sizeof(ipsl); | |
817 | types[0] = 0; | |
818 | ||
819 | (void) ipllog(IPL_LOGSTATE, 0, items, sizes, types, 1); | |
820 | } | |
821 | #endif |