]>
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 | * Added redirect stuff and a LOT of bug fixes. (mcn@EnGarde.com) | |
30 | */ | |
31 | #if !defined(lint) | |
32 | /* static const char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed"; */ | |
33 | #endif | |
34 | ||
35 | #include "opt_ipfilter.h" | |
36 | #define __FreeBSD_version 300000 /* it's a hack, but close enough */ | |
37 | ||
38 | #if defined(__FreeBSD__) && defined(KERNEL) && !defined(_KERNEL) | |
39 | #define _KERNEL | |
40 | #endif | |
41 | ||
42 | #if !defined(_KERNEL) && !defined(KERNEL) | |
43 | # include <stdio.h> | |
44 | # include <string.h> | |
45 | # include <stdlib.h> | |
46 | #endif | |
47 | #include <sys/errno.h> | |
48 | #include <sys/types.h> | |
49 | #include <sys/param.h> | |
50 | #include <sys/time.h> | |
51 | #include <sys/file.h> | |
52 | #if defined(KERNEL) && (__FreeBSD_version >= 220000) | |
53 | # include <sys/filio.h> | |
54 | # include <sys/fcntl.h> | |
55 | #else | |
56 | # include <sys/ioctl.h> | |
57 | #endif | |
58 | #include <sys/fcntl.h> | |
59 | #include <sys/uio.h> | |
60 | #ifndef linux | |
61 | # include <sys/protosw.h> | |
62 | #endif | |
63 | #include <sys/socket.h> | |
64 | #if defined(_KERNEL) && !defined(linux) | |
65 | # include <sys/systm.h> | |
66 | #endif | |
67 | #if !defined(__SVR4) && !defined(__svr4__) | |
68 | # ifndef linux | |
69 | # include <sys/mbuf.h> | |
70 | # endif | |
71 | #else | |
72 | # include <sys/filio.h> | |
73 | # include <sys/byteorder.h> | |
74 | # include <sys/dditypes.h> | |
75 | # include <sys/stream.h> | |
76 | # include <sys/kmem.h> | |
77 | #endif | |
78 | #if __FreeBSD_version >= 300000 | |
79 | # include <sys/queue.h> | |
80 | # include <sys/malloc.h> | |
81 | #endif | |
82 | #include <net/if.h> | |
83 | #if __FreeBSD_version >= 300000 | |
84 | # include <net/if_var.h> | |
85 | #endif | |
86 | #ifdef sun | |
87 | #include <net/af.h> | |
88 | #endif | |
89 | #include <net/route.h> | |
90 | #include <netinet/in.h> | |
91 | #include <netinet/in_systm.h> | |
92 | #include <netinet/ip.h> | |
93 | ||
94 | #ifdef __sgi | |
95 | # ifdef IFF_DRVRLOCK /* IRIX6 */ | |
96 | #include <sys/hashing.h> | |
97 | #include <netinet/in_var.h> | |
98 | # endif | |
99 | #endif | |
100 | ||
101 | #if RFC1825 | |
102 | #include <vpn/md5.h> | |
103 | #include <vpn/ipsec.h> | |
104 | extern struct ifnet vpnif; | |
105 | #endif | |
106 | ||
107 | #ifndef linux | |
108 | # include <netinet/ip_var.h> | |
109 | #endif | |
110 | #include <netinet/tcp.h> | |
111 | #include <netinet/udp.h> | |
112 | #include <netinet/ip_icmp.h> | |
113 | #include "netinet/ip_compat.h" | |
114 | #include <netinet/tcpip.h> | |
115 | #include "netinet/ip_fil.h" | |
116 | #include "netinet/ip_proxy.h" | |
117 | #include "netinet/ip_nat.h" | |
118 | #include "netinet/ip_frag.h" | |
119 | #include "netinet/ip_state.h" | |
120 | #ifndef MIN | |
121 | #define MIN(a,b) (((a)<(b))?(a):(b)) | |
122 | #endif | |
123 | #undef SOCKADDR_IN | |
124 | #define SOCKADDR_IN struct sockaddr_in | |
125 | ||
126 | nat_t *nat_table[2][NAT_SIZE], *nat_instances = NULL; | |
127 | static ipnat_t *nat_list = NULL; | |
128 | u_long fr_defnatage = 1200, /* 10 minutes (600 seconds) */ | |
129 | fr_defnaticmpage = 6; /* 3 seconds */ | |
130 | static natstat_t nat_stats; | |
131 | #if (SOLARIS || defined(__sgi)) && defined(_KERNEL) | |
132 | extern kmutex_t ipf_nat; | |
133 | #endif | |
134 | ||
135 | static int nat_flushtable __P((void)); | |
136 | static int nat_clearlist __P((void)); | |
137 | static void nat_delete __P((struct nat *)); | |
138 | static int nat_ifpaddr __P((nat_t *, void *, struct in_addr *)); | |
139 | ||
140 | ||
141 | #define LONG_SUM(in) (((in) & 0xffff) + ((in) >> 16)) | |
142 | ||
143 | #define CALC_SUMD(s1, s2, sd) { \ | |
144 | /* Do it twice */ \ | |
145 | (s1) = ((s1) & 0xffff) + ((s1) >> 16); \ | |
146 | (s1) = ((s1) & 0xffff) + ((s1) >> 16); \ | |
147 | /* Do it twice */ \ | |
148 | (s2) = ((s2) & 0xffff) + ((s2) >> 16); \ | |
149 | (s2) = ((s2) & 0xffff) + ((s2) >> 16); \ | |
150 | /* Because ~1 == -2, We really need ~1 == -1 */ \ | |
151 | if ((s1) > (s2)) (s2)--; \ | |
152 | (sd) = (s2) - (s1); \ | |
153 | (sd) = ((sd) & 0xffff) + ((sd) >> 16); } | |
154 | ||
155 | void fix_outcksum(sp, n) | |
156 | u_short *sp; | |
157 | u_32_t n; | |
158 | { | |
159 | register u_short sumshort; | |
160 | register u_32_t sum1; | |
161 | ||
162 | if (!n) | |
163 | return; | |
164 | sum1 = (~ntohs(*sp)) & 0xffff; | |
165 | sum1 += (n); | |
166 | sum1 = (sum1 >> 16) + (sum1 & 0xffff); | |
167 | /* Again */ | |
168 | sum1 = (sum1 >> 16) + (sum1 & 0xffff); | |
169 | sumshort = ~(u_short)sum1; | |
170 | *(sp) = htons(sumshort); | |
171 | } | |
172 | ||
173 | ||
174 | void fix_incksum(sp, n) | |
175 | u_short *sp; | |
176 | u_32_t n; | |
177 | { | |
178 | register u_short sumshort; | |
179 | register u_32_t sum1; | |
180 | ||
181 | if (!n) | |
182 | return; | |
183 | #if sparc | |
184 | sum1 = (~(*sp)) & 0xffff; | |
185 | #else | |
186 | sum1 = (~ntohs(*sp)) & 0xffff; | |
187 | #endif | |
188 | sum1 += ~(n) & 0xffff; | |
189 | sum1 = (sum1 >> 16) + (sum1 & 0xffff); | |
190 | /* Again */ | |
191 | sum1 = (sum1 >> 16) + (sum1 & 0xffff); | |
192 | sumshort = ~(u_short)sum1; | |
193 | *(sp) = htons(sumshort); | |
194 | } | |
195 | ||
196 | ||
197 | /* | |
198 | * How the NAT is organised and works. | |
199 | * | |
200 | * Inside (interface y) NAT Outside (interface x) | |
201 | * -------------------- -+- ------------------------------------- | |
202 | * Packet going | out, processsed by ip_natout() for x | |
203 | * ------------> | ------------> | |
204 | * src=10.1.1.1 | src=192.1.1.1 | |
205 | * | | |
206 | * | in, processed by ip_natin() for x | |
207 | * <------------ | <------------ | |
208 | * dst=10.1.1.1 | dst=192.1.1.1 | |
209 | * -------------------- -+- ------------------------------------- | |
210 | * ip_natout() - changes ip_src and if required, sport | |
211 | * - creates a new mapping, if required. | |
212 | * ip_natin() - changes ip_dst and if required, dport | |
213 | * | |
214 | * In the NAT table, internal source is recorded as "in" and externally | |
215 | * seen as "out". | |
216 | */ | |
217 | ||
218 | /* | |
219 | * Handle ioctls which manipulate the NAT. | |
220 | */ | |
221 | int nat_ioctl(data, cmd, mode) | |
222 | #if defined(__NetBSD__) || defined(__OpenBSD__) || (__FreeBSD_version >= 300003) | |
223 | u_long cmd; | |
224 | #else | |
225 | int cmd; | |
226 | #endif | |
227 | caddr_t data; | |
228 | int mode; | |
229 | { | |
230 | register ipnat_t *nat, *n = NULL, **np = NULL; | |
231 | ipnat_t natd; | |
232 | int error = 0, ret; | |
233 | #if defined(_KERNEL) && !SOLARIS | |
234 | int s; | |
235 | #endif | |
236 | ||
237 | nat = NULL; /* XXX gcc -Wuninitialized */ | |
238 | ||
239 | /* | |
240 | * For add/delete, look to see if the NAT entry is already present | |
241 | */ | |
242 | SPL_NET(s); | |
243 | MUTEX_ENTER(&ipf_nat); | |
244 | if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) { | |
245 | IRCOPY(data, (char *)&natd, sizeof(natd)); | |
246 | nat = &natd; | |
247 | nat->in_inip &= nat->in_inmsk; | |
248 | nat->in_outip &= nat->in_outmsk; | |
249 | for (np = &nat_list; (n = *np); np = &n->in_next) | |
250 | if (!bcmp((char *)&nat->in_flags, (char *)&n->in_flags, | |
251 | IPN_CMPSIZ)) | |
252 | break; | |
253 | } | |
254 | ||
255 | switch (cmd) | |
256 | { | |
257 | case SIOCADNAT : | |
258 | if (!(mode & FWRITE)) { | |
259 | error = EPERM; | |
260 | break; | |
261 | } | |
262 | if (n) { | |
263 | error = EEXIST; | |
264 | break; | |
265 | } | |
266 | KMALLOC(n, ipnat_t *, sizeof(*n)); | |
267 | if (n == NULL) { | |
268 | error = ENOMEM; | |
269 | break; | |
270 | } | |
271 | bcopy((char *)nat, (char *)n, sizeof(*n)); | |
272 | n->in_ifp = (void *)GETUNIT(n->in_ifname); | |
273 | if (!n->in_ifp) | |
274 | n->in_ifp = (void *)-1; | |
275 | n->in_apr = ap_match(n->in_p, n->in_plabel); | |
276 | n->in_next = *np; | |
277 | n->in_use = 0; | |
278 | n->in_space = ~(0xffffffff & ntohl(n->in_outmsk)); | |
279 | if (n->in_space) /* lose 2: broadcast + network address */ | |
280 | n->in_space -= 2; | |
281 | else | |
282 | n->in_space = 1; /* single IP# mapping */ | |
283 | if ((n->in_outmsk != 0xffffffff) && n->in_outmsk) | |
284 | n->in_nip = ntohl(n->in_outip) + 1; | |
285 | else | |
286 | n->in_nip = ntohl(n->in_outip); | |
287 | if (n->in_redir & NAT_MAP) { | |
288 | n->in_pnext = ntohs(n->in_pmin); | |
289 | /* | |
290 | * Multiply by the number of ports made available. | |
291 | */ | |
292 | if (ntohs(n->in_pmax) > ntohs(n->in_pmin)) | |
293 | n->in_space *= (ntohs(n->in_pmax) - | |
294 | ntohs(n->in_pmin)); | |
295 | } | |
296 | /* Otherwise, these fields are preset */ | |
297 | *np = n; | |
298 | nat_stats.ns_rules++; | |
299 | break; | |
300 | case SIOCRMNAT : | |
301 | if (!(mode & FWRITE)) { | |
302 | error = EPERM; | |
303 | break; | |
304 | } | |
305 | if (!n) { | |
306 | error = ESRCH; | |
307 | break; | |
308 | } | |
309 | *np = n->in_next; | |
310 | if (!n->in_use) { | |
311 | if (n->in_apr) | |
312 | ap_free(n->in_apr); | |
313 | KFREE(n); | |
314 | nat_stats.ns_rules--; | |
315 | } else { | |
316 | n->in_flags |= IPN_DELETE; | |
317 | n->in_next = NULL; | |
318 | } | |
319 | break; | |
320 | case SIOCGNATS : | |
321 | nat_stats.ns_table[0] = nat_table[0]; | |
322 | nat_stats.ns_table[1] = nat_table[1]; | |
323 | nat_stats.ns_list = nat_list; | |
324 | IWCOPY((char *)&nat_stats, (char *)data, sizeof(nat_stats)); | |
325 | break; | |
326 | case SIOCGNATL : | |
327 | { | |
328 | natlookup_t nl; | |
329 | ||
330 | IRCOPY((char *)data, (char *)&nl, sizeof(nl)); | |
331 | ||
332 | if (nat_lookupredir(&nl)) { | |
333 | IWCOPY((char *)&nl, (char *)data, sizeof(nl)); | |
334 | } else | |
335 | error = ESRCH; | |
336 | break; | |
337 | } | |
338 | case SIOCFLNAT : | |
339 | if (!(mode & FWRITE)) { | |
340 | error = EPERM; | |
341 | break; | |
342 | } | |
343 | ret = nat_flushtable(); | |
344 | (void) ap_unload(); | |
345 | IWCOPY((caddr_t)&ret, data, sizeof(ret)); | |
346 | break; | |
347 | case SIOCCNATL : | |
348 | if (!(mode & FWRITE)) { | |
349 | error = EPERM; | |
350 | break; | |
351 | } | |
352 | ret = nat_clearlist(); | |
353 | IWCOPY((caddr_t)&ret, data, sizeof(ret)); | |
354 | break; | |
355 | case FIONREAD : | |
356 | #if IPFILTER_LOG | |
357 | IWCOPY((caddr_t)&iplused[IPL_LOGNAT], (caddr_t)data, | |
358 | sizeof(iplused[IPL_LOGNAT])); | |
359 | #endif | |
360 | break; | |
361 | } | |
362 | MUTEX_EXIT(&ipf_nat); | |
363 | SPL_X(s); | |
364 | return error; | |
365 | } | |
366 | ||
367 | ||
368 | /* | |
369 | * Delete a nat entry from the various lists and table. | |
370 | */ | |
371 | static void nat_delete(natd) | |
372 | struct nat *natd; | |
373 | { | |
374 | register struct nat **natp, *nat; | |
375 | struct ipnat *ipn; | |
376 | ||
377 | for (natp = natd->nat_hstart[0]; (nat = *natp); | |
378 | natp = &nat->nat_hnext[0]) | |
379 | if (nat == natd) { | |
380 | *natp = nat->nat_hnext[0]; | |
381 | break; | |
382 | } | |
383 | ||
384 | for (natp = natd->nat_hstart[1]; (nat = *natp); | |
385 | natp = &nat->nat_hnext[1]) | |
386 | if (nat == natd) { | |
387 | *natp = nat->nat_hnext[1]; | |
388 | break; | |
389 | } | |
390 | ||
391 | /* | |
392 | * If there is an active reference from the nat entry to its parent | |
393 | * rule, decrement the rule's reference count and free it too if no | |
394 | * longer being used. | |
395 | */ | |
396 | if ((ipn = natd->nat_ptr)) { | |
397 | ipn->in_space++; | |
398 | ipn->in_use--; | |
399 | if (!ipn->in_use && (ipn->in_flags & IPN_DELETE)) { | |
400 | if (ipn->in_apr) | |
401 | ap_free(ipn->in_apr); | |
402 | KFREE(ipn); | |
403 | nat_stats.ns_rules--; | |
404 | } | |
405 | } | |
406 | ||
407 | /* | |
408 | * If there's a fragment table entry too for this nat entry, then | |
409 | * dereference that as well. | |
410 | */ | |
411 | ipfr_forget((void *)natd); | |
412 | KFREE(natd); | |
413 | } | |
414 | ||
415 | ||
416 | /* | |
417 | * nat_flushtable - clear the NAT table of all mapping entries. | |
418 | */ | |
419 | static int nat_flushtable() | |
420 | { | |
421 | register nat_t *nat, **natp; | |
422 | register int j = 0; | |
423 | ||
424 | /* | |
425 | * Everything will be deleted, so lets just make it the deletions | |
426 | * quicker. | |
427 | */ | |
428 | bzero((char *)nat_table[0], sizeof(nat_table[0])); | |
429 | bzero((char *)nat_table[1], sizeof(nat_table[1])); | |
430 | ||
431 | for (natp = &nat_instances; (nat = *natp); ) { | |
432 | *natp = nat->nat_next; | |
433 | nat_delete(nat); | |
434 | j++; | |
435 | } | |
436 | ||
437 | return j; | |
438 | } | |
439 | ||
440 | ||
441 | /* | |
442 | * nat_clearlist - delete all entries in the active NAT mapping list. | |
443 | */ | |
444 | static int nat_clearlist() | |
445 | { | |
446 | register ipnat_t *n, **np = &nat_list; | |
447 | int i = 0; | |
448 | ||
449 | while ((n = *np)) { | |
450 | *np = n->in_next; | |
451 | if (!n->in_use) { | |
452 | if (n->in_apr) | |
453 | ap_free(n->in_apr); | |
454 | KFREE(n); | |
455 | nat_stats.ns_rules--; | |
456 | i++; | |
457 | } else { | |
458 | n->in_flags |= IPN_DELETE; | |
459 | n->in_next = NULL; | |
460 | } | |
461 | } | |
462 | nat_stats.ns_inuse = 0; | |
463 | return i; | |
464 | } | |
465 | ||
466 | ||
467 | /* | |
468 | * return the first IP Address associated with an interface | |
469 | */ | |
470 | static int nat_ifpaddr(nat, ifptr, inp) | |
471 | nat_t *nat; | |
472 | void *ifptr; | |
473 | struct in_addr *inp; | |
474 | { | |
475 | #if SOLARIS | |
476 | ill_t *ill = ifptr; | |
477 | #else | |
478 | struct ifnet *ifp = ifptr; | |
479 | #endif | |
480 | struct in_addr in; | |
481 | ||
482 | #if SOLARIS | |
483 | in.s_addr = ntohl(ill->ill_ipif->ipif_local_addr); | |
484 | #else /* SOLARIS */ | |
485 | # if linux | |
486 | ; | |
487 | # else /* linux */ | |
488 | struct ifaddr *ifa; | |
489 | struct sockaddr_in *sin; | |
490 | ||
491 | # if (__FreeBSD_version >= 300000) | |
492 | ifa = TAILQ_FIRST(&ifp->if_addrhead); | |
493 | # else | |
494 | # if defined(__NetBSD__) || defined(__OpenBSD__) | |
495 | ifa = ifp->if_addrlist.tqh_first; | |
496 | # else | |
497 | # if defined(__sgi) && defined(IFF_DRVRLOCK) /* IRIX 6 */ | |
498 | ifa = &((struct in_ifaddr *)ifp->in_ifaddr)->ia_ifa; | |
499 | # else | |
500 | ifa = ifp->if_addrlist; | |
501 | # endif | |
502 | # endif /* __NetBSD__ || __OpenBSD__ */ | |
503 | # endif /* __FreeBSD_version >= 300000 */ | |
504 | # if (BSD < 199306) && !(/*IRIX6*/defined(__sgi) && defined(IFF_DRVRLOCK)) | |
505 | sin = (SOCKADDR_IN *)&ifa->ifa_addr; | |
506 | # else | |
507 | sin = (SOCKADDR_IN *)ifa->ifa_addr; | |
508 | while (sin && ifa && | |
509 | sin->sin_family != AF_INET) { | |
510 | # if (__FreeBSD_version >= 300000) | |
511 | ifa = TAILQ_NEXT(ifa, ifa_link); | |
512 | # else | |
513 | # if defined(__NetBSD__) || defined(__OpenBSD__) | |
514 | ifa = ifa->ifa_list.tqe_next; | |
515 | # else | |
516 | ifa = ifa->ifa_next; | |
517 | # endif | |
518 | # endif /* __FreeBSD_version >= 300000 */ | |
519 | if (ifa) | |
520 | sin = (SOCKADDR_IN *)ifa->ifa_addr; | |
521 | } | |
522 | if (!ifa) | |
523 | sin = NULL; | |
524 | if (!sin) { | |
525 | KFREE(nat); | |
526 | return -1; | |
527 | } | |
528 | # endif /* (BSD < 199306) && (!__sgi && IFF_DRVLOCK) */ | |
529 | in = sin->sin_addr; | |
530 | in.s_addr = ntohl(in.s_addr); | |
531 | # endif /* linux */ | |
532 | #endif /* SOLARIS */ | |
533 | *inp = in; | |
534 | return 0; | |
535 | } | |
536 | ||
537 | ||
538 | /* | |
539 | * Create a new NAT table entry. | |
540 | */ | |
541 | nat_t *nat_new(np, ip, fin, flags, direction) | |
542 | ipnat_t *np; | |
543 | ip_t *ip; | |
544 | fr_info_t *fin; | |
545 | u_short flags; | |
546 | int direction; | |
547 | { | |
548 | register u_32_t sum1, sum2, sumd, l; | |
549 | u_short port = 0, sport = 0, dport = 0, nport = 0; | |
550 | struct in_addr in; | |
551 | tcphdr_t *tcp = NULL; | |
552 | nat_t *nat, **natp; | |
553 | u_short nflags; | |
554 | ||
555 | nflags = flags & np->in_flags; | |
556 | if (flags & IPN_TCPUDP) { | |
557 | tcp = (tcphdr_t *)fin->fin_dp; | |
558 | sport = tcp->th_sport; | |
559 | dport = tcp->th_dport; | |
560 | } | |
561 | ||
562 | /* Give me a new nat */ | |
563 | KMALLOC(nat, nat_t *, sizeof(*nat)); | |
564 | if (nat == NULL) | |
565 | return NULL; | |
566 | ||
567 | bzero((char *)nat, sizeof(*nat)); | |
568 | nat->nat_flags = flags; | |
569 | ||
570 | /* | |
571 | * Search the current table for a match. | |
572 | */ | |
573 | if (direction == NAT_OUTBOUND) { | |
574 | /* | |
575 | * If it's an outbound packet which doesn't match any existing | |
576 | * record, then create a new port | |
577 | */ | |
578 | l = 0; | |
579 | do { | |
580 | l++; | |
581 | port = 0; | |
582 | in.s_addr = np->in_nip; | |
583 | if (!in.s_addr && (np->in_outmsk == 0xffffffff)) { | |
584 | if ((l > 1) || | |
585 | nat_ifpaddr(nat, fin->fin_ifp, &in) == -1) { | |
586 | KFREE(nat); | |
587 | return NULL; | |
588 | } | |
589 | } else if (!in.s_addr && !np->in_outmsk) { | |
590 | if (l > 1) { | |
591 | KFREE(nat); | |
592 | return NULL; | |
593 | } | |
594 | in.s_addr = ntohl(ip->ip_src.s_addr); | |
595 | if (nflags & IPN_TCPUDP) | |
596 | port = sport; | |
597 | } else if (nflags & IPN_TCPUDP) { | |
598 | port = htons(np->in_pnext++); | |
599 | if (np->in_pnext >= ntohs(np->in_pmax)) { | |
600 | np->in_pnext = ntohs(np->in_pmin); | |
601 | np->in_space--; | |
602 | if (np->in_outmsk != 0xffffffff) | |
603 | np->in_nip++; | |
604 | } | |
605 | } else if (np->in_outmsk != 0xffffffff) { | |
606 | np->in_space--; | |
607 | np->in_nip++; | |
608 | } | |
609 | ||
610 | if (!port && (flags & IPN_TCPUDP)) | |
611 | port = sport; | |
612 | if ((np->in_nip & ntohl(np->in_outmsk)) > | |
613 | ntohl(np->in_outip)) | |
614 | np->in_nip = ntohl(np->in_outip) + 1; | |
615 | } while (nat_inlookup(fin->fin_ifp, flags, ip->ip_dst, | |
616 | dport, in, port)); | |
617 | ||
618 | /* Setup the NAT table */ | |
619 | nat->nat_inip = ip->ip_src; | |
620 | nat->nat_outip.s_addr = htonl(in.s_addr); | |
621 | nat->nat_oip = ip->ip_dst; | |
622 | ||
623 | sum1 = (ntohl(ip->ip_src.s_addr) & 0xffff) + | |
624 | (ntohl(ip->ip_src.s_addr) >> 16) + ntohs(sport); | |
625 | ||
626 | sum2 = (in.s_addr & 0xffff) + (in.s_addr >> 16) + ntohs(port); | |
627 | ||
628 | if (flags & IPN_TCPUDP) { | |
629 | nat->nat_inport = sport; | |
630 | nat->nat_outport = port; | |
631 | nat->nat_oport = dport; | |
632 | } | |
633 | } else { | |
634 | ||
635 | /* | |
636 | * Otherwise, it's an inbound packet. Most likely, we don't | |
637 | * want to rewrite source ports and source addresses. Instead, | |
638 | * we want to rewrite to a fixed internal address and fixed | |
639 | * internal port. | |
640 | */ | |
641 | in.s_addr = ntohl(np->in_inip); | |
642 | if (!(nport = np->in_pnext)) | |
643 | nport = dport; | |
644 | ||
645 | nat->nat_inip.s_addr = htonl(in.s_addr); | |
646 | nat->nat_outip = ip->ip_dst; | |
647 | nat->nat_oip = ip->ip_src; | |
648 | ||
649 | sum1 = (ntohl(ip->ip_dst.s_addr) & 0xffff) + | |
650 | (ntohl(ip->ip_dst.s_addr) >> 16) + ntohs(dport); | |
651 | ||
652 | sum2 = (in.s_addr & 0xffff) + (in.s_addr >> 16) + ntohs(nport); | |
653 | ||
654 | if (flags & IPN_TCPUDP) { | |
655 | nat->nat_inport = nport; | |
656 | nat->nat_outport = dport; | |
657 | nat->nat_oport = sport; | |
658 | } | |
659 | } | |
660 | ||
661 | /* Do it twice */ | |
662 | sum1 = (sum1 & 0xffff) + (sum1 >> 16); | |
663 | sum1 = (sum1 & 0xffff) + (sum1 >> 16); | |
664 | ||
665 | /* Do it twice */ | |
666 | sum2 = (sum2 & 0xffff) + (sum2 >> 16); | |
667 | sum2 = (sum2 & 0xffff) + (sum2 >> 16); | |
668 | ||
669 | if (sum1 > sum2) | |
670 | sum2--; /* Because ~1 == -2, We really need ~1 == -1 */ | |
671 | sumd = sum2 - sum1; | |
672 | sumd = (sumd & 0xffff) + (sumd >> 16); | |
673 | nat->nat_sumd = (sumd & 0xffff) + (sumd >> 16); | |
674 | ||
675 | if ((flags & IPN_TCPUDP) && ((sport != port) || (dport != nport))) { | |
676 | if (direction == NAT_OUTBOUND) | |
677 | sum1 = (ntohl(ip->ip_src.s_addr) & 0xffff) + | |
678 | (ntohl(ip->ip_src.s_addr) >> 16); | |
679 | else | |
680 | sum1 = (ntohl(ip->ip_dst.s_addr) & 0xffff) + | |
681 | (ntohl(ip->ip_dst.s_addr) >> 16); | |
682 | ||
683 | sum2 = (in.s_addr & 0xffff) + (in.s_addr >> 16); | |
684 | ||
685 | /* Do it twice */ | |
686 | sum1 = (sum1 & 0xffff) + (sum1 >> 16); | |
687 | sum1 = (sum1 & 0xffff) + (sum1 >> 16); | |
688 | ||
689 | /* Do it twice */ | |
690 | sum2 = (sum2 & 0xffff) + (sum2 >> 16); | |
691 | sum2 = (sum2 & 0xffff) + (sum2 >> 16); | |
692 | ||
693 | if (sum1 > sum2) | |
694 | sum2--; /* Because ~1 == -2, We really need ~1 == -1 */ | |
695 | sumd = sum2 - sum1; | |
696 | sumd = (sumd & 0xffff) + (sumd >> 16); | |
697 | nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); | |
698 | } else | |
699 | nat->nat_ipsumd = nat->nat_sumd; | |
700 | ||
701 | in.s_addr = htonl(in.s_addr); | |
702 | nat->nat_next = nat_instances; | |
703 | nat_instances = nat; | |
704 | natp = &nat_table[0][nat->nat_inip.s_addr % NAT_SIZE]; | |
705 | nat->nat_hstart[0] = natp; | |
706 | nat->nat_hnext[0] = *natp; | |
707 | *natp = nat; | |
708 | natp = &nat_table[1][nat->nat_outip.s_addr % NAT_SIZE]; | |
709 | nat->nat_hstart[1] = natp; | |
710 | nat->nat_hnext[1] = *natp; | |
711 | *natp = nat; | |
712 | nat->nat_ptr = np; | |
713 | nat->nat_bytes = 0; | |
714 | nat->nat_pkts = 0; | |
715 | nat->nat_ifp = fin->fin_ifp; | |
716 | nat->nat_dir = direction; | |
717 | if (direction == NAT_OUTBOUND) { | |
718 | if (flags & IPN_TCPUDP) | |
719 | tcp->th_sport = port; | |
720 | } else { | |
721 | if (flags & IPN_TCPUDP) | |
722 | tcp->th_dport = nport; | |
723 | } | |
724 | nat_stats.ns_added++; | |
725 | nat_stats.ns_inuse++; | |
726 | np->in_use++; | |
727 | return nat; | |
728 | } | |
729 | ||
730 | ||
731 | nat_t *nat_icmpinlookup(ip, fin) | |
732 | ip_t *ip; | |
733 | fr_info_t *fin; | |
734 | { | |
735 | icmphdr_t *icmp; | |
736 | tcphdr_t *tcp = NULL; | |
737 | ip_t *oip; | |
738 | int flags = 0, type; | |
739 | ||
740 | icmp = (icmphdr_t *)fin->fin_dp; | |
741 | /* | |
742 | * Does it at least have the return (basic) IP header ? | |
743 | * Only a basic IP header (no options) should be with an ICMP error | |
744 | * header. | |
745 | */ | |
746 | if ((ip->ip_hl != 5) || (ip->ip_len < sizeof(*icmp) + sizeof(ip_t))) | |
747 | return NULL; | |
748 | type = icmp->icmp_type; | |
749 | /* | |
750 | * If it's not an error type, then return. | |
751 | */ | |
752 | if ((type != ICMP_UNREACH) && (type != ICMP_SOURCEQUENCH) && | |
753 | (type != ICMP_REDIRECT) && (type != ICMP_TIMXCEED) && | |
754 | (type != ICMP_PARAMPROB)) | |
755 | return NULL; | |
756 | ||
757 | oip = (ip_t *)((char *)fin->fin_dp + 8); | |
758 | if (oip->ip_p == IPPROTO_TCP) | |
759 | flags = IPN_TCP; | |
760 | else if (oip->ip_p == IPPROTO_UDP) | |
761 | flags = IPN_UDP; | |
762 | if (flags & IPN_TCPUDP) { | |
763 | tcp = (tcphdr_t *)((char *)oip + (oip->ip_hl << 2)); | |
764 | return nat_inlookup(fin->fin_ifp, flags, oip->ip_dst, | |
765 | tcp->th_dport, oip->ip_src, tcp->th_sport); | |
766 | } | |
767 | return nat_inlookup(fin->fin_ifp, 0, oip->ip_src, 0, oip->ip_dst, 0); | |
768 | } | |
769 | ||
770 | ||
771 | /* | |
772 | * This should *ONLY* be used for incoming packets to make sure a NAT'd ICMP | |
773 | * packet gets correctly recognised. | |
774 | */ | |
775 | nat_t *nat_icmpin(ip, fin, nflags) | |
776 | ip_t *ip; | |
777 | fr_info_t *fin; | |
778 | int *nflags; | |
779 | { | |
780 | icmphdr_t *icmp; | |
781 | nat_t *nat; | |
782 | ip_t *oip; | |
783 | int flags = 0; | |
784 | ||
785 | if (!(nat = nat_icmpinlookup(ip, fin))) | |
786 | return NULL; | |
787 | ||
788 | *nflags = IPN_ICMPERR; | |
789 | icmp = (icmphdr_t *)fin->fin_dp; | |
790 | oip = (ip_t *)((char *)icmp + 8); | |
791 | if (oip->ip_p == IPPROTO_TCP) | |
792 | flags = IPN_TCP; | |
793 | else if (oip->ip_p == IPPROTO_UDP) | |
794 | flags = IPN_UDP; | |
795 | /* | |
796 | * Need to adjust ICMP header to include the real IP#'s and | |
797 | * port #'s. Only apply a checksum change relative to the | |
798 | * IP address change is it will be modified again in ip_natout | |
799 | * for both address and port. Two checksum changes are | |
800 | * necessary for the two header address changes. Be careful | |
801 | * to only modify the checksum once for the port # and twice | |
802 | * for the IP#. | |
803 | */ | |
804 | if (flags & IPN_TCPUDP) { | |
805 | tcphdr_t *tcp = (tcphdr_t *)(oip + 1); | |
806 | u_32_t sum1, sum2, sumd; | |
807 | struct in_addr in; | |
808 | ||
809 | if (nat->nat_dir == NAT_OUTBOUND) { | |
810 | sum1 = LONG_SUM(ntohl(oip->ip_src.s_addr)); | |
811 | in = nat->nat_outip; | |
812 | oip->ip_src = in; | |
813 | tcp->th_sport = nat->nat_outport; | |
814 | } else { | |
815 | sum1 = LONG_SUM(ntohl(oip->ip_dst.s_addr)); | |
816 | in = nat->nat_inip; | |
817 | oip->ip_dst = in; | |
818 | tcp->th_dport = nat->nat_inport; | |
819 | } | |
820 | ||
821 | sum2 = LONG_SUM(in.s_addr); | |
822 | ||
823 | CALC_SUMD(sum1, sum2, sumd); | |
824 | sumd = (sumd & 0xffff) + (sumd >> 16); | |
825 | ||
826 | if (nat->nat_dir == NAT_OUTBOUND) { | |
827 | fix_incksum(&oip->ip_sum, sumd); | |
828 | fix_incksum(&icmp->icmp_cksum, sumd); | |
829 | } else { | |
830 | fix_outcksum(&oip->ip_sum, sumd); | |
831 | fix_outcksum(&icmp->icmp_cksum, sumd); | |
832 | } | |
833 | ||
834 | /* | |
835 | * TCP checksum doesn't make it into the 1st eight | |
836 | * bytes but UDP does. | |
837 | */ | |
838 | if (ip->ip_p == IPPROTO_UDP) { | |
839 | udphdr_t *udp = (udphdr_t *)tcp; | |
840 | ||
841 | if (udp->uh_sum) { | |
842 | if (nat->nat_dir == NAT_OUTBOUND) | |
843 | fix_incksum(&udp->uh_sum, | |
844 | nat->nat_sumd); | |
845 | else | |
846 | fix_outcksum(&udp->uh_sum, | |
847 | nat->nat_sumd); | |
848 | } | |
849 | } | |
850 | } else | |
851 | ip->ip_dst = nat->nat_outip; | |
852 | nat->nat_age = fr_defnaticmpage; | |
853 | return nat; | |
854 | } | |
855 | ||
856 | ||
857 | /* | |
858 | * NB: these lookups don't lock access to the list, it assume it has already | |
859 | * been done! | |
860 | */ | |
861 | /* | |
862 | * Lookup a nat entry based on the mapped destination ip address/port and | |
863 | * real source address/port. We use this lookup when receiving a packet, | |
864 | * we're looking for a table entry, based on the destination address. | |
865 | * NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. | |
866 | */ | |
867 | #ifdef __STDC__ | |
868 | nat_t *nat_inlookup(void *ifp, int flags, struct in_addr src, u_short sport, struct in_addr mapdst, u_short mapdport) | |
869 | #else | |
870 | nat_t *nat_inlookup(ifp, flags, src, sport, mapdst, mapdport) | |
871 | void *ifp; | |
872 | register int flags; | |
873 | struct in_addr src , mapdst; | |
874 | u_short sport, mapdport; | |
875 | #endif | |
876 | { | |
877 | register nat_t *nat; | |
878 | ||
879 | flags &= IPN_TCPUDP; | |
880 | ||
881 | nat = nat_table[1][mapdst.s_addr % NAT_SIZE]; | |
882 | for (; nat; nat = nat->nat_hnext[1]) | |
883 | if ((!ifp || ifp == nat->nat_ifp) && | |
884 | nat->nat_oip.s_addr == src.s_addr && | |
885 | nat->nat_outip.s_addr == mapdst.s_addr && | |
886 | flags == nat->nat_flags && (!flags || | |
887 | (nat->nat_oport == sport && | |
888 | nat->nat_outport == mapdport))) | |
889 | return nat; | |
890 | return NULL; | |
891 | } | |
892 | ||
893 | ||
894 | /* | |
895 | * Lookup a nat entry based on the source 'real' ip address/port and | |
896 | * destination address/port. We use this lookup when sending a packet out, | |
897 | * we're looking for a table entry, based on the source address. | |
898 | * NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. | |
899 | */ | |
900 | #ifdef __STDC__ | |
901 | nat_t *nat_outlookup(void *ifp, int flags, struct in_addr src, u_short sport, struct in_addr dst, u_short dport) | |
902 | #else | |
903 | nat_t *nat_outlookup(ifp, flags, src, sport, dst, dport) | |
904 | void *ifp; | |
905 | register int flags; | |
906 | struct in_addr src , dst; | |
907 | u_short sport, dport; | |
908 | #endif | |
909 | { | |
910 | register nat_t *nat; | |
911 | ||
912 | flags &= IPN_TCPUDP; | |
913 | ||
914 | nat = nat_table[0][src.s_addr % NAT_SIZE]; | |
915 | for (; nat; nat = nat->nat_hnext[0]) { | |
916 | if ((!ifp || ifp == nat->nat_ifp) && | |
917 | nat->nat_inip.s_addr == src.s_addr && | |
918 | nat->nat_oip.s_addr == dst.s_addr && | |
919 | flags == nat->nat_flags && (!flags || | |
920 | (nat->nat_inport == sport && nat->nat_oport == dport))) | |
921 | return nat; | |
922 | } | |
923 | return NULL; | |
924 | } | |
925 | ||
926 | ||
927 | /* | |
928 | * Lookup a nat entry based on the mapped source ip address/port and | |
929 | * real destination address/port. We use this lookup when sending a packet | |
930 | * out, we're looking for a table entry, based on the source address. | |
931 | */ | |
932 | #ifdef __STDC__ | |
933 | nat_t *nat_lookupmapip(void *ifp, int flags, struct in_addr mapsrc, u_short mapsport, struct in_addr dst, u_short dport) | |
934 | #else | |
935 | nat_t *nat_lookupmapip(ifp, flags, mapsrc, mapsport, dst, dport) | |
936 | void *ifp; | |
937 | register int flags; | |
938 | struct in_addr mapsrc , dst; | |
939 | u_short mapsport, dport; | |
940 | #endif | |
941 | { | |
942 | register nat_t *nat; | |
943 | ||
944 | flags &= IPN_TCPUDP; | |
945 | ||
946 | nat = nat_table[1][mapsrc.s_addr % NAT_SIZE]; | |
947 | for (; nat; nat = nat->nat_hnext[0]) | |
948 | if ((!ifp || ifp == nat->nat_ifp) && | |
949 | nat->nat_oip.s_addr == dst.s_addr && | |
950 | nat->nat_outip.s_addr == mapsrc.s_addr && | |
951 | flags == nat->nat_flags && (!flags || | |
952 | (nat->nat_outport == mapsport && | |
953 | nat->nat_oport == dport))) | |
954 | return nat; | |
955 | return NULL; | |
956 | } | |
957 | ||
958 | ||
959 | /* | |
960 | * Lookup the NAT tables to search for a matching redirect | |
961 | */ | |
962 | nat_t *nat_lookupredir(np) | |
963 | register natlookup_t *np; | |
964 | { | |
965 | nat_t *nat; | |
966 | ||
967 | /* | |
968 | * If nl_inip is non null, this is a lookup based on the real | |
969 | * ip address. Else, we use the fake. | |
970 | */ | |
971 | if ((nat = nat_outlookup(NULL, np->nl_flags, np->nl_inip, | |
972 | np->nl_inport, np->nl_outip, | |
973 | np->nl_outport))) { | |
974 | np->nl_realip = nat->nat_outip; | |
975 | np->nl_realport = nat->nat_outport; | |
976 | } | |
977 | return nat; | |
978 | } | |
979 | ||
980 | ||
981 | /* | |
982 | * Packets going out on the external interface go through this. | |
983 | * Here, the source address requires alteration, if anything. | |
984 | */ | |
985 | int ip_natout(ip, hlen, fin) | |
986 | ip_t *ip; | |
987 | int hlen; | |
988 | fr_info_t *fin; | |
989 | { | |
990 | register ipnat_t *np; | |
991 | register u_32_t ipa; | |
992 | tcphdr_t *tcp = NULL; | |
993 | u_short nflags = 0, sport = 0, dport = 0, *csump = NULL; | |
994 | struct ifnet *ifp; | |
995 | frentry_t *fr; | |
996 | nat_t *nat; | |
997 | int natadd = 1; | |
998 | ||
999 | if ((fr = fin->fin_fr) && !(fr->fr_flags & FR_DUP) && | |
1000 | fr->fr_tif.fd_ifp && fr->fr_tif.fd_ifp != (void *)-1) | |
1001 | ifp = fr->fr_tif.fd_ifp; | |
1002 | else | |
1003 | ifp = fin->fin_ifp; | |
1004 | ||
1005 | if (!(ip->ip_off & 0x1fff) && !(fin->fin_fi.fi_fl & FI_SHORT)) { | |
1006 | if (ip->ip_p == IPPROTO_TCP) | |
1007 | nflags = IPN_TCP; | |
1008 | else if (ip->ip_p == IPPROTO_UDP) | |
1009 | nflags = IPN_UDP; | |
1010 | if (nflags) { | |
1011 | tcp = (tcphdr_t *)fin->fin_dp; | |
1012 | sport = tcp->th_sport; | |
1013 | dport = tcp->th_dport; | |
1014 | } | |
1015 | } | |
1016 | ||
1017 | ipa = ip->ip_src.s_addr; | |
1018 | ||
1019 | MUTEX_ENTER(&ipf_nat); | |
1020 | if ((ip->ip_off & (IP_OFFMASK|IP_MF)) && | |
1021 | (nat = ipfr_nat_knownfrag(ip, fin))) | |
1022 | natadd = 0; | |
1023 | else if ((nat = nat_outlookup(ifp, nflags, ip->ip_src, sport, | |
1024 | ip->ip_dst, dport))) | |
1025 | ; | |
1026 | else | |
1027 | /* | |
1028 | * If there is no current entry in the nat table for this IP#, | |
1029 | * create one for it (if there is a matching rule). | |
1030 | */ | |
1031 | for (np = nat_list; np; np = np->in_next) | |
1032 | if ((np->in_ifp == ifp) && np->in_space && | |
1033 | (!np->in_flags || (np->in_flags & nflags)) && | |
1034 | ((ipa & np->in_inmsk) == np->in_inip) && | |
1035 | ((np->in_redir & NAT_MAP) || | |
1036 | (np->in_pnext == sport))) { | |
1037 | if (*np->in_plabel && !ap_ok(ip, tcp, np)) | |
1038 | continue; | |
1039 | /* | |
1040 | * If it's a redirection, then we don't want to | |
1041 | * create new outgoing port stuff. | |
1042 | * Redirections are only for incoming | |
1043 | * connections. | |
1044 | */ | |
1045 | if (!(np->in_redir & NAT_MAP)) | |
1046 | continue; | |
1047 | if ((nat = nat_new(np, ip, fin, nflags, | |
1048 | NAT_OUTBOUND))) | |
1049 | #if IPFILTER_LOG | |
1050 | nat_log(nat, (u_short)np->in_redir); | |
1051 | #else | |
1052 | ; | |
1053 | #endif | |
1054 | break; | |
1055 | } | |
1056 | ||
1057 | if (nat) { | |
1058 | if (natadd && fin->fin_fi.fi_fl & FI_FRAG) | |
1059 | ipfr_nat_newfrag(ip, fin, 0, nat); | |
1060 | nat->nat_age = fr_defnatage; | |
1061 | ip->ip_src = nat->nat_outip; | |
1062 | nat->nat_bytes += ip->ip_len; | |
1063 | nat->nat_pkts++; | |
1064 | ||
1065 | /* | |
1066 | * Fix up checksums, not by recalculating them, but | |
1067 | * simply computing adjustments. | |
1068 | */ | |
1069 | #if SOLARIS || defined(__sgi) | |
1070 | if (nat->nat_dir == NAT_OUTBOUND) | |
1071 | fix_outcksum(&ip->ip_sum, nat->nat_ipsumd); | |
1072 | else | |
1073 | fix_incksum(&ip->ip_sum, nat->nat_ipsumd); | |
1074 | #endif | |
1075 | ||
1076 | if (nflags && !(ip->ip_off & 0x1fff) && | |
1077 | !(fin->fin_fi.fi_fl & FI_SHORT)) { | |
1078 | ||
1079 | if (nat->nat_outport) | |
1080 | tcp->th_sport = nat->nat_outport; | |
1081 | ||
1082 | if (ip->ip_p == IPPROTO_TCP) { | |
1083 | csump = &tcp->th_sum; | |
1084 | fr_tcp_age(&nat->nat_age, | |
1085 | nat->nat_state, ip, fin,1); | |
1086 | /* | |
1087 | * Increase this because we may have | |
1088 | * "keep state" following this too and | |
1089 | * packet storms can occur if this is | |
1090 | * removed too quickly. | |
1091 | */ | |
1092 | if (nat->nat_age == fr_tcpclosed) | |
1093 | nat->nat_age = fr_tcplastack; | |
1094 | } else if (ip->ip_p == IPPROTO_UDP) { | |
1095 | udphdr_t *udp = (udphdr_t *)tcp; | |
1096 | ||
1097 | if (udp->uh_sum) | |
1098 | csump = &udp->uh_sum; | |
1099 | } else if (ip->ip_p == IPPROTO_ICMP) { | |
1100 | icmphdr_t *ic = (icmphdr_t *)tcp; | |
1101 | ||
1102 | csump = &ic->icmp_cksum; | |
1103 | } | |
1104 | if (csump) { | |
1105 | if (nat->nat_dir == NAT_OUTBOUND) | |
1106 | fix_outcksum(csump, | |
1107 | nat->nat_sumd); | |
1108 | else | |
1109 | fix_incksum(csump, | |
1110 | nat->nat_sumd); | |
1111 | } | |
1112 | } | |
1113 | (void) ap_check(ip, tcp, fin, nat); | |
1114 | nat_stats.ns_mapped[1]++; | |
1115 | MUTEX_EXIT(&ipf_nat); | |
1116 | return -2; | |
1117 | } | |
1118 | MUTEX_EXIT(&ipf_nat); | |
1119 | return 0; | |
1120 | } | |
1121 | ||
1122 | ||
1123 | /* | |
1124 | * Packets coming in from the external interface go through this. | |
1125 | * Here, the destination address requires alteration, if anything. | |
1126 | */ | |
1127 | int ip_natin(ip, hlen, fin) | |
1128 | ip_t *ip; | |
1129 | int hlen; | |
1130 | fr_info_t *fin; | |
1131 | { | |
1132 | register ipnat_t *np; | |
1133 | register struct in_addr in; | |
1134 | struct ifnet *ifp = fin->fin_ifp; | |
1135 | tcphdr_t *tcp = NULL; | |
1136 | u_short sport = 0, dport = 0, *csump = NULL; | |
1137 | nat_t *nat; | |
1138 | int nflags = 0, natadd = 1; | |
1139 | ||
1140 | if (!(ip->ip_off & 0x1fff) && !(fin->fin_fi.fi_fl & FI_SHORT)) { | |
1141 | if (ip->ip_p == IPPROTO_TCP) | |
1142 | nflags = IPN_TCP; | |
1143 | else if (ip->ip_p == IPPROTO_UDP) | |
1144 | nflags = IPN_UDP; | |
1145 | if (nflags) { | |
1146 | tcp = (tcphdr_t *)((char *)ip + hlen); | |
1147 | dport = tcp->th_dport; | |
1148 | sport = tcp->th_sport; | |
1149 | } | |
1150 | } | |
1151 | ||
1152 | in = ip->ip_dst; | |
1153 | ||
1154 | MUTEX_ENTER(&ipf_nat); | |
1155 | ||
1156 | if ((ip->ip_p == IPPROTO_ICMP) && (nat = nat_icmpin(ip, fin, &nflags))) | |
1157 | ; | |
1158 | else if ((ip->ip_off & IP_OFFMASK) && | |
1159 | (nat = ipfr_nat_knownfrag(ip, fin))) | |
1160 | natadd = 0; | |
1161 | else if ((nat = nat_inlookup(fin->fin_ifp, nflags, ip->ip_src, sport, | |
1162 | ip->ip_dst, dport))) | |
1163 | ; | |
1164 | else | |
1165 | /* | |
1166 | * If there is no current entry in the nat table for this IP#, | |
1167 | * create one for it (if there is a matching rule). | |
1168 | */ | |
1169 | for (np = nat_list; np; np = np->in_next) | |
1170 | if ((np->in_ifp == ifp) && | |
1171 | (!np->in_flags || (nflags & np->in_flags)) && | |
1172 | ((in.s_addr & np->in_outmsk) == np->in_outip) && | |
1173 | (np->in_redir & NAT_REDIRECT) && | |
1174 | (!np->in_pmin || np->in_pmin == dport)) { | |
1175 | if ((nat = nat_new(np, ip, fin, nflags, | |
1176 | NAT_INBOUND))) | |
1177 | #if IPFILTER_LOG | |
1178 | nat_log(nat, (u_short)np->in_redir); | |
1179 | #else | |
1180 | ; | |
1181 | #endif | |
1182 | break; | |
1183 | } | |
1184 | if (nat) { | |
1185 | if (natadd && fin->fin_fi.fi_fl & FI_FRAG) | |
1186 | ipfr_nat_newfrag(ip, fin, 0, nat); | |
1187 | (void) ap_check(ip, tcp, fin, nat); | |
1188 | ||
1189 | if (nflags != IPN_ICMPERR) | |
1190 | nat->nat_age = fr_defnatage; | |
1191 | ||
1192 | ip->ip_dst = nat->nat_inip; | |
1193 | nat->nat_bytes += ip->ip_len; | |
1194 | nat->nat_pkts++; | |
1195 | ||
1196 | /* | |
1197 | * Fix up checksums, not by recalculating them, but | |
1198 | * simply computing adjustments. | |
1199 | */ | |
1200 | #if SOLARIS || defined(__sgi) | |
1201 | if (nat->nat_dir == NAT_OUTBOUND) | |
1202 | fix_incksum(&ip->ip_sum, nat->nat_ipsumd); | |
1203 | else | |
1204 | fix_outcksum(&ip->ip_sum, nat->nat_ipsumd); | |
1205 | #endif | |
1206 | if ((nflags & IPN_TCPUDP) && !(ip->ip_off & 0x1fff) && | |
1207 | !(fin->fin_fi.fi_fl & FI_SHORT)) { | |
1208 | ||
1209 | if (nat->nat_inport) | |
1210 | tcp->th_dport = nat->nat_inport; | |
1211 | ||
1212 | if (ip->ip_p == IPPROTO_TCP) { | |
1213 | csump = &tcp->th_sum; | |
1214 | fr_tcp_age(&nat->nat_age, | |
1215 | nat->nat_state, ip, fin,0); | |
1216 | /* | |
1217 | * Increase this because we may have | |
1218 | * "keep state" following this too and | |
1219 | * packet storms can occur if this is | |
1220 | * removed too quickly. | |
1221 | */ | |
1222 | if (nat->nat_age == fr_tcpclosed) | |
1223 | nat->nat_age = fr_tcplastack; | |
1224 | } else if (ip->ip_p == IPPROTO_UDP) { | |
1225 | udphdr_t *udp = (udphdr_t *)tcp; | |
1226 | ||
1227 | if (udp->uh_sum) | |
1228 | csump = &udp->uh_sum; | |
1229 | } else if (ip->ip_p == IPPROTO_ICMP) { | |
1230 | icmphdr_t *ic = (icmphdr_t *)tcp; | |
1231 | ||
1232 | csump = &ic->icmp_cksum; | |
1233 | } | |
1234 | if (csump) { | |
1235 | if (nat->nat_dir == NAT_OUTBOUND) | |
1236 | fix_incksum(csump, | |
1237 | nat->nat_sumd); | |
1238 | else | |
1239 | fix_outcksum(csump, | |
1240 | nat->nat_sumd); | |
1241 | } | |
1242 | } | |
1243 | nat_stats.ns_mapped[0]++; | |
1244 | MUTEX_EXIT(&ipf_nat); | |
1245 | return -2; | |
1246 | } | |
1247 | MUTEX_EXIT(&ipf_nat); | |
1248 | return 0; | |
1249 | } | |
1250 | ||
1251 | ||
1252 | /* | |
1253 | * Free all memory used by NAT structures allocated at runtime. | |
1254 | */ | |
1255 | void ip_natunload() | |
1256 | { | |
1257 | MUTEX_ENTER(&ipf_nat); | |
1258 | (void) nat_clearlist(); | |
1259 | (void) nat_flushtable(); | |
1260 | (void) ap_unload(); | |
1261 | MUTEX_EXIT(&ipf_nat); | |
1262 | } | |
1263 | ||
1264 | ||
1265 | /* | |
1266 | * Slowly expire held state for NAT entries. Timeouts are set in | |
1267 | * expectation of this being called twice per second. | |
1268 | */ | |
1269 | void ip_natexpire() | |
1270 | { | |
1271 | register struct nat *nat, **natp; | |
1272 | #if defined(_KERNEL) && !SOLARIS | |
1273 | int s; | |
1274 | #endif | |
1275 | ||
1276 | SPL_NET(s); | |
1277 | MUTEX_ENTER(&ipf_nat); | |
1278 | for (natp = &nat_instances; (nat = *natp); ) { | |
1279 | if (--nat->nat_age) { | |
1280 | natp = &nat->nat_next; | |
1281 | continue; | |
1282 | } | |
1283 | *natp = nat->nat_next; | |
1284 | #if IPFILTER_LOG | |
1285 | nat_log(nat, NL_EXPIRE); | |
1286 | #endif | |
1287 | nat_delete(nat); | |
1288 | nat_stats.ns_expire++; | |
1289 | } | |
1290 | ||
1291 | ap_expire(); | |
1292 | ||
1293 | MUTEX_EXIT(&ipf_nat); | |
1294 | SPL_X(s); | |
1295 | } | |
1296 | ||
1297 | ||
1298 | /* | |
1299 | */ | |
1300 | #ifdef __STDC__ | |
1301 | void ip_natsync(void *ifp) | |
1302 | #else | |
1303 | void ip_natsync(ifp) | |
1304 | void *ifp; | |
1305 | #endif | |
1306 | { | |
1307 | register nat_t *nat; | |
1308 | register u_32_t sum1, sum2, sumd; | |
1309 | struct in_addr in; | |
1310 | ipnat_t *np; | |
1311 | #if defined(_KERNEL) && !SOLARIS | |
1312 | int s; | |
1313 | #endif | |
1314 | ||
1315 | SPL_NET(s); | |
1316 | MUTEX_ENTER(&ipf_nat); | |
1317 | for (nat = nat_instances; nat; nat = nat->nat_next) | |
1318 | if ((ifp == nat->nat_ifp) && (np = nat->nat_ptr)) | |
1319 | if ((np->in_outmsk == 0xffffffff) && !np->in_nip) { | |
1320 | /* | |
1321 | * Change the map-to address to be the same | |
1322 | * as the new one. | |
1323 | */ | |
1324 | sum1 = nat->nat_outip.s_addr; | |
1325 | if (nat_ifpaddr(nat, ifp, &in) == -1) | |
1326 | nat->nat_outip.s_addr = htonl(in.s_addr); | |
1327 | sum2 = nat->nat_outip.s_addr; | |
1328 | ||
1329 | /* | |
1330 | * Readjust the checksum adjustment to take | |
1331 | * into account the new IP#. | |
1332 | * | |
1333 | * Do it twice | |
1334 | */ | |
1335 | sum1 = (sum1 & 0xffff) + (sum1 >> 16); | |
1336 | sum1 = (sum1 & 0xffff) + (sum1 >> 16); | |
1337 | ||
1338 | /* Do it twice */ | |
1339 | sum2 = (sum2 & 0xffff) + (sum2 >> 16); | |
1340 | sum2 = (sum2 & 0xffff) + (sum2 >> 16); | |
1341 | ||
1342 | /* Because ~1 == -2, We really need ~1 == -1 */ | |
1343 | if (sum1 > sum2) | |
1344 | sum2--; | |
1345 | sumd = sum2 - sum1; | |
1346 | sumd = (sumd & 0xffff) + (sumd >> 16); | |
1347 | sumd += nat->nat_sumd; | |
1348 | nat->nat_sumd = (sumd & 0xffff) + (sumd >> 16); | |
1349 | } | |
1350 | MUTEX_EXIT(&ipf_nat); | |
1351 | SPL_X(s); | |
1352 | } | |
1353 | ||
1354 | ||
1355 | #if IPFILTER_LOG | |
1356 | # ifdef __STDC__ | |
1357 | void nat_log(struct nat *nat, u_short type) | |
1358 | # else | |
1359 | void nat_log(nat, type) | |
1360 | struct nat *nat; | |
1361 | u_short type; | |
1362 | # endif | |
1363 | { | |
1364 | struct ipnat *np; | |
1365 | struct natlog natl; | |
1366 | void *items[1]; | |
1367 | size_t sizes[1]; | |
1368 | int rulen, types[1]; | |
1369 | ||
1370 | natl.nl_inip = nat->nat_inip; | |
1371 | natl.nl_outip = nat->nat_outip; | |
1372 | natl.nl_origip = nat->nat_oip; | |
1373 | natl.nl_bytes = nat->nat_bytes; | |
1374 | natl.nl_pkts = nat->nat_pkts; | |
1375 | natl.nl_origport = nat->nat_oport; | |
1376 | natl.nl_inport = nat->nat_inport; | |
1377 | natl.nl_outport = nat->nat_outport; | |
1378 | natl.nl_type = type; | |
1379 | natl.nl_rule = -1; | |
1380 | if (nat->nat_ptr) { | |
1381 | for (rulen = 0, np = nat_list; np; np = np->in_next, rulen++) | |
1382 | if (np == nat->nat_ptr) { | |
1383 | natl.nl_rule = rulen; | |
1384 | break; | |
1385 | } | |
1386 | } | |
1387 | items[0] = &natl; | |
1388 | sizes[0] = sizeof(natl); | |
1389 | types[0] = 0; | |
1390 | ||
1391 | (void) ipllog(IPL_LOGNAT, 0, items, sizes, types, 1); | |
1392 | } | |
1393 | #endif |