1 | /* $NetBSD: ip_nat.c,v 1.17 2016/10/04 14:36:46 sborrill Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (C) 2012 by Darren Reed. |
5 | * |
6 | * See the IPFILTER.LICENCE file for details on licencing. |
7 | */ |
8 | #if defined(KERNEL) || defined(_KERNEL) |
9 | # undef KERNEL |
10 | # undef KERNEL |
11 | # define KERNEL 1 |
12 | # define KERNEL 1 |
13 | #endif |
14 | #include <sys/errno.h> |
15 | #include <sys/types.h> |
16 | #include <sys/param.h> |
17 | #include <sys/time.h> |
18 | #include <sys/file.h> |
19 | #if defined(_KERNEL) && \ |
20 | (defined(__NetBSD_Version) && (__NetBSD_Version >= 399002000)) |
21 | # include <sys/kauth.h> |
22 | #endif |
23 | #if !defined(_KERNEL) |
24 | # include <stdio.h> |
25 | # include <string.h> |
26 | # include <stdlib.h> |
27 | # define KERNEL |
28 | # ifdef _OpenBSD__ |
29 | struct file; |
30 | # endif |
31 | # include <sys/uio.h> |
32 | # undef KERNEL |
33 | #endif |
34 | #if defined(_KERNEL) && \ |
35 | defined(__FreeBSD_version) && (__FreeBSD_version >= 220000) |
36 | # include <sys/filio.h> |
37 | # include <sys/fcntl.h> |
38 | #else |
39 | # include <sys/ioctl.h> |
40 | #endif |
41 | #if !defined(AIX) |
42 | # include <sys/fcntl.h> |
43 | #endif |
44 | #if !defined(linux) |
45 | # include <sys/protosw.h> |
46 | #endif |
47 | #include <sys/socket.h> |
48 | #if defined(_KERNEL) |
49 | # include <sys/systm.h> |
50 | # if !defined(__SVR4) && !defined(__svr4__) |
51 | # include <sys/mbuf.h> |
52 | # endif |
53 | #endif |
54 | #if defined(__SVR4) || defined(__svr4__) |
55 | # include <sys/filio.h> |
56 | # include <sys/byteorder.h> |
57 | # ifdef KERNEL |
58 | # include <sys/dditypes.h> |
59 | # endif |
60 | # include <sys/stream.h> |
61 | # include <sys/kmem.h> |
62 | #endif |
63 | #if _FreeBSD_version >= 300000 |
64 | # include <sys/queue.h> |
65 | #endif |
66 | #include <net/if.h> |
67 | #if _FreeBSD_version >= 300000 |
68 | # include <net/if_var.h> |
69 | #endif |
70 | #ifdef sun |
71 | # include <net/af.h> |
72 | #endif |
73 | #include <netinet/in.h> |
74 | #include <netinet/in_systm.h> |
75 | #include <netinet/ip.h> |
76 | |
77 | #ifdef RFC1825 |
78 | # include <vpn/md5.h> |
79 | # include <vpn/ipsec.h> |
80 | extern struct ifnet vpnif; |
81 | #endif |
82 | |
83 | #if !defined(linux) |
84 | # include <netinet/ip_var.h> |
85 | #endif |
86 | #include <netinet/tcp.h> |
87 | #include <netinet/udp.h> |
88 | #include <netinet/ip_icmp.h> |
89 | #include "netinet/ip_compat.h" |
90 | #include <netinet/tcpip.h> |
91 | #include "netinet/ipl.h" |
92 | #include "netinet/ip_fil.h" |
93 | #include "netinet/ip_nat.h" |
94 | #include "netinet/ip_frag.h" |
95 | #include "netinet/ip_state.h" |
96 | #include "netinet/ip_proxy.h" |
97 | #include "netinet/ip_lookup.h" |
98 | #include "netinet/ip_dstlist.h" |
99 | #include "netinet/ip_sync.h" |
100 | #if FREEBSD_GE_REV(300000) |
101 | # include <sys/malloc.h> |
102 | #endif |
103 | #ifdef HAS_SYS_MD5_H |
104 | # include <sys/md5.h> |
105 | #else |
106 | # include "md5.h" |
107 | #endif |
108 | /* END OF INCLUDES */ |
109 | |
110 | #undef SOCKADDR_IN |
111 | #define SOCKADDR_IN struct sockaddr_in |
112 | |
113 | #if !defined(lint) |
114 | #if defined(__NetBSD__) |
115 | #include <sys/cdefs.h> |
116 | __KERNEL_RCSID(0, "$NetBSD: ip_nat.c,v 1.17 2016/10/04 14:36:46 sborrill Exp $" ); |
117 | #else |
118 | static const char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed" ; |
119 | static const char rcsid[] = "@(#)Id: ip_nat.c,v 1.1.1.2 2012/07/22 13:45:27 darrenr Exp" ; |
120 | #endif |
121 | #endif |
122 | |
123 | |
124 | #define NATFSUM(n,v,f) ((v) == 4 ? (n)->f.in4.s_addr : (n)->f.i6[0] + \ |
125 | (n)->f.i6[1] + (n)->f.i6[2] + (n)->f.i6[3]) |
126 | #define NBUMP(x) softn->(x)++ |
127 | #define NBUMPD(x, y) do { \ |
128 | softn->x.y++; \ |
129 | DT(y); \ |
130 | } while (0) |
131 | #define NBUMPSIDE(y,x) softn->ipf_nat_stats.ns_side[y].x++ |
132 | #define NBUMPSIDED(y,x) do { softn->ipf_nat_stats.ns_side[y].x++; \ |
133 | DT(x); } while (0) |
134 | #define NBUMPSIDEX(y,x,z) \ |
135 | do { softn->ipf_nat_stats.ns_side[y].x++; \ |
136 | DT(z); } while (0) |
137 | #define NBUMPSIDEDF(y,x)do { softn->ipf_nat_stats.ns_side[y].x++; \ |
138 | DT1(x, fr_info_t *, fin); } while (0) |
139 | |
140 | frentry_t ipfnatblock; |
141 | |
142 | static ipftuneable_t ipf_nat_tuneables[] = { |
143 | /* nat */ |
144 | { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_lock) }, |
145 | "nat_lock" , 0, 1, |
146 | stsizeof(ipf_nat_softc_t, ipf_nat_lock), |
147 | IPFT_RDONLY, NULL, NULL }, |
148 | { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_sz) }, |
149 | "nat_table_size" , 1, 0x7fffffff, |
150 | stsizeof(ipf_nat_softc_t, ipf_nat_table_sz), |
151 | 0, NULL, ipf_nat_rehash }, |
152 | { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_max) }, |
153 | "nat_table_max" , 1, 0x7fffffff, |
154 | stsizeof(ipf_nat_softc_t, ipf_nat_table_max), |
155 | 0, NULL, NULL }, |
156 | { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maprules_sz) }, |
157 | "nat_rules_size" , 1, 0x7fffffff, |
158 | stsizeof(ipf_nat_softc_t, ipf_nat_maprules_sz), |
159 | 0, NULL, ipf_nat_rehash_rules }, |
160 | { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_rdrrules_sz) }, |
161 | "rdr_rules_size" , 1, 0x7fffffff, |
162 | stsizeof(ipf_nat_softc_t, ipf_nat_rdrrules_sz), |
163 | 0, NULL, ipf_nat_rehash_rules }, |
164 | { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_hostmap_sz) }, |
165 | "hostmap_size" , 1, 0x7fffffff, |
166 | stsizeof(ipf_nat_softc_t, ipf_nat_hostmap_sz), |
167 | 0, NULL, ipf_nat_hostmap_rehash }, |
168 | { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maxbucket) }, |
169 | "nat_maxbucket" ,1, 0x7fffffff, |
170 | stsizeof(ipf_nat_softc_t, ipf_nat_maxbucket), |
171 | 0, NULL, NULL }, |
172 | { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_logging) }, |
173 | "nat_logging" , 0, 1, |
174 | stsizeof(ipf_nat_softc_t, ipf_nat_logging), |
175 | 0, NULL, NULL }, |
176 | { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_doflush) }, |
177 | "nat_doflush" , 0, 1, |
178 | stsizeof(ipf_nat_softc_t, ipf_nat_doflush), |
179 | 0, NULL, NULL }, |
180 | { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_low) }, |
181 | "nat_table_wm_low" , 1, 99, |
182 | stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_low), |
183 | 0, NULL, NULL }, |
184 | { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_high) }, |
185 | "nat_table_wm_high" , 2, 100, |
186 | stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_high), |
187 | 0, NULL, NULL }, |
188 | { { 0 }, |
189 | NULL, 0, 0, |
190 | 0, |
191 | 0, NULL, NULL } |
192 | }; |
193 | |
194 | /* ======================================================================== */ |
195 | /* How the NAT is organised and works. */ |
196 | /* */ |
197 | /* Inside (interface y) NAT Outside (interface x) */ |
198 | /* -------------------- -+- ------------------------------------- */ |
199 | /* Packet going | out, processsed by ipf_nat_checkout() for x */ |
200 | /* ------------> | ------------> */ |
201 | /* src=10.1.1.1 | src=192.1.1.1 */ |
202 | /* | */ |
203 | /* | in, processed by ipf_nat_checkin() for x */ |
204 | /* <------------ | <------------ */ |
205 | /* dst=10.1.1.1 | dst=192.1.1.1 */ |
206 | /* -------------------- -+- ------------------------------------- */ |
207 | /* ipf_nat_checkout() - changes ip_src and if required, sport */ |
208 | /* - creates a new mapping, if required. */ |
209 | /* ipf_nat_checkin() - changes ip_dst and if required, dport */ |
210 | /* */ |
211 | /* In the NAT table, internal source is recorded as "in" and externally */ |
212 | /* seen as "out". */ |
213 | /* ======================================================================== */ |
214 | |
215 | |
216 | #if SOLARIS && !defined(INSTANCES) |
217 | extern int pfil_delayed_copy; |
218 | #endif |
219 | |
220 | static int ipf_nat_flush_entry(ipf_main_softc_t *, void *); |
221 | static int ipf_nat_getent(ipf_main_softc_t *, void *, int); |
222 | static int ipf_nat_getsz(ipf_main_softc_t *, void *, int); |
223 | static int ipf_nat_putent(ipf_main_softc_t *, void *, int); |
224 | static void ipf_nat_addmap(ipf_nat_softc_t *, ipnat_t *); |
225 | static void ipf_nat_addrdr(ipf_nat_softc_t *, ipnat_t *); |
226 | static int ipf_nat_builddivertmp(ipf_nat_softc_t *, ipnat_t *); |
227 | static int ipf_nat_clearlist(ipf_main_softc_t *, ipf_nat_softc_t *); |
228 | static int ipf_nat_cmp_rules(ipnat_t *, ipnat_t *); |
229 | static int ipf_nat_decap(fr_info_t *, nat_t *); |
230 | static void ipf_nat_delrule(ipf_main_softc_t *, ipf_nat_softc_t *, |
231 | ipnat_t *, int); |
232 | static int ipf_nat_extraflush(ipf_main_softc_t *, ipf_nat_softc_t *, int); |
233 | static int ipf_nat_finalise(fr_info_t *, nat_t *); |
234 | static int ipf_nat_flushtable(ipf_main_softc_t *, ipf_nat_softc_t *); |
235 | static int ipf_nat_getnext(ipf_main_softc_t *, ipftoken_t *, |
236 | ipfgeniter_t *, ipfobj_t *); |
237 | static int ipf_nat_gettable(ipf_main_softc_t *, ipf_nat_softc_t *, char *); |
238 | static hostmap_t *ipf_nat_hostmap(ipf_nat_softc_t *, ipnat_t *, |
239 | struct in_addr, struct in_addr, |
240 | struct in_addr, u_32_t); |
241 | static int ipf_nat_icmpquerytype(int); |
242 | static int ipf_nat_iterator(ipf_main_softc_t *, ipftoken_t *, |
243 | ipfgeniter_t *, ipfobj_t *); |
244 | static int ipf_nat_match(fr_info_t *, ipnat_t *); |
245 | static int ipf_nat_matcharray(nat_t *, int *, u_long); |
246 | static int ipf_nat_matchflush(ipf_main_softc_t *, ipf_nat_softc_t *, |
247 | void *); |
248 | static void ipf_nat_mssclamp(tcphdr_t *, u_32_t, fr_info_t *, u_short *); |
249 | static int ipf_nat_newmap(fr_info_t *, nat_t *, natinfo_t *); |
250 | static int ipf_nat_newdivert(fr_info_t *, nat_t *, natinfo_t *); |
251 | static int ipf_nat_newrdr(fr_info_t *, nat_t *, natinfo_t *); |
252 | static int ipf_nat_newrewrite(fr_info_t *, nat_t *, natinfo_t *); |
253 | static int ipf_nat_nextaddr(fr_info_t *, nat_addr_t *, u_32_t *, u_32_t *); |
254 | static int ipf_nat_nextaddrinit(ipf_main_softc_t *, char *, |
255 | nat_addr_t *, int, void *); |
256 | static int ipf_nat_resolverule(ipf_main_softc_t *, ipnat_t *); |
257 | static int ipf_nat_ruleaddrinit(ipf_main_softc_t *, |
258 | ipf_nat_softc_t *, ipnat_t *); |
259 | static void ipf_nat_rule_fini(ipf_main_softc_t *, ipnat_t *); |
260 | static int ipf_nat_rule_init(ipf_main_softc_t *, ipf_nat_softc_t *, |
261 | ipnat_t *); |
262 | static int ipf_nat_siocaddnat(ipf_main_softc_t *, ipf_nat_softc_t *, |
263 | ipnat_t *, int); |
264 | static void ipf_nat_siocdelnat(ipf_main_softc_t *, ipf_nat_softc_t *, |
265 | ipnat_t *, int); |
266 | static void ipf_nat_tabmove(ipf_nat_softc_t *, nat_t *); |
267 | |
268 | /* ------------------------------------------------------------------------ */ |
269 | /* Function: ipf_nat_main_load */ |
270 | /* Returns: int - 0 == success, -1 == failure */ |
271 | /* Parameters: Nil */ |
272 | /* */ |
273 | /* The only global NAT structure that needs to be initialised is the filter */ |
274 | /* rule that is used with blocking packets. */ |
275 | /* ------------------------------------------------------------------------ */ |
276 | int |
277 | ipf_nat_main_load(void) |
278 | { |
279 | bzero((char *)&ipfnatblock, sizeof(ipfnatblock)); |
280 | ipfnatblock.fr_flags = FR_BLOCK|FR_QUICK; |
281 | ipfnatblock.fr_ref = 1; |
282 | |
283 | return 0; |
284 | } |
285 | |
286 | |
287 | /* ------------------------------------------------------------------------ */ |
288 | /* Function: ipf_nat_main_unload */ |
289 | /* Returns: int - 0 == success, -1 == failure */ |
290 | /* Parameters: Nil */ |
291 | /* */ |
292 | /* A null-op function that exists as a placeholder so that the flow in */ |
293 | /* other functions is obvious. */ |
294 | /* ------------------------------------------------------------------------ */ |
295 | int |
296 | ipf_nat_main_unload(void) |
297 | { |
298 | return 0; |
299 | } |
300 | |
301 | |
302 | /* ------------------------------------------------------------------------ */ |
303 | /* Function: ipf_nat_soft_create */ |
304 | /* Returns: void * - NULL = failure, else pointer to NAT context */ |
305 | /* Parameters: softc(I) - pointer to soft context main structure */ |
306 | /* */ |
307 | /* Allocate the initial soft context structure for NAT and populate it with */ |
308 | /* some default values. Creating the tables is left until we call _init so */ |
309 | /* that sizes can be changed before we get under way. */ |
310 | /* ------------------------------------------------------------------------ */ |
311 | void * |
312 | ipf_nat_soft_create(ipf_main_softc_t *softc) |
313 | { |
314 | ipf_nat_softc_t *softn; |
315 | |
316 | KMALLOC(softn, ipf_nat_softc_t *); |
317 | if (softn == NULL) |
318 | return NULL; |
319 | |
320 | bzero((char *)softn, sizeof(*softn)); |
321 | |
322 | softn->ipf_nat_tune = ipf_tune_array_copy(softn, |
323 | sizeof(ipf_nat_tuneables), |
324 | ipf_nat_tuneables); |
325 | if (softn->ipf_nat_tune == NULL) { |
326 | ipf_nat_soft_destroy(softc, softn); |
327 | return NULL; |
328 | } |
329 | if (ipf_tune_array_link(softc, softn->ipf_nat_tune) == -1) { |
330 | ipf_nat_soft_destroy(softc, softn); |
331 | return NULL; |
332 | } |
333 | |
334 | softn->ipf_nat_list_tail = &softn->ipf_nat_list; |
335 | |
336 | softn->ipf_nat_table_max = NAT_TABLE_MAX; |
337 | softn->ipf_nat_table_sz = NAT_TABLE_SZ; |
338 | softn->ipf_nat_maprules_sz = NAT_SIZE; |
339 | softn->ipf_nat_rdrrules_sz = RDR_SIZE; |
340 | softn->ipf_nat_hostmap_sz = HOSTMAP_SIZE; |
341 | softn->ipf_nat_doflush = 0; |
342 | #ifdef IPFILTER_LOG |
343 | softn->ipf_nat_logging = 1; |
344 | #else |
345 | softn->ipf_nat_logging = 0; |
346 | #endif |
347 | |
348 | softn->ipf_nat_defage = DEF_NAT_AGE; |
349 | softn->ipf_nat_defipage = IPF_TTLVAL(60); |
350 | softn->ipf_nat_deficmpage = IPF_TTLVAL(3); |
351 | softn->ipf_nat_table_wm_high = 99; |
352 | softn->ipf_nat_table_wm_low = 90; |
353 | |
354 | return softn; |
355 | } |
356 | |
357 | /* ------------------------------------------------------------------------ */ |
358 | /* Function: ipf_nat_soft_destroy */ |
359 | /* Returns: Nil */ |
360 | /* Parameters: softc(I) - pointer to soft context main structure */ |
361 | /* */ |
362 | /* ------------------------------------------------------------------------ */ |
363 | void |
364 | ipf_nat_soft_destroy(ipf_main_softc_t *softc, void *arg) |
365 | { |
366 | ipf_nat_softc_t *softn = arg; |
367 | |
368 | if (softn->ipf_nat_tune != NULL) { |
369 | ipf_tune_array_unlink(softc, softn->ipf_nat_tune); |
370 | KFREES(softn->ipf_nat_tune, sizeof(ipf_nat_tuneables)); |
371 | softn->ipf_nat_tune = NULL; |
372 | } |
373 | |
374 | KFREE(softn); |
375 | } |
376 | |
377 | |
378 | /* ------------------------------------------------------------------------ */ |
379 | /* Function: ipf_nat_init */ |
380 | /* Returns: int - 0 == success, -1 == failure */ |
381 | /* Parameters: softc(I) - pointer to soft context main structure */ |
382 | /* */ |
383 | /* Initialise all of the NAT locks, tables and other structures. */ |
384 | /* ------------------------------------------------------------------------ */ |
385 | int |
386 | ipf_nat_soft_init(ipf_main_softc_t *softc, void *arg) |
387 | { |
388 | ipf_nat_softc_t *softn = arg; |
389 | ipftq_t *tq; |
390 | int i; |
391 | |
392 | KMALLOCS(softn->ipf_nat_table[0], nat_t **, \ |
393 | sizeof(nat_t *) * softn->ipf_nat_table_sz); |
394 | |
395 | if (softn->ipf_nat_table[0] != NULL) { |
396 | bzero((char *)softn->ipf_nat_table[0], |
397 | softn->ipf_nat_table_sz * sizeof(nat_t *)); |
398 | } else { |
399 | return -1; |
400 | } |
401 | |
402 | KMALLOCS(softn->ipf_nat_table[1], nat_t **, \ |
403 | sizeof(nat_t *) * softn->ipf_nat_table_sz); |
404 | |
405 | if (softn->ipf_nat_table[1] != NULL) { |
406 | bzero((char *)softn->ipf_nat_table[1], |
407 | softn->ipf_nat_table_sz * sizeof(nat_t *)); |
408 | } else { |
409 | return -2; |
410 | } |
411 | |
412 | KMALLOCS(softn->ipf_nat_map_rules, ipnat_t **, \ |
413 | sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz); |
414 | |
415 | if (softn->ipf_nat_map_rules != NULL) { |
416 | bzero((char *)softn->ipf_nat_map_rules, |
417 | softn->ipf_nat_maprules_sz * sizeof(ipnat_t *)); |
418 | } else { |
419 | return -3; |
420 | } |
421 | |
422 | KMALLOCS(softn->ipf_nat_rdr_rules, ipnat_t **, \ |
423 | sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz); |
424 | |
425 | if (softn->ipf_nat_rdr_rules != NULL) { |
426 | bzero((char *)softn->ipf_nat_rdr_rules, |
427 | softn->ipf_nat_rdrrules_sz * sizeof(ipnat_t *)); |
428 | } else { |
429 | return -4; |
430 | } |
431 | |
432 | KMALLOCS(softn->ipf_hm_maptable, hostmap_t **, \ |
433 | sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); |
434 | |
435 | if (softn->ipf_hm_maptable != NULL) { |
436 | bzero((char *)softn->ipf_hm_maptable, |
437 | sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); |
438 | } else { |
439 | return -5; |
440 | } |
441 | softn->ipf_hm_maplist = NULL; |
442 | |
443 | KMALLOCS(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, u_int *, |
444 | softn->ipf_nat_table_sz * sizeof(u_int)); |
445 | |
446 | if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen == NULL) { |
447 | return -6; |
448 | } |
449 | bzero((char *)softn->ipf_nat_stats.ns_side[0].ns_bucketlen, |
450 | softn->ipf_nat_table_sz * sizeof(u_int)); |
451 | |
452 | KMALLOCS(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, u_int *, |
453 | softn->ipf_nat_table_sz * sizeof(u_int)); |
454 | |
455 | if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen == NULL) { |
456 | return -7; |
457 | } |
458 | |
459 | bzero((char *)softn->ipf_nat_stats.ns_side[1].ns_bucketlen, |
460 | softn->ipf_nat_table_sz * sizeof(u_int)); |
461 | |
462 | if (softn->ipf_nat_maxbucket == 0) { |
463 | for (i = softn->ipf_nat_table_sz; i > 0; i >>= 1) |
464 | softn->ipf_nat_maxbucket++; |
465 | softn->ipf_nat_maxbucket *= 2; |
466 | } |
467 | |
468 | ipf_sttab_init(softc, softn->ipf_nat_tcptq); |
469 | /* |
470 | * Increase this because we may have "keep state" following this too |
471 | * and packet storms can occur if this is removed too quickly. |
472 | */ |
473 | softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack; |
474 | softn->ipf_nat_tcptq[IPF_TCP_NSTATES - 1].ifq_next = |
475 | &softn->ipf_nat_udptq; |
476 | |
477 | IPFTQ_INIT(&softn->ipf_nat_udptq, softn->ipf_nat_defage, |
478 | "nat ipftq udp tab" ); |
479 | softn->ipf_nat_udptq.ifq_next = &softn->ipf_nat_udpacktq; |
480 | |
481 | IPFTQ_INIT(&softn->ipf_nat_udpacktq, softn->ipf_nat_defage, |
482 | "nat ipftq udpack tab" ); |
483 | softn->ipf_nat_udpacktq.ifq_next = &softn->ipf_nat_icmptq; |
484 | |
485 | IPFTQ_INIT(&softn->ipf_nat_icmptq, softn->ipf_nat_deficmpage, |
486 | "nat icmp ipftq tab" ); |
487 | softn->ipf_nat_icmptq.ifq_next = &softn->ipf_nat_icmpacktq; |
488 | |
489 | IPFTQ_INIT(&softn->ipf_nat_icmpacktq, softn->ipf_nat_defage, |
490 | "nat icmpack ipftq tab" ); |
491 | softn->ipf_nat_icmpacktq.ifq_next = &softn->ipf_nat_iptq; |
492 | |
493 | IPFTQ_INIT(&softn->ipf_nat_iptq, softn->ipf_nat_defipage, |
494 | "nat ip ipftq tab" ); |
495 | softn->ipf_nat_iptq.ifq_next = &softn->ipf_nat_pending; |
496 | |
497 | IPFTQ_INIT(&softn->ipf_nat_pending, 1, "nat pending ipftq tab" ); |
498 | softn->ipf_nat_pending.ifq_next = NULL; |
499 | |
500 | for (i = 0, tq = softn->ipf_nat_tcptq; i < IPF_TCP_NSTATES; i++, tq++) { |
501 | if (tq->ifq_ttl < softn->ipf_nat_deficmpage) |
502 | tq->ifq_ttl = softn->ipf_nat_deficmpage; |
503 | #ifdef LARGE_NAT |
504 | else if (tq->ifq_ttl > softn->ipf_nat_defage) |
505 | tq->ifq_ttl = softn->ipf_nat_defage; |
506 | #endif |
507 | } |
508 | |
509 | /* |
510 | * Increase this because we may have "keep state" following |
511 | * this too and packet storms can occur if this is removed |
512 | * too quickly. |
513 | */ |
514 | softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack; |
515 | |
516 | MUTEX_INIT(&softn->ipf_nat_new, "ipf nat new mutex" ); |
517 | MUTEX_INIT(&softn->ipf_nat_io, "ipf nat io mutex" ); |
518 | |
519 | softn->ipf_nat_inited = 1; |
520 | |
521 | return 0; |
522 | } |
523 | |
524 | |
525 | /* ------------------------------------------------------------------------ */ |
526 | /* Function: ipf_nat_soft_fini */ |
527 | /* Returns: Nil */ |
528 | /* Parameters: softc(I) - pointer to soft context main structure */ |
529 | /* */ |
530 | /* Free all memory used by NAT structures allocated at runtime. */ |
531 | /* ------------------------------------------------------------------------ */ |
532 | int |
533 | ipf_nat_soft_fini(ipf_main_softc_t *softc, void *arg) |
534 | { |
535 | ipf_nat_softc_t *softn = arg; |
536 | ipftq_t *ifq, *ifqnext; |
537 | |
538 | (void) ipf_nat_clearlist(softc, softn); |
539 | (void) ipf_nat_flushtable(softc, softn); |
540 | |
541 | /* |
542 | * Proxy timeout queues are not cleaned here because although they |
543 | * exist on the NAT list, ipf_proxy_unload is called after unload |
544 | * and the proxies actually are responsible for them being created. |
545 | * Should the proxy timeouts have their own list? There's no real |
546 | * justification as this is the only complication. |
547 | */ |
548 | for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) { |
549 | ifqnext = ifq->ifq_next; |
550 | if (ipf_deletetimeoutqueue(ifq) == 0) |
551 | ipf_freetimeoutqueue(softc, ifq); |
552 | } |
553 | |
554 | if (softn->ipf_nat_table[0] != NULL) { |
555 | KFREES(softn->ipf_nat_table[0], |
556 | sizeof(nat_t *) * softn->ipf_nat_table_sz); |
557 | softn->ipf_nat_table[0] = NULL; |
558 | } |
559 | if (softn->ipf_nat_table[1] != NULL) { |
560 | KFREES(softn->ipf_nat_table[1], |
561 | sizeof(nat_t *) * softn->ipf_nat_table_sz); |
562 | softn->ipf_nat_table[1] = NULL; |
563 | } |
564 | if (softn->ipf_nat_map_rules != NULL) { |
565 | KFREES(softn->ipf_nat_map_rules, |
566 | sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz); |
567 | softn->ipf_nat_map_rules = NULL; |
568 | } |
569 | if (softn->ipf_nat_rdr_rules != NULL) { |
570 | KFREES(softn->ipf_nat_rdr_rules, |
571 | sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz); |
572 | softn->ipf_nat_rdr_rules = NULL; |
573 | } |
574 | if (softn->ipf_hm_maptable != NULL) { |
575 | KFREES(softn->ipf_hm_maptable, |
576 | sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); |
577 | softn->ipf_hm_maptable = NULL; |
578 | } |
579 | if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) { |
580 | KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, |
581 | sizeof(u_int) * softn->ipf_nat_table_sz); |
582 | softn->ipf_nat_stats.ns_side[0].ns_bucketlen = NULL; |
583 | } |
584 | if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) { |
585 | KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, |
586 | sizeof(u_int) * softn->ipf_nat_table_sz); |
587 | softn->ipf_nat_stats.ns_side[1].ns_bucketlen = NULL; |
588 | } |
589 | |
590 | if (softn->ipf_nat_inited == 1) { |
591 | softn->ipf_nat_inited = 0; |
592 | ipf_sttab_destroy(softn->ipf_nat_tcptq); |
593 | |
594 | MUTEX_DESTROY(&softn->ipf_nat_new); |
595 | MUTEX_DESTROY(&softn->ipf_nat_io); |
596 | |
597 | MUTEX_DESTROY(&softn->ipf_nat_udptq.ifq_lock); |
598 | MUTEX_DESTROY(&softn->ipf_nat_udpacktq.ifq_lock); |
599 | MUTEX_DESTROY(&softn->ipf_nat_icmptq.ifq_lock); |
600 | MUTEX_DESTROY(&softn->ipf_nat_icmpacktq.ifq_lock); |
601 | MUTEX_DESTROY(&softn->ipf_nat_iptq.ifq_lock); |
602 | MUTEX_DESTROY(&softn->ipf_nat_pending.ifq_lock); |
603 | } |
604 | |
605 | return 0; |
606 | } |
607 | |
608 | |
609 | /* ------------------------------------------------------------------------ */ |
610 | /* Function: ipf_nat_setlock */ |
611 | /* Returns: Nil */ |
612 | /* Parameters: arg(I) - pointer to soft state information */ |
613 | /* tmp(I) - new lock value */ |
614 | /* */ |
615 | /* Set the "lock status" of NAT to the value in tmp. */ |
616 | /* ------------------------------------------------------------------------ */ |
617 | void |
618 | ipf_nat_setlock(void *arg, int tmp) |
619 | { |
620 | ipf_nat_softc_t *softn = arg; |
621 | |
622 | softn->ipf_nat_lock = tmp; |
623 | } |
624 | |
625 | |
626 | /* ------------------------------------------------------------------------ */ |
627 | /* Function: ipf_nat_addrdr */ |
628 | /* Returns: Nil */ |
629 | /* Parameters: n(I) - pointer to NAT rule to add */ |
630 | /* */ |
631 | /* Adds a redirect rule to the hash table of redirect rules and the list of */ |
632 | /* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ |
633 | /* use by redirect rules. */ |
634 | /* ------------------------------------------------------------------------ */ |
635 | static void |
636 | ipf_nat_addrdr(ipf_nat_softc_t *softn, ipnat_t *n) |
637 | { |
638 | ipnat_t **np; |
639 | u_32_t j; |
640 | u_int hv; |
641 | u_int rhv; |
642 | int k; |
643 | |
644 | if (n->in_odstatype == FRI_NORMAL) { |
645 | k = count4bits(n->in_odstmsk); |
646 | ipf_inet_mask_add(k, &softn->ipf_nat_rdr_mask); |
647 | j = (n->in_odstaddr & n->in_odstmsk); |
648 | rhv = NAT_HASH_FN(j, 0, 0xffffffff); |
649 | } else { |
650 | ipf_inet_mask_add(0, &softn->ipf_nat_rdr_mask); |
651 | j = 0; |
652 | rhv = 0; |
653 | } |
654 | hv = rhv % softn->ipf_nat_rdrrules_sz; |
655 | np = softn->ipf_nat_rdr_rules + hv; |
656 | while (*np != NULL) |
657 | np = &(*np)->in_rnext; |
658 | n->in_rnext = NULL; |
659 | n->in_prnext = np; |
660 | n->in_hv[0] = hv; |
661 | n->in_use++; |
662 | *np = n; |
663 | } |
664 | |
665 | |
666 | /* ------------------------------------------------------------------------ */ |
667 | /* Function: ipf_nat_addmap */ |
668 | /* Returns: Nil */ |
669 | /* Parameters: n(I) - pointer to NAT rule to add */ |
670 | /* */ |
671 | /* Adds a NAT map rule to the hash table of rules and the list of loaded */ |
672 | /* NAT rules. Updates the bitmask indicating which netmasks are in use by */ |
673 | /* redirect rules. */ |
674 | /* ------------------------------------------------------------------------ */ |
675 | static void |
676 | ipf_nat_addmap(ipf_nat_softc_t *softn, ipnat_t *n) |
677 | { |
678 | ipnat_t **np; |
679 | u_32_t j; |
680 | u_int hv; |
681 | u_int rhv; |
682 | int k; |
683 | |
684 | if (n->in_osrcatype == FRI_NORMAL) { |
685 | k = count4bits(n->in_osrcmsk); |
686 | ipf_inet_mask_add(k, &softn->ipf_nat_map_mask); |
687 | j = (n->in_osrcaddr & n->in_osrcmsk); |
688 | rhv = NAT_HASH_FN(j, 0, 0xffffffff); |
689 | } else { |
690 | ipf_inet_mask_add(0, &softn->ipf_nat_map_mask); |
691 | j = 0; |
692 | rhv = 0; |
693 | } |
694 | hv = rhv % softn->ipf_nat_maprules_sz; |
695 | np = softn->ipf_nat_map_rules + hv; |
696 | while (*np != NULL) |
697 | np = &(*np)->in_mnext; |
698 | n->in_mnext = NULL; |
699 | n->in_pmnext = np; |
700 | n->in_hv[1] = rhv; |
701 | n->in_use++; |
702 | *np = n; |
703 | } |
704 | |
705 | |
706 | /* ------------------------------------------------------------------------ */ |
707 | /* Function: ipf_nat_delrdr */ |
708 | /* Returns: Nil */ |
709 | /* Parameters: n(I) - pointer to NAT rule to delete */ |
710 | /* */ |
711 | /* Removes a redirect rule from the hash table of redirect rules. */ |
712 | /* ------------------------------------------------------------------------ */ |
713 | void |
714 | ipf_nat_delrdr(ipf_nat_softc_t *softn, ipnat_t *n) |
715 | { |
716 | if (n->in_odstatype == FRI_NORMAL) { |
717 | int k = count4bits(n->in_odstmsk); |
718 | ipf_inet_mask_del(k, &softn->ipf_nat_rdr_mask); |
719 | } else { |
720 | ipf_inet_mask_del(0, &softn->ipf_nat_rdr_mask); |
721 | } |
722 | if (n->in_rnext) |
723 | n->in_rnext->in_prnext = n->in_prnext; |
724 | *n->in_prnext = n->in_rnext; |
725 | n->in_use--; |
726 | } |
727 | |
728 | |
729 | /* ------------------------------------------------------------------------ */ |
730 | /* Function: ipf_nat_delmap */ |
731 | /* Returns: Nil */ |
732 | /* Parameters: n(I) - pointer to NAT rule to delete */ |
733 | /* */ |
734 | /* Removes a NAT map rule from the hash table of NAT map rules. */ |
735 | /* ------------------------------------------------------------------------ */ |
736 | void |
737 | ipf_nat_delmap(ipf_nat_softc_t *softn, ipnat_t *n) |
738 | { |
739 | if (n->in_osrcatype == FRI_NORMAL) { |
740 | int k = count4bits(n->in_osrcmsk); |
741 | ipf_inet_mask_del(k, &softn->ipf_nat_map_mask); |
742 | } else { |
743 | ipf_inet_mask_del(0, &softn->ipf_nat_map_mask); |
744 | } |
745 | if (n->in_mnext != NULL) |
746 | n->in_mnext->in_pmnext = n->in_pmnext; |
747 | *n->in_pmnext = n->in_mnext; |
748 | n->in_use--; |
749 | } |
750 | |
751 | |
752 | /* ------------------------------------------------------------------------ */ |
753 | /* Function: ipf_nat_hostmap */ |
754 | /* Returns: struct hostmap* - NULL if no hostmap could be created, */ |
755 | /* else a pointer to the hostmapping to use */ |
756 | /* Parameters: np(I) - pointer to NAT rule */ |
757 | /* real(I) - real IP address */ |
758 | /* map(I) - mapped IP address */ |
759 | /* port(I) - destination port number */ |
760 | /* Write Locks: ipf_nat */ |
761 | /* */ |
762 | /* Check if an ip address has already been allocated for a given mapping */ |
763 | /* that is not doing port based translation. If is not yet allocated, then */ |
764 | /* create a new entry if a non-NULL NAT rule pointer has been supplied. */ |
765 | /* ------------------------------------------------------------------------ */ |
766 | static struct hostmap * |
767 | ipf_nat_hostmap(ipf_nat_softc_t *softn, ipnat_t *np, struct in_addr src, |
768 | struct in_addr dst, struct in_addr map, u_32_t port) |
769 | { |
770 | hostmap_t *hm; |
771 | u_int hv, rhv; |
772 | |
773 | hv = (src.s_addr ^ dst.s_addr); |
774 | hv += src.s_addr; |
775 | hv += dst.s_addr; |
776 | rhv = hv; |
777 | hv %= softn->ipf_nat_hostmap_sz; |
778 | for (hm = softn->ipf_hm_maptable[hv]; hm; hm = hm->hm_hnext) |
779 | if ((hm->hm_osrcip.s_addr == src.s_addr) && |
780 | (hm->hm_odstip.s_addr == dst.s_addr) && |
781 | ((np == NULL) || (np == hm->hm_ipnat)) && |
782 | ((port == 0) || (port == hm->hm_port))) { |
783 | softn->ipf_nat_stats.ns_hm_addref++; |
784 | hm->hm_ref++; |
785 | return hm; |
786 | } |
787 | |
788 | if (np == NULL) { |
789 | softn->ipf_nat_stats.ns_hm_nullnp++; |
790 | return NULL; |
791 | } |
792 | |
793 | KMALLOC(hm, hostmap_t *); |
794 | if (hm) { |
795 | hm->hm_next = softn->ipf_hm_maplist; |
796 | hm->hm_pnext = &softn->ipf_hm_maplist; |
797 | if (softn->ipf_hm_maplist != NULL) |
798 | softn->ipf_hm_maplist->hm_pnext = &hm->hm_next; |
799 | softn->ipf_hm_maplist = hm; |
800 | hm->hm_hnext = softn->ipf_hm_maptable[hv]; |
801 | hm->hm_phnext = softn->ipf_hm_maptable + hv; |
802 | if (softn->ipf_hm_maptable[hv] != NULL) |
803 | softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; |
804 | softn->ipf_hm_maptable[hv] = hm; |
805 | hm->hm_ipnat = np; |
806 | np->in_use++; |
807 | hm->hm_osrcip = src; |
808 | hm->hm_odstip = dst; |
809 | hm->hm_nsrcip = map; |
810 | hm->hm_ndstip.s_addr = 0; |
811 | hm->hm_ref = 1; |
812 | hm->hm_port = port; |
813 | hm->hm_hv = rhv; |
814 | hm->hm_v = 4; |
815 | softn->ipf_nat_stats.ns_hm_new++; |
816 | } else { |
817 | softn->ipf_nat_stats.ns_hm_newfail++; |
818 | } |
819 | return hm; |
820 | } |
821 | |
822 | |
823 | /* ------------------------------------------------------------------------ */ |
824 | /* Function: ipf_nat_hostmapdel */ |
825 | /* Returns: Nil */ |
826 | /* Parameters: hmp(I) - pointer to hostmap structure pointer */ |
827 | /* Write Locks: ipf_nat */ |
828 | /* */ |
829 | /* Decrement the references to this hostmap structure by one. If this */ |
830 | /* reaches zero then remove it and free it. */ |
831 | /* ------------------------------------------------------------------------ */ |
832 | void |
833 | ipf_nat_hostmapdel(ipf_main_softc_t *softc, struct hostmap **hmp) |
834 | { |
835 | struct hostmap *hm; |
836 | |
837 | hm = *hmp; |
838 | *hmp = NULL; |
839 | |
840 | hm->hm_ref--; |
841 | if (hm->hm_ref == 0) { |
842 | ipf_nat_rule_deref(softc, &hm->hm_ipnat); |
843 | if (hm->hm_hnext) |
844 | hm->hm_hnext->hm_phnext = hm->hm_phnext; |
845 | *hm->hm_phnext = hm->hm_hnext; |
846 | if (hm->hm_next) |
847 | hm->hm_next->hm_pnext = hm->hm_pnext; |
848 | *hm->hm_pnext = hm->hm_next; |
849 | KFREE(hm); |
850 | } |
851 | } |
852 | |
853 | |
854 | /* ------------------------------------------------------------------------ */ |
855 | /* Function: ipf_fix_outcksum */ |
856 | /* Returns: Nil */ |
857 | /* Parameters: fin(I) - pointer to packet information */ |
858 | /* sp(I) - location of 16bit checksum to update */ |
859 | /* n((I) - amount to adjust checksum by */ |
860 | /* */ |
861 | /* Adjusts the 16bit checksum by "n" for packets going out. */ |
862 | /* ------------------------------------------------------------------------ */ |
863 | void |
864 | ipf_fix_outcksum(int cksum, u_short *sp, u_32_t n, u_32_t partial) |
865 | { |
866 | u_short sumshort; |
867 | u_32_t sum1; |
868 | |
869 | if (n == 0) |
870 | return; |
871 | |
872 | if (cksum == 4) { |
873 | *sp = 0; |
874 | return; |
875 | } |
876 | if (cksum == 2) { |
877 | sum1 = partial; |
878 | sum1 = (sum1 & 0xffff) + (sum1 >> 16); |
879 | *sp = htons(sum1); |
880 | return; |
881 | } |
882 | sum1 = (~ntohs(*sp)) & 0xffff; |
883 | sum1 += (n); |
884 | sum1 = (sum1 >> 16) + (sum1 & 0xffff); |
885 | /* Again */ |
886 | sum1 = (sum1 >> 16) + (sum1 & 0xffff); |
887 | sumshort = ~(u_short)sum1; |
888 | *(sp) = htons(sumshort); |
889 | } |
890 | |
891 | |
892 | /* ------------------------------------------------------------------------ */ |
893 | /* Function: ipf_fix_incksum */ |
894 | /* Returns: Nil */ |
895 | /* Parameters: fin(I) - pointer to packet information */ |
896 | /* sp(I) - location of 16bit checksum to update */ |
897 | /* n((I) - amount to adjust checksum by */ |
898 | /* */ |
899 | /* Adjusts the 16bit checksum by "n" for packets going in. */ |
900 | /* ------------------------------------------------------------------------ */ |
901 | void |
902 | ipf_fix_incksum(int cksum, u_short *sp, u_32_t n, u_32_t partial) |
903 | { |
904 | u_short sumshort; |
905 | u_32_t sum1; |
906 | |
907 | if (n == 0) |
908 | return; |
909 | |
910 | if (cksum == 4) { |
911 | *sp = 0; |
912 | return; |
913 | } |
914 | if (cksum == 2) { |
915 | sum1 = partial; |
916 | sum1 = (sum1 & 0xffff) + (sum1 >> 16); |
917 | *sp = htons(sum1); |
918 | return; |
919 | } |
920 | |
921 | sum1 = (~ntohs(*sp)) & 0xffff; |
922 | sum1 += ~(n) & 0xffff; |
923 | sum1 = (sum1 >> 16) + (sum1 & 0xffff); |
924 | /* Again */ |
925 | sum1 = (sum1 >> 16) + (sum1 & 0xffff); |
926 | sumshort = ~(u_short)sum1; |
927 | *(sp) = htons(sumshort); |
928 | } |
929 | |
930 | |
931 | /* ------------------------------------------------------------------------ */ |
932 | /* Function: ipf_fix_datacksum */ |
933 | /* Returns: Nil */ |
934 | /* Parameters: sp(I) - location of 16bit checksum to update */ |
935 | /* n((I) - amount to adjust checksum by */ |
936 | /* */ |
937 | /* Fix_datacksum is used *only* for the adjustments of checksums in the */ |
938 | /* data section of an IP packet. */ |
939 | /* */ |
940 | /* The only situation in which you need to do this is when NAT'ing an */ |
941 | /* ICMP error message. Such a message, contains in its body the IP header */ |
942 | /* of the original IP packet, that causes the error. */ |
943 | /* */ |
944 | /* You can't use fix_incksum or fix_outcksum in that case, because for the */ |
945 | /* kernel the data section of the ICMP error is just data, and no special */ |
946 | /* processing like hardware cksum or ntohs processing have been done by the */ |
947 | /* kernel on the data section. */ |
948 | /* ------------------------------------------------------------------------ */ |
949 | void |
950 | ipf_fix_datacksum(u_short *sp, u_32_t n) |
951 | { |
952 | u_short sumshort; |
953 | u_32_t sum1; |
954 | |
955 | if (n == 0) |
956 | return; |
957 | |
958 | sum1 = (~ntohs(*sp)) & 0xffff; |
959 | sum1 += (n); |
960 | sum1 = (sum1 >> 16) + (sum1 & 0xffff); |
961 | /* Again */ |
962 | sum1 = (sum1 >> 16) + (sum1 & 0xffff); |
963 | sumshort = ~(u_short)sum1; |
964 | *(sp) = htons(sumshort); |
965 | } |
966 | |
967 | |
968 | /* ------------------------------------------------------------------------ */ |
969 | /* Function: ipf_nat_ioctl */ |
970 | /* Returns: int - 0 == success, != 0 == failure */ |
971 | /* Parameters: softc(I) - pointer to soft context main structure */ |
972 | /* data(I) - pointer to ioctl data */ |
973 | /* cmd(I) - ioctl command integer */ |
974 | /* mode(I) - file mode bits used with open */ |
975 | /* uid(I) - uid of calling process */ |
976 | /* ctx(I) - pointer used as key for finding context */ |
977 | /* */ |
978 | /* Processes an ioctl call made to operate on the IP Filter NAT device. */ |
979 | /* ------------------------------------------------------------------------ */ |
980 | int |
981 | ipf_nat_ioctl(ipf_main_softc_t *softc, void *data, ioctlcmd_t cmd, int mode, |
982 | int uid, void *ctx) |
983 | { |
984 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
985 | int error = 0, ret, arg, getlock; |
986 | ipnat_t *nat, *nt, *n; |
987 | ipnat_t natd; |
988 | SPL_INT(s); |
989 | |
990 | #if BSD_GE_YEAR(199306) && defined(_KERNEL) |
991 | # if NETBSD_GE_REV(399002000) |
992 | if ((mode & FWRITE) && |
993 | kauth_authorize_network(curlwp->l_cred, KAUTH_NETWORK_FIREWALL, |
994 | KAUTH_REQ_NETWORK_FIREWALL_FW, |
995 | NULL, NULL, NULL)) |
996 | # else |
997 | # if defined(__FreeBSD_version) && (__FreeBSD_version >= 500034) |
998 | if (securelevel_ge(curthread->td_ucred, 3) && (mode & FWRITE)) |
999 | # else |
1000 | if ((securelevel >= 3) && (mode & FWRITE)) |
1001 | # endif |
1002 | # endif |
1003 | { |
1004 | IPFERROR(60001); |
1005 | return EPERM; |
1006 | } |
1007 | #endif |
1008 | |
1009 | #if defined(__osf__) && defined(_KERNEL) |
1010 | getlock = 0; |
1011 | #else |
1012 | getlock = (mode & NAT_LOCKHELD) ? 0 : 1; |
1013 | #endif |
1014 | |
1015 | n = NULL; |
1016 | nt = NULL; |
1017 | nat = NULL; |
1018 | |
1019 | if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT) || |
1020 | (cmd == (ioctlcmd_t)SIOCPURGENAT)) { |
1021 | if (mode & NAT_SYSSPACE) { |
1022 | bcopy(data, (char *)&natd, sizeof(natd)); |
1023 | nat = &natd; |
1024 | error = 0; |
1025 | } else { |
1026 | bzero(&natd, sizeof(natd)); |
1027 | error = ipf_inobj(softc, data, NULL, &natd, |
1028 | IPFOBJ_IPNAT); |
1029 | if (error != 0) |
1030 | goto done; |
1031 | |
1032 | if (natd.in_size < sizeof(ipnat_t)) { |
1033 | error = EINVAL; |
1034 | goto done; |
1035 | } |
1036 | KMALLOCS(nt, ipnat_t *, natd.in_size); |
1037 | if (nt == NULL) { |
1038 | IPFERROR(60070); |
1039 | error = ENOMEM; |
1040 | goto done; |
1041 | } |
1042 | bzero(nt, natd.in_size); |
1043 | error = ipf_inobjsz(softc, data, nt, IPFOBJ_IPNAT, |
1044 | natd.in_size); |
1045 | if (error) |
1046 | goto done; |
1047 | nat = nt; |
1048 | } |
1049 | |
1050 | /* |
1051 | * For add/delete, look to see if the NAT entry is |
1052 | * already present |
1053 | */ |
1054 | nat->in_flags &= IPN_USERFLAGS; |
1055 | if ((nat->in_redir & NAT_MAPBLK) == 0) { |
1056 | if (nat->in_osrcatype == FRI_NORMAL || |
1057 | nat->in_osrcatype == FRI_NONE) |
1058 | nat->in_osrcaddr &= nat->in_osrcmsk; |
1059 | if (nat->in_odstatype == FRI_NORMAL || |
1060 | nat->in_odstatype == FRI_NONE) |
1061 | nat->in_odstaddr &= nat->in_odstmsk; |
1062 | if ((nat->in_flags & (IPN_SPLIT|IPN_SIPRANGE)) == 0) { |
1063 | if (nat->in_nsrcatype == FRI_NORMAL) |
1064 | nat->in_nsrcaddr &= nat->in_nsrcmsk; |
1065 | if (nat->in_ndstatype == FRI_NORMAL) |
1066 | nat->in_ndstaddr &= nat->in_ndstmsk; |
1067 | } |
1068 | } |
1069 | |
1070 | error = ipf_nat_rule_init(softc, softn, nat); |
1071 | if (error != 0) |
1072 | goto done; |
1073 | |
1074 | MUTEX_ENTER(&softn->ipf_nat_io); |
1075 | for (n = softn->ipf_nat_list; n != NULL; n = n->in_next) |
1076 | if (ipf_nat_cmp_rules(nat, n) == 0) |
1077 | break; |
1078 | } |
1079 | |
1080 | switch (cmd) |
1081 | { |
1082 | #ifdef IPFILTER_LOG |
1083 | case SIOCIPFFB : |
1084 | { |
1085 | int tmp; |
1086 | |
1087 | if (!(mode & FWRITE)) { |
1088 | IPFERROR(60002); |
1089 | error = EPERM; |
1090 | } else { |
1091 | tmp = ipf_log_clear(softc, IPL_LOGNAT); |
1092 | error = BCOPYOUT(&tmp, data, sizeof(tmp)); |
1093 | if (error != 0) { |
1094 | IPFERROR(60057); |
1095 | error = EFAULT; |
1096 | } |
1097 | } |
1098 | break; |
1099 | } |
1100 | |
1101 | case SIOCSETLG : |
1102 | if (!(mode & FWRITE)) { |
1103 | IPFERROR(60003); |
1104 | error = EPERM; |
1105 | } else { |
1106 | error = BCOPYIN(data, &softn->ipf_nat_logging, |
1107 | sizeof(softn->ipf_nat_logging)); |
1108 | if (error != 0) |
1109 | error = EFAULT; |
1110 | } |
1111 | break; |
1112 | |
1113 | case SIOCGETLG : |
1114 | error = BCOPYOUT(&softn->ipf_nat_logging, data, |
1115 | sizeof(softn->ipf_nat_logging)); |
1116 | if (error != 0) { |
1117 | IPFERROR(60004); |
1118 | error = EFAULT; |
1119 | } |
1120 | break; |
1121 | |
1122 | case FIONREAD : |
1123 | arg = ipf_log_bytesused(softc, IPL_LOGNAT); |
1124 | error = BCOPYOUT(&arg, data, sizeof(arg)); |
1125 | if (error != 0) { |
1126 | IPFERROR(60005); |
1127 | error = EFAULT; |
1128 | } |
1129 | break; |
1130 | #endif |
1131 | case SIOCADNAT : |
1132 | if (!(mode & FWRITE)) { |
1133 | IPFERROR(60006); |
1134 | error = EPERM; |
1135 | } else if (n != NULL) { |
1136 | natd.in_flineno = n->in_flineno; |
1137 | (void) ipf_outobj(softc, data, &natd, IPFOBJ_IPNAT); |
1138 | IPFERROR(60007); |
1139 | error = EEXIST; |
1140 | } else if (nt == NULL) { |
1141 | IPFERROR(60008); |
1142 | error = ENOMEM; |
1143 | } |
1144 | if (error != 0) { |
1145 | MUTEX_EXIT(&softn->ipf_nat_io); |
1146 | break; |
1147 | } |
1148 | if (nat != nt) |
1149 | bcopy((char *)nat, (char *)nt, sizeof(*n)); |
1150 | error = ipf_nat_siocaddnat(softc, softn, nt, getlock); |
1151 | MUTEX_EXIT(&softn->ipf_nat_io); |
1152 | if (error == 0) { |
1153 | nat = NULL; |
1154 | nt = NULL; |
1155 | } |
1156 | break; |
1157 | |
1158 | case SIOCRMNAT : |
1159 | case SIOCPURGENAT : |
1160 | if (!(mode & FWRITE)) { |
1161 | IPFERROR(60009); |
1162 | error = EPERM; |
1163 | n = NULL; |
1164 | } else if (n == NULL) { |
1165 | IPFERROR(60010); |
1166 | error = ESRCH; |
1167 | } |
1168 | |
1169 | if (error != 0) { |
1170 | MUTEX_EXIT(&softn->ipf_nat_io); |
1171 | break; |
1172 | } |
1173 | if (cmd == (ioctlcmd_t)SIOCPURGENAT) { |
1174 | error = ipf_outobjsz(softc, data, n, IPFOBJ_IPNAT, |
1175 | n->in_size); |
1176 | if (error) { |
1177 | MUTEX_EXIT(&softn->ipf_nat_io); |
1178 | goto done; |
1179 | } |
1180 | n->in_flags |= IPN_PURGE; |
1181 | } |
1182 | ipf_nat_siocdelnat(softc, softn, n, getlock); |
1183 | |
1184 | MUTEX_EXIT(&softn->ipf_nat_io); |
1185 | n = NULL; |
1186 | break; |
1187 | |
1188 | case SIOCGNATS : |
1189 | { |
1190 | natstat_t *nsp = &softn->ipf_nat_stats; |
1191 | |
1192 | nsp->ns_side[0].ns_table = softn->ipf_nat_table[0]; |
1193 | nsp->ns_side[1].ns_table = softn->ipf_nat_table[1]; |
1194 | nsp->ns_list = softn->ipf_nat_list; |
1195 | nsp->ns_maptable = softn->ipf_hm_maptable; |
1196 | nsp->ns_maplist = softn->ipf_hm_maplist; |
1197 | nsp->ns_nattab_sz = softn->ipf_nat_table_sz; |
1198 | nsp->ns_nattab_max = softn->ipf_nat_table_max; |
1199 | nsp->ns_rultab_sz = softn->ipf_nat_maprules_sz; |
1200 | nsp->ns_rdrtab_sz = softn->ipf_nat_rdrrules_sz; |
1201 | nsp->ns_hostmap_sz = softn->ipf_nat_hostmap_sz; |
1202 | nsp->ns_instances = softn->ipf_nat_instances; |
1203 | nsp->ns_ticks = softc->ipf_ticks; |
1204 | #ifdef IPFILTER_LOGGING |
1205 | nsp->ns_log_ok = ipf_log_logok(softc, IPF_LOGNAT); |
1206 | nsp->ns_log_fail = ipf_log_failures(softc, IPF_LOGNAT); |
1207 | #else |
1208 | nsp->ns_log_ok = 0; |
1209 | nsp->ns_log_fail = 0; |
1210 | #endif |
1211 | error = ipf_outobj(softc, data, nsp, IPFOBJ_NATSTAT); |
1212 | break; |
1213 | } |
1214 | |
1215 | case SIOCGNATL : |
1216 | { |
1217 | natlookup_t nl; |
1218 | |
1219 | error = ipf_inobj(softc, data, NULL, &nl, IPFOBJ_NATLOOKUP); |
1220 | if (error == 0) { |
1221 | void *ptr; |
1222 | |
1223 | if (getlock) { |
1224 | READ_ENTER(&softc->ipf_nat); |
1225 | } |
1226 | |
1227 | switch (nl.nl_v) |
1228 | { |
1229 | case 4 : |
1230 | ptr = ipf_nat_lookupredir(softc, &nl); |
1231 | break; |
1232 | #ifdef USE_INET6 |
1233 | case 6 : |
1234 | ptr = ipf_nat6_lookupredir(softc, &nl); |
1235 | break; |
1236 | #endif |
1237 | default: |
1238 | ptr = NULL; |
1239 | break; |
1240 | } |
1241 | |
1242 | if (getlock) { |
1243 | RWLOCK_EXIT(&softc->ipf_nat); |
1244 | } |
1245 | if (ptr != NULL) { |
1246 | error = ipf_outobj(softc, data, &nl, |
1247 | IPFOBJ_NATLOOKUP); |
1248 | } else { |
1249 | IPFERROR(60011); |
1250 | error = ESRCH; |
1251 | } |
1252 | } |
1253 | break; |
1254 | } |
1255 | |
1256 | case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */ |
1257 | if (!(mode & FWRITE)) { |
1258 | IPFERROR(60012); |
1259 | error = EPERM; |
1260 | break; |
1261 | } |
1262 | if (getlock) { |
1263 | WRITE_ENTER(&softc->ipf_nat); |
1264 | } |
1265 | |
1266 | error = BCOPYIN(data, &arg, sizeof(arg)); |
1267 | if (error != 0) { |
1268 | IPFERROR(60013); |
1269 | error = EFAULT; |
1270 | } else { |
1271 | if (arg == 0) |
1272 | ret = ipf_nat_flushtable(softc, softn); |
1273 | else if (arg == 1) |
1274 | ret = ipf_nat_clearlist(softc, softn); |
1275 | else |
1276 | ret = ipf_nat_extraflush(softc, softn, arg); |
1277 | ipf_proxy_flush(softc->ipf_proxy_soft, arg); |
1278 | } |
1279 | |
1280 | if (getlock) { |
1281 | RWLOCK_EXIT(&softc->ipf_nat); |
1282 | } |
1283 | if (error == 0) { |
1284 | error = BCOPYOUT(&ret, data, sizeof(ret)); |
1285 | } |
1286 | break; |
1287 | |
1288 | case SIOCMATCHFLUSH : |
1289 | if (!(mode & FWRITE)) { |
1290 | IPFERROR(60014); |
1291 | error = EPERM; |
1292 | break; |
1293 | } |
1294 | if (getlock) { |
1295 | WRITE_ENTER(&softc->ipf_nat); |
1296 | } |
1297 | |
1298 | error = ipf_nat_matchflush(softc, softn, data); |
1299 | |
1300 | if (getlock) { |
1301 | RWLOCK_EXIT(&softc->ipf_nat); |
1302 | } |
1303 | break; |
1304 | |
1305 | case SIOCPROXY : |
1306 | error = ipf_proxy_ioctl(softc, data, cmd, mode, ctx); |
1307 | break; |
1308 | |
1309 | case SIOCSTLCK : |
1310 | if (!(mode & FWRITE)) { |
1311 | IPFERROR(60015); |
1312 | error = EPERM; |
1313 | } else { |
1314 | error = ipf_lock(data, &softn->ipf_nat_lock); |
1315 | } |
1316 | break; |
1317 | |
1318 | case SIOCSTPUT : |
1319 | if ((mode & FWRITE) != 0) { |
1320 | error = ipf_nat_putent(softc, data, getlock); |
1321 | } else { |
1322 | IPFERROR(60016); |
1323 | error = EACCES; |
1324 | } |
1325 | break; |
1326 | |
1327 | case SIOCSTGSZ : |
1328 | if (softn->ipf_nat_lock) { |
1329 | error = ipf_nat_getsz(softc, data, getlock); |
1330 | } else { |
1331 | IPFERROR(60017); |
1332 | error = EACCES; |
1333 | } |
1334 | break; |
1335 | |
1336 | case SIOCSTGET : |
1337 | if (softn->ipf_nat_lock) { |
1338 | error = ipf_nat_getent(softc, data, getlock); |
1339 | } else { |
1340 | IPFERROR(60018); |
1341 | error = EACCES; |
1342 | } |
1343 | break; |
1344 | |
1345 | case SIOCGENITER : |
1346 | { |
1347 | ipfgeniter_t iter; |
1348 | ipftoken_t *token; |
1349 | ipfobj_t obj; |
1350 | |
1351 | error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER); |
1352 | if (error != 0) |
1353 | break; |
1354 | |
1355 | SPL_SCHED(s); |
1356 | token = ipf_token_find(softc, iter.igi_type, uid, ctx); |
1357 | if (token != NULL) { |
1358 | error = ipf_nat_iterator(softc, token, &iter, &obj); |
1359 | WRITE_ENTER(&softc->ipf_tokens); |
1360 | ipf_token_deref(softc, token); |
1361 | RWLOCK_EXIT(&softc->ipf_tokens); |
1362 | } |
1363 | SPL_X(s); |
1364 | break; |
1365 | } |
1366 | |
1367 | case SIOCIPFDELTOK : |
1368 | error = BCOPYIN(data, &arg, sizeof(arg)); |
1369 | if (error == 0) { |
1370 | SPL_SCHED(s); |
1371 | error = ipf_token_del(softc, arg, uid, ctx); |
1372 | SPL_X(s); |
1373 | } else { |
1374 | IPFERROR(60019); |
1375 | error = EFAULT; |
1376 | } |
1377 | break; |
1378 | |
1379 | case SIOCGTQTAB : |
1380 | error = ipf_outobj(softc, data, softn->ipf_nat_tcptq, |
1381 | IPFOBJ_STATETQTAB); |
1382 | break; |
1383 | |
1384 | case SIOCGTABL : |
1385 | error = ipf_nat_gettable(softc, softn, data); |
1386 | break; |
1387 | |
1388 | default : |
1389 | IPFERROR(60020); |
1390 | error = EINVAL; |
1391 | break; |
1392 | } |
1393 | done: |
1394 | if (nat != NULL) |
1395 | ipf_nat_rule_fini(softc, nat); |
1396 | if (nt != NULL) |
1397 | KFREES(nt, nt->in_size); |
1398 | return error; |
1399 | } |
1400 | |
1401 | |
1402 | /* ------------------------------------------------------------------------ */ |
1403 | /* Function: ipf_nat_siocaddnat */ |
1404 | /* Returns: int - 0 == success, != 0 == failure */ |
1405 | /* Parameters: softc(I) - pointer to soft context main structure */ |
1406 | /* softn(I) - pointer to NAT context structure */ |
1407 | /* n(I) - pointer to new NAT rule */ |
1408 | /* np(I) - pointer to where to insert new NAT rule */ |
1409 | /* getlock(I) - flag indicating if lock on is held */ |
1410 | /* Mutex Locks: ipf_nat_io */ |
1411 | /* */ |
1412 | /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ |
1413 | /* from information passed to the kernel, then add it to the appropriate */ |
1414 | /* NAT rule table(s). */ |
1415 | /* ------------------------------------------------------------------------ */ |
1416 | static int |
1417 | ipf_nat_siocaddnat(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, ipnat_t *n, |
1418 | int getlock) |
1419 | { |
1420 | int error = 0; |
1421 | |
1422 | if (ipf_nat_resolverule(softc, n) != 0) { |
1423 | IPFERROR(60022); |
1424 | return ENOENT; |
1425 | } |
1426 | |
1427 | if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) { |
1428 | IPFERROR(60023); |
1429 | return EINVAL; |
1430 | } |
1431 | |
1432 | if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) { |
1433 | /* |
1434 | * Prerecord whether or not the destination of the divert |
1435 | * is local or not to the interface the packet is going |
1436 | * to be sent out. |
1437 | */ |
1438 | n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1], |
1439 | n->in_ifps[1], &n->in_ndstip6); |
1440 | } |
1441 | |
1442 | if (getlock) { |
1443 | WRITE_ENTER(&softc->ipf_nat); |
1444 | } |
1445 | n->in_next = NULL; |
1446 | n->in_pnext = softn->ipf_nat_list_tail; |
1447 | *n->in_pnext = n; |
1448 | softn->ipf_nat_list_tail = &n->in_next; |
1449 | n->in_use++; |
1450 | |
1451 | if (n->in_redir & NAT_REDIRECT) { |
1452 | n->in_flags &= ~IPN_NOTDST; |
1453 | switch (n->in_v[0]) |
1454 | { |
1455 | case 4 : |
1456 | ipf_nat_addrdr(softn, n); |
1457 | break; |
1458 | #ifdef USE_INET6 |
1459 | case 6 : |
1460 | ipf_nat6_addrdr(softn, n); |
1461 | break; |
1462 | #endif |
1463 | default : |
1464 | break; |
1465 | } |
1466 | ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_rdr); |
1467 | } |
1468 | |
1469 | if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { |
1470 | n->in_flags &= ~IPN_NOTSRC; |
1471 | switch (n->in_v[0]) |
1472 | { |
1473 | case 4 : |
1474 | ipf_nat_addmap(softn, n); |
1475 | break; |
1476 | #ifdef USE_INET6 |
1477 | case 6 : |
1478 | ipf_nat6_addmap(softn, n); |
1479 | break; |
1480 | #endif |
1481 | default : |
1482 | break; |
1483 | } |
1484 | ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_map); |
1485 | } |
1486 | |
1487 | if (n->in_age[0] != 0) |
1488 | n->in_tqehead[0] = ipf_addtimeoutqueue(softc, |
1489 | &softn->ipf_nat_utqe, |
1490 | n->in_age[0]); |
1491 | |
1492 | if (n->in_age[1] != 0) |
1493 | n->in_tqehead[1] = ipf_addtimeoutqueue(softc, |
1494 | &softn->ipf_nat_utqe, |
1495 | n->in_age[1]); |
1496 | |
1497 | MUTEX_INIT(&n->in_lock, "ipnat rule lock" ); |
1498 | |
1499 | n = NULL; |
1500 | ATOMIC_INC32(softn->ipf_nat_stats.ns_rules); |
1501 | #if SOLARIS && !defined(INSTANCES) |
1502 | pfil_delayed_copy = 0; |
1503 | #endif |
1504 | if (getlock) { |
1505 | RWLOCK_EXIT(&softc->ipf_nat); /* WRITE */ |
1506 | } |
1507 | |
1508 | return error; |
1509 | } |
1510 | |
1511 | |
1512 | /* ------------------------------------------------------------------------ */ |
1513 | /* Function: ipf_nat_ruleaddrinit */ |
1514 | /* Parameters: softc(I) - pointer to soft context main structure */ |
1515 | /* softn(I) - pointer to NAT context structure */ |
1516 | /* n(I) - pointer to NAT rule */ |
1517 | /* */ |
1518 | /* Initialise all of the NAT address structures in a NAT rule. */ |
1519 | /* ------------------------------------------------------------------------ */ |
1520 | static int |
1521 | ipf_nat_ruleaddrinit(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, |
1522 | ipnat_t *n) |
1523 | { |
1524 | int idx, error; |
1525 | |
1526 | if ((n->in_ndst.na_atype == FRI_LOOKUP) && |
1527 | (n->in_ndst.na_type != IPLT_DSTLIST)) { |
1528 | IPFERROR(60071); |
1529 | return EINVAL; |
1530 | } |
1531 | if ((n->in_nsrc.na_atype == FRI_LOOKUP) && |
1532 | (n->in_nsrc.na_type != IPLT_DSTLIST)) { |
1533 | IPFERROR(60069); |
1534 | return EINVAL; |
1535 | } |
1536 | |
1537 | if (n->in_redir == NAT_BIMAP) { |
1538 | n->in_ndstaddr = n->in_osrcaddr; |
1539 | n->in_ndstmsk = n->in_osrcmsk; |
1540 | n->in_odstaddr = n->in_nsrcaddr; |
1541 | n->in_odstmsk = n->in_nsrcmsk; |
1542 | |
1543 | } |
1544 | |
1545 | if (n->in_redir & NAT_REDIRECT) |
1546 | idx = 1; |
1547 | else |
1548 | idx = 0; |
1549 | /* |
1550 | * Initialise all of the address fields. |
1551 | */ |
1552 | error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, 1, |
1553 | n->in_ifps[idx]); |
1554 | if (error != 0) |
1555 | return error; |
1556 | |
1557 | error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, 1, |
1558 | n->in_ifps[idx]); |
1559 | if (error != 0) |
1560 | return error; |
1561 | |
1562 | error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, 1, |
1563 | n->in_ifps[idx]); |
1564 | if (error != 0) |
1565 | return error; |
1566 | |
1567 | error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, 1, |
1568 | n->in_ifps[idx]); |
1569 | if (error != 0) |
1570 | return error; |
1571 | |
1572 | if (n->in_redir & NAT_DIVERTUDP) |
1573 | ipf_nat_builddivertmp(softn, n); |
1574 | |
1575 | return 0; |
1576 | } |
1577 | |
1578 | |
1579 | /* ------------------------------------------------------------------------ */ |
1580 | /* Function: ipf_nat_resolvrule */ |
1581 | /* Returns: Nil */ |
1582 | /* Parameters: softc(I) - pointer to soft context main structure */ |
1583 | /* n(I) - pointer to NAT rule */ |
1584 | /* */ |
1585 | /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ |
1586 | /* from information passed to the kernel, then add it to the appropriate */ |
1587 | /* NAT rule table(s). */ |
1588 | /* ------------------------------------------------------------------------ */ |
1589 | static int |
1590 | ipf_nat_resolverule(ipf_main_softc_t *softc, ipnat_t *n) |
1591 | { |
1592 | char *base; |
1593 | |
1594 | base = n->in_names; |
1595 | |
1596 | n->in_ifps[0] = ipf_resolvenic(softc, base + n->in_ifnames[0], |
1597 | n->in_v[0]); |
1598 | |
1599 | if (n->in_ifnames[1] == -1) { |
1600 | n->in_ifnames[1] = n->in_ifnames[0]; |
1601 | n->in_ifps[1] = n->in_ifps[0]; |
1602 | } else { |
1603 | n->in_ifps[1] = ipf_resolvenic(softc, base + n->in_ifnames[1], |
1604 | n->in_v[1]); |
1605 | } |
1606 | |
1607 | if (n->in_plabel != -1) { |
1608 | if (n->in_redir & NAT_REDIRECT) |
1609 | n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft, |
1610 | n->in_pr[0], |
1611 | base + n->in_plabel); |
1612 | else |
1613 | n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft, |
1614 | n->in_pr[1], |
1615 | base + n->in_plabel); |
1616 | if (n->in_apr == NULL) |
1617 | return -1; |
1618 | } |
1619 | return 0; |
1620 | } |
1621 | |
1622 | |
1623 | /* ------------------------------------------------------------------------ */ |
1624 | /* Function: ipf_nat_siocdelnat */ |
1625 | /* Returns: int - 0 == success, != 0 == failure */ |
1626 | /* Parameters: softc(I) - pointer to soft context main structure */ |
1627 | /* softn(I) - pointer to NAT context structure */ |
1628 | /* n(I) - pointer to new NAT rule */ |
1629 | /* getlock(I) - flag indicating if lock on is held */ |
1630 | /* Mutex Locks: ipf_nat_io */ |
1631 | /* */ |
1632 | /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ |
1633 | /* from information passed to the kernel, then add it to the appropriate */ |
1634 | /* NAT rule table(s). */ |
1635 | /* ------------------------------------------------------------------------ */ |
1636 | static void |
1637 | ipf_nat_siocdelnat(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, ipnat_t *n, |
1638 | int getlock) |
1639 | { |
1640 | #ifdef IPF_NAT6 |
1641 | int i; |
1642 | #endif |
1643 | |
1644 | if (getlock) { |
1645 | WRITE_ENTER(&softc->ipf_nat); |
1646 | } |
1647 | |
1648 | ipf_nat_delrule(softc, softn, n, 1); |
1649 | |
1650 | if (getlock) { |
1651 | RWLOCK_EXIT(&softc->ipf_nat); /* READ/WRITE */ |
1652 | } |
1653 | } |
1654 | |
1655 | |
1656 | /* ------------------------------------------------------------------------ */ |
1657 | /* Function: ipf_nat_getsz */ |
1658 | /* Returns: int - 0 == success, != 0 is the error value. */ |
1659 | /* Parameters: softc(I) - pointer to soft context main structure */ |
1660 | /* data(I) - pointer to natget structure with kernel */ |
1661 | /* pointer get the size of. */ |
1662 | /* getlock(I) - flag indicating whether or not the caller */ |
1663 | /* holds a lock on ipf_nat */ |
1664 | /* */ |
1665 | /* Handle SIOCSTGSZ. */ |
1666 | /* Return the size of the nat list entry to be copied back to user space. */ |
1667 | /* The size of the entry is stored in the ng_sz field and the enture natget */ |
1668 | /* structure is copied back to the user. */ |
1669 | /* ------------------------------------------------------------------------ */ |
1670 | static int |
1671 | ipf_nat_getsz(ipf_main_softc_t *softc, void *data, int getlock) |
1672 | { |
1673 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
1674 | ap_session_t *aps; |
1675 | nat_t *nat, *n; |
1676 | natget_t ng; |
1677 | int error; |
1678 | |
1679 | error = BCOPYIN(data, &ng, sizeof(ng)); |
1680 | if (error != 0) { |
1681 | IPFERROR(60024); |
1682 | return EFAULT; |
1683 | } |
1684 | |
1685 | if (getlock) { |
1686 | READ_ENTER(&softc->ipf_nat); |
1687 | } |
1688 | |
1689 | nat = ng.ng_ptr; |
1690 | if (!nat) { |
1691 | nat = softn->ipf_nat_instances; |
1692 | ng.ng_sz = 0; |
1693 | /* |
1694 | * Empty list so the size returned is 0. Simple. |
1695 | */ |
1696 | if (nat == NULL) { |
1697 | if (getlock) { |
1698 | RWLOCK_EXIT(&softc->ipf_nat); |
1699 | } |
1700 | error = BCOPYOUT(&ng, data, sizeof(ng)); |
1701 | if (error != 0) { |
1702 | IPFERROR(60025); |
1703 | return EFAULT; |
1704 | } |
1705 | return 0; |
1706 | } |
1707 | } else { |
1708 | /* |
1709 | * Make sure the pointer we're copying from exists in the |
1710 | * current list of entries. Security precaution to prevent |
1711 | * copying of random kernel data. |
1712 | */ |
1713 | for (n = softn->ipf_nat_instances; n; n = n->nat_next) |
1714 | if (n == nat) |
1715 | break; |
1716 | if (n == NULL) { |
1717 | if (getlock) { |
1718 | RWLOCK_EXIT(&softc->ipf_nat); |
1719 | } |
1720 | IPFERROR(60026); |
1721 | return ESRCH; |
1722 | } |
1723 | } |
1724 | |
1725 | /* |
1726 | * Incluse any space required for proxy data structures. |
1727 | */ |
1728 | ng.ng_sz = sizeof(nat_save_t); |
1729 | aps = nat->nat_aps; |
1730 | if (aps != NULL) { |
1731 | ng.ng_sz += sizeof(ap_session_t) - 4; |
1732 | if (aps->aps_data != 0) |
1733 | ng.ng_sz += aps->aps_psiz; |
1734 | } |
1735 | if (getlock) { |
1736 | RWLOCK_EXIT(&softc->ipf_nat); |
1737 | } |
1738 | |
1739 | error = BCOPYOUT(&ng, data, sizeof(ng)); |
1740 | if (error != 0) { |
1741 | IPFERROR(60027); |
1742 | return EFAULT; |
1743 | } |
1744 | return 0; |
1745 | } |
1746 | |
1747 | |
1748 | /* ------------------------------------------------------------------------ */ |
1749 | /* Function: ipf_nat_getent */ |
1750 | /* Returns: int - 0 == success, != 0 is the error value. */ |
1751 | /* Parameters: softc(I) - pointer to soft context main structure */ |
1752 | /* data(I) - pointer to natget structure with kernel pointer*/ |
1753 | /* to NAT structure to copy out. */ |
1754 | /* getlock(I) - flag indicating whether or not the caller */ |
1755 | /* holds a lock on ipf_nat */ |
1756 | /* */ |
1757 | /* Handle SIOCSTGET. */ |
1758 | /* Copies out NAT entry to user space. Any additional data held for a */ |
1759 | /* proxy is also copied, as to is the NAT rule which was responsible for it */ |
1760 | /* ------------------------------------------------------------------------ */ |
1761 | static int |
1762 | ipf_nat_getent(ipf_main_softc_t *softc, void *data, int getlock) |
1763 | { |
1764 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
1765 | int error, outsize; |
1766 | ap_session_t *aps; |
1767 | nat_save_t *ipn, ipns; |
1768 | nat_t *n, *nat; |
1769 | |
1770 | error = ipf_inobj(softc, data, NULL, &ipns, IPFOBJ_NATSAVE); |
1771 | if (error != 0) |
1772 | return error; |
1773 | |
1774 | if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) { |
1775 | IPFERROR(60028); |
1776 | return EINVAL; |
1777 | } |
1778 | |
1779 | KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize); |
1780 | if (ipn == NULL) { |
1781 | IPFERROR(60029); |
1782 | return ENOMEM; |
1783 | } |
1784 | |
1785 | if (getlock) { |
1786 | READ_ENTER(&softc->ipf_nat); |
1787 | } |
1788 | |
1789 | ipn->ipn_dsize = ipns.ipn_dsize; |
1790 | nat = ipns.ipn_next; |
1791 | if (nat == NULL) { |
1792 | nat = softn->ipf_nat_instances; |
1793 | if (nat == NULL) { |
1794 | if (softn->ipf_nat_instances == NULL) { |
1795 | IPFERROR(60030); |
1796 | error = ENOENT; |
1797 | } |
1798 | goto finished; |
1799 | } |
1800 | } else { |
1801 | /* |
1802 | * Make sure the pointer we're copying from exists in the |
1803 | * current list of entries. Security precaution to prevent |
1804 | * copying of random kernel data. |
1805 | */ |
1806 | for (n = softn->ipf_nat_instances; n; n = n->nat_next) |
1807 | if (n == nat) |
1808 | break; |
1809 | if (n == NULL) { |
1810 | IPFERROR(60031); |
1811 | error = ESRCH; |
1812 | goto finished; |
1813 | } |
1814 | } |
1815 | ipn->ipn_next = nat->nat_next; |
1816 | |
1817 | /* |
1818 | * Copy the NAT structure. |
1819 | */ |
1820 | bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat)); |
1821 | |
1822 | /* |
1823 | * If we have a pointer to the NAT rule it belongs to, save that too. |
1824 | */ |
1825 | if (nat->nat_ptr != NULL) |
1826 | bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat, |
1827 | ipn->ipn_ipnat.in_size); |
1828 | |
1829 | /* |
1830 | * If we also know the NAT entry has an associated filter rule, |
1831 | * save that too. |
1832 | */ |
1833 | if (nat->nat_fr != NULL) |
1834 | bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr, |
1835 | sizeof(ipn->ipn_fr)); |
1836 | |
1837 | /* |
1838 | * Last but not least, if there is an application proxy session set |
1839 | * up for this NAT entry, then copy that out too, including any |
1840 | * private data saved along side it by the proxy. |
1841 | */ |
1842 | aps = nat->nat_aps; |
1843 | outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data); |
1844 | if (aps != NULL) { |
1845 | char *s; |
1846 | |
1847 | if (outsize < sizeof(*aps)) { |
1848 | IPFERROR(60032); |
1849 | error = ENOBUFS; |
1850 | goto finished; |
1851 | } |
1852 | |
1853 | s = ipn->ipn_data; |
1854 | bcopy((char *)aps, s, sizeof(*aps)); |
1855 | s += sizeof(*aps); |
1856 | outsize -= sizeof(*aps); |
1857 | if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz)) |
1858 | bcopy(aps->aps_data, s, aps->aps_psiz); |
1859 | else { |
1860 | IPFERROR(60033); |
1861 | error = ENOBUFS; |
1862 | } |
1863 | } |
1864 | if (error == 0) { |
1865 | if (getlock) { |
1866 | READ_ENTER(&softc->ipf_nat); |
1867 | getlock = 0; |
1868 | } |
1869 | error = ipf_outobjsz(softc, data, ipn, IPFOBJ_NATSAVE, |
1870 | ipns.ipn_dsize); |
1871 | } |
1872 | |
1873 | finished: |
1874 | if (getlock) { |
1875 | READ_ENTER(&softc->ipf_nat); |
1876 | } |
1877 | if (ipn != NULL) { |
1878 | KFREES(ipn, ipns.ipn_dsize); |
1879 | } |
1880 | return error; |
1881 | } |
1882 | |
1883 | |
1884 | /* ------------------------------------------------------------------------ */ |
1885 | /* Function: ipf_nat_putent */ |
1886 | /* Returns: int - 0 == success, != 0 is the error value. */ |
1887 | /* Parameters: softc(I) - pointer to soft context main structure */ |
1888 | /* data(I) - pointer to natget structure with NAT */ |
1889 | /* structure information to load into the kernel */ |
1890 | /* getlock(I) - flag indicating whether or not a write lock */ |
1891 | /* on is already held. */ |
1892 | /* */ |
1893 | /* Handle SIOCSTPUT. */ |
1894 | /* Loads a NAT table entry from user space, including a NAT rule, proxy and */ |
1895 | /* firewall rule data structures, if pointers to them indicate so. */ |
1896 | /* ------------------------------------------------------------------------ */ |
1897 | static int |
1898 | ipf_nat_putent(ipf_main_softc_t *softc, void *data, int getlock) |
1899 | { |
1900 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
1901 | nat_save_t *ipn, *ipnn; |
1902 | ap_session_t *aps; |
1903 | nat_t *n, *nat; |
1904 | frentry_t *fr; |
1905 | fr_info_t *fin; |
1906 | ipnat_t *in; |
1907 | int error; |
1908 | |
1909 | KMALLOC(ipn, nat_save_t *); |
1910 | if (ipn == NULL) |
1911 | return ENOMEM; |
1912 | error = ipf_inobj(softc, data, NULL, ipn, IPFOBJ_NATSAVE); |
1913 | if (error != 0) |
1914 | return error; |
1915 | |
1916 | /* |
1917 | * Initialise early because of code at junkput label. |
1918 | */ |
1919 | n = NULL; |
1920 | in = NULL; |
1921 | aps = NULL; |
1922 | nat = NULL; |
1923 | ipnn = NULL; |
1924 | fin = NULL; |
1925 | fr = NULL; |
1926 | |
1927 | /* |
1928 | * New entry, copy in the rest of the NAT entry if it's size is more |
1929 | * than just the nat_t structure. |
1930 | */ |
1931 | if (ipn->ipn_dsize > sizeof(*ipn)) { |
1932 | if (ipn->ipn_dsize > 81920) { |
1933 | IPFERROR(60034); |
1934 | error = ENOMEM; |
1935 | goto junkput; |
1936 | } |
1937 | |
1938 | KMALLOCS(ipnn, nat_save_t *, ipn->ipn_dsize); |
1939 | if (ipnn == NULL) { |
1940 | IPFERROR(60035); |
1941 | return ENOMEM; |
1942 | } |
1943 | |
1944 | bzero(ipnn, ipn->ipn_dsize); |
1945 | error = ipf_inobjsz(softc, data, ipnn, IPFOBJ_NATSAVE, |
1946 | ipn->ipn_dsize); |
1947 | if (error != 0) { |
1948 | goto junkput; |
1949 | } |
1950 | } else |
1951 | ipnn = ipn; |
1952 | |
1953 | KMALLOC(nat, nat_t *); |
1954 | if (nat == NULL) { |
1955 | IPFERROR(60037); |
1956 | error = ENOMEM; |
1957 | goto junkput; |
1958 | } |
1959 | |
1960 | bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat)); |
1961 | |
1962 | switch (nat->nat_v[0]) |
1963 | { |
1964 | case 4: |
1965 | #ifdef USE_IENT6 |
1966 | case 6 : |
1967 | #endif |
1968 | break; |
1969 | default : |
1970 | IPFERROR(60061); |
1971 | error = EPROTONOSUPPORT; |
1972 | goto junkput; |
1973 | /*NOTREACHED*/ |
1974 | } |
1975 | |
1976 | /* |
1977 | * Initialize all these so that ipf_nat_delete() doesn't cause a crash. |
1978 | */ |
1979 | bzero((char *)nat, offsetof(struct nat, nat_tqe)); |
1980 | nat->nat_tqe.tqe_pnext = NULL; |
1981 | nat->nat_tqe.tqe_next = NULL; |
1982 | nat->nat_tqe.tqe_ifq = NULL; |
1983 | nat->nat_tqe.tqe_parent = nat; |
1984 | |
1985 | /* |
1986 | * Restore the rule associated with this nat session |
1987 | */ |
1988 | in = ipnn->ipn_nat.nat_ptr; |
1989 | if (in != NULL) { |
1990 | KMALLOCS(in, ipnat_t *, ipnn->ipn_ipnat.in_size); |
1991 | nat->nat_ptr = in; |
1992 | if (in == NULL) { |
1993 | IPFERROR(60038); |
1994 | error = ENOMEM; |
1995 | goto junkput; |
1996 | } |
1997 | bcopy((char *)&ipnn->ipn_ipnat, (char *)in, |
1998 | ipnn->ipn_ipnat.in_size); |
1999 | in->in_use = 1; |
2000 | in->in_flags |= IPN_DELETE; |
2001 | |
2002 | ATOMIC_INC32(softn->ipf_nat_stats.ns_rules); |
2003 | |
2004 | if (ipf_nat_resolverule(softc, in) != 0) { |
2005 | IPFERROR(60039); |
2006 | error = ESRCH; |
2007 | goto junkput; |
2008 | } |
2009 | } |
2010 | |
2011 | /* |
2012 | * Check that the NAT entry doesn't already exist in the kernel. |
2013 | * |
2014 | * For NAT_OUTBOUND, we're lookup for a duplicate MAP entry. To do |
2015 | * this, we check to see if the inbound combination of addresses and |
2016 | * ports is already known. Similar logic is applied for NAT_INBOUND. |
2017 | * |
2018 | */ |
2019 | KMALLOC(fin, fr_info_t *); |
2020 | if (fin == NULL) { |
2021 | error = ENOMEM; |
2022 | goto junkput; |
2023 | } |
2024 | bzero(fin, sizeof(*fin)); |
2025 | fin->fin_v = nat->nat_v[0]; |
2026 | fin->fin_p = nat->nat_pr[0]; |
2027 | fin->fin_rev = nat->nat_rev; |
2028 | fin->fin_ifp = nat->nat_ifps[0]; |
2029 | fin->fin_data[0] = ntohs(nat->nat_ndport); |
2030 | fin->fin_data[1] = ntohs(nat->nat_nsport); |
2031 | |
2032 | switch (nat->nat_dir) |
2033 | { |
2034 | case NAT_OUTBOUND : |
2035 | case NAT_DIVERTOUT : |
2036 | if (getlock) { |
2037 | READ_ENTER(&softc->ipf_nat); |
2038 | } |
2039 | |
2040 | fin->fin_v = nat->nat_v[1]; |
2041 | if (nat->nat_v[1] == 4) { |
2042 | n = ipf_nat_inlookup(fin, nat->nat_flags, fin->fin_p, |
2043 | nat->nat_ndstip, nat->nat_nsrcip); |
2044 | #ifdef USE_INET6 |
2045 | } else if (nat->nat_v[1] == 6) { |
2046 | n = ipf_nat6_inlookup(fin, nat->nat_flags, fin->fin_p, |
2047 | &nat->nat_ndst6.in6, |
2048 | &nat->nat_nsrc6.in6); |
2049 | #endif |
2050 | } |
2051 | |
2052 | if (getlock) { |
2053 | RWLOCK_EXIT(&softc->ipf_nat); |
2054 | } |
2055 | if (n != NULL) { |
2056 | IPFERROR(60040); |
2057 | error = EEXIST; |
2058 | goto junkput; |
2059 | } |
2060 | break; |
2061 | |
2062 | case NAT_INBOUND : |
2063 | case NAT_DIVERTIN : |
2064 | if (getlock) { |
2065 | READ_ENTER(&softc->ipf_nat); |
2066 | } |
2067 | |
2068 | if (fin->fin_v == 4) { |
2069 | n = ipf_nat_outlookup(fin, nat->nat_flags, fin->fin_p, |
2070 | nat->nat_ndstip, |
2071 | nat->nat_nsrcip); |
2072 | #ifdef USE_INET6 |
2073 | } else if (fin->fin_v == 6) { |
2074 | n = ipf_nat6_outlookup(fin, nat->nat_flags, fin->fin_p, |
2075 | &nat->nat_ndst6.in6, |
2076 | &nat->nat_nsrc6.in6); |
2077 | #endif |
2078 | } |
2079 | |
2080 | if (getlock) { |
2081 | RWLOCK_EXIT(&softc->ipf_nat); |
2082 | } |
2083 | if (n != NULL) { |
2084 | IPFERROR(60041); |
2085 | error = EEXIST; |
2086 | goto junkput; |
2087 | } |
2088 | break; |
2089 | |
2090 | default : |
2091 | IPFERROR(60042); |
2092 | error = EINVAL; |
2093 | goto junkput; |
2094 | } |
2095 | |
2096 | /* |
2097 | * Restore ap_session_t structure. Include the private data allocated |
2098 | * if it was there. |
2099 | */ |
2100 | aps = nat->nat_aps; |
2101 | if (aps != NULL) { |
2102 | KMALLOC(aps, ap_session_t *); |
2103 | nat->nat_aps = aps; |
2104 | if (aps == NULL) { |
2105 | IPFERROR(60043); |
2106 | error = ENOMEM; |
2107 | goto junkput; |
2108 | } |
2109 | bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps)); |
2110 | if (in != NULL) |
2111 | aps->aps_apr = in->in_apr; |
2112 | else |
2113 | aps->aps_apr = NULL; |
2114 | if (aps->aps_psiz != 0) { |
2115 | if (aps->aps_psiz > 81920) { |
2116 | IPFERROR(60044); |
2117 | error = ENOMEM; |
2118 | goto junkput; |
2119 | } |
2120 | KMALLOCS(aps->aps_data, void *, aps->aps_psiz); |
2121 | if (aps->aps_data == NULL) { |
2122 | IPFERROR(60045); |
2123 | error = ENOMEM; |
2124 | goto junkput; |
2125 | } |
2126 | bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data, |
2127 | aps->aps_psiz); |
2128 | } else { |
2129 | aps->aps_psiz = 0; |
2130 | aps->aps_data = NULL; |
2131 | } |
2132 | } |
2133 | |
2134 | /* |
2135 | * If there was a filtering rule associated with this entry then |
2136 | * build up a new one. |
2137 | */ |
2138 | fr = nat->nat_fr; |
2139 | if (fr != NULL) { |
2140 | if ((nat->nat_flags & SI_NEWFR) != 0) { |
2141 | KMALLOC(fr, frentry_t *); |
2142 | nat->nat_fr = fr; |
2143 | if (fr == NULL) { |
2144 | IPFERROR(60046); |
2145 | error = ENOMEM; |
2146 | goto junkput; |
2147 | } |
2148 | ipnn->ipn_nat.nat_fr = fr; |
2149 | fr->fr_ref = 1; |
2150 | (void) ipf_outobj(softc, data, ipnn, IPFOBJ_NATSAVE); |
2151 | bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr)); |
2152 | |
2153 | fr->fr_ref = 1; |
2154 | fr->fr_dsize = 0; |
2155 | fr->fr_data = NULL; |
2156 | fr->fr_type = FR_T_NONE; |
2157 | |
2158 | MUTEX_NUKE(&fr->fr_lock); |
2159 | MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock" ); |
2160 | } else { |
2161 | if (getlock) { |
2162 | READ_ENTER(&softc->ipf_nat); |
2163 | } |
2164 | for (n = softn->ipf_nat_instances; n; n = n->nat_next) |
2165 | if (n->nat_fr == fr) |
2166 | break; |
2167 | |
2168 | if (n != NULL) { |
2169 | MUTEX_ENTER(&fr->fr_lock); |
2170 | fr->fr_ref++; |
2171 | MUTEX_EXIT(&fr->fr_lock); |
2172 | } |
2173 | if (getlock) { |
2174 | RWLOCK_EXIT(&softc->ipf_nat); |
2175 | } |
2176 | |
2177 | if (n == NULL) { |
2178 | IPFERROR(60047); |
2179 | error = ESRCH; |
2180 | goto junkput; |
2181 | } |
2182 | } |
2183 | } |
2184 | |
2185 | if (ipnn != ipn) { |
2186 | KFREES(ipnn, ipn->ipn_dsize); |
2187 | ipnn = NULL; |
2188 | } |
2189 | |
2190 | if (getlock) { |
2191 | WRITE_ENTER(&softc->ipf_nat); |
2192 | } |
2193 | |
2194 | if (fin->fin_v == 4) |
2195 | error = ipf_nat_finalise(fin, nat); |
2196 | #ifdef USE_INET6 |
2197 | else |
2198 | error = ipf_nat6_finalise(fin, nat); |
2199 | #endif |
2200 | |
2201 | if (getlock) { |
2202 | RWLOCK_EXIT(&softc->ipf_nat); |
2203 | } |
2204 | |
2205 | if (error == 0) |
2206 | return 0; |
2207 | |
2208 | IPFERROR(60048); |
2209 | error = ENOMEM; |
2210 | |
2211 | junkput: |
2212 | if (fin != NULL) |
2213 | KFREE(fin); |
2214 | if (fr != NULL) { |
2215 | (void) ipf_derefrule(softc, &fr); |
2216 | } |
2217 | |
2218 | if ((ipnn != NULL) && (ipnn != ipn)) { |
2219 | KFREES(ipnn, ipn->ipn_dsize); |
2220 | } |
2221 | if (ipn != NULL) |
2222 | KFREE(ipn); |
2223 | if (nat != NULL) { |
2224 | if (aps != NULL) { |
2225 | if (aps->aps_data != NULL) { |
2226 | KFREES(aps->aps_data, aps->aps_psiz); |
2227 | } |
2228 | KFREE(aps); |
2229 | } |
2230 | if (in != NULL) { |
2231 | if (in->in_apr) |
2232 | ipf_proxy_deref(in->in_apr); |
2233 | KFREES(in, in->in_size); |
2234 | } |
2235 | KFREE(nat); |
2236 | } |
2237 | return error; |
2238 | } |
2239 | |
2240 | |
2241 | /* ------------------------------------------------------------------------ */ |
2242 | /* Function: ipf_nat_delete */ |
2243 | /* Returns: Nil */ |
2244 | /* Parameters: softc(I) - pointer to soft context main structure */ |
2245 | /* nat(I) - pointer to NAT structure to delete */ |
2246 | /* logtype(I) - type of LOG record to create before deleting */ |
2247 | /* Write Lock: ipf_nat */ |
2248 | /* */ |
2249 | /* Delete a nat entry from the various lists and table. If NAT logging is */ |
2250 | /* enabled then generate a NAT log record for this event. */ |
2251 | /* ------------------------------------------------------------------------ */ |
2252 | void |
2253 | ipf_nat_delete(ipf_main_softc_t *softc, struct nat *nat, int logtype) |
2254 | { |
2255 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
2256 | int madeorphan = 0, removed = 0; |
2257 | u_int bkt; |
2258 | nat_stat_side_t *nss; |
2259 | struct ipnat *ipn; |
2260 | |
2261 | if (logtype != 0 && softn->ipf_nat_logging != 0) |
2262 | ipf_nat_log(softc, softn, nat, logtype); |
2263 | |
2264 | /* |
2265 | * Take it as a general indication that all the pointers are set if |
2266 | * nat_pnext is set. |
2267 | */ |
2268 | if (nat->nat_pnext != NULL) { |
2269 | removed = 1; |
2270 | |
2271 | bkt = nat->nat_hv[0] % softn->ipf_nat_table_sz; |
2272 | nss = &softn->ipf_nat_stats.ns_side[0]; |
2273 | ASSERT(nss->ns_bucketlen[bkt] > 0); |
2274 | nss->ns_bucketlen[bkt]--; |
2275 | if (nss->ns_bucketlen[bkt] == 0) { |
2276 | nss->ns_inuse--; |
2277 | } |
2278 | |
2279 | bkt = nat->nat_hv[1] % softn->ipf_nat_table_sz; |
2280 | nss = &softn->ipf_nat_stats.ns_side[1]; |
2281 | ASSERT(nss->ns_bucketlen[bkt] > 0); |
2282 | nss->ns_bucketlen[bkt]--; |
2283 | if (nss->ns_bucketlen[bkt] == 0) { |
2284 | nss->ns_inuse--; |
2285 | } |
2286 | |
2287 | *nat->nat_pnext = nat->nat_next; |
2288 | if (nat->nat_next != NULL) { |
2289 | nat->nat_next->nat_pnext = nat->nat_pnext; |
2290 | nat->nat_next = NULL; |
2291 | } |
2292 | nat->nat_pnext = NULL; |
2293 | |
2294 | *nat->nat_phnext[0] = nat->nat_hnext[0]; |
2295 | if (nat->nat_hnext[0] != NULL) { |
2296 | nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; |
2297 | nat->nat_hnext[0] = NULL; |
2298 | } |
2299 | nat->nat_phnext[0] = NULL; |
2300 | |
2301 | *nat->nat_phnext[1] = nat->nat_hnext[1]; |
2302 | if (nat->nat_hnext[1] != NULL) { |
2303 | nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; |
2304 | nat->nat_hnext[1] = NULL; |
2305 | } |
2306 | nat->nat_phnext[1] = NULL; |
2307 | |
2308 | if ((nat->nat_flags & SI_WILDP) != 0) { |
2309 | ATOMIC_DEC32(softn->ipf_nat_stats.ns_wilds); |
2310 | } |
2311 | madeorphan = 1; |
2312 | } |
2313 | |
2314 | if (nat->nat_me != NULL) { |
2315 | *nat->nat_me = NULL; |
2316 | nat->nat_me = NULL; |
2317 | nat->nat_ref--; |
2318 | ASSERT(nat->nat_ref >= 0); |
2319 | } |
2320 | |
2321 | if (nat->nat_tqe.tqe_ifq != NULL) { |
2322 | /* |
2323 | * No call to ipf_freetimeoutqueue() is made here, they are |
2324 | * garbage collected in ipf_nat_expire(). |
2325 | */ |
2326 | (void) ipf_deletequeueentry(&nat->nat_tqe); |
2327 | } |
2328 | |
2329 | if (nat->nat_sync) { |
2330 | ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); |
2331 | nat->nat_sync = NULL; |
2332 | } |
2333 | |
2334 | if (logtype == NL_EXPIRE) |
2335 | softn->ipf_nat_stats.ns_expire++; |
2336 | |
2337 | MUTEX_ENTER(&nat->nat_lock); |
2338 | /* |
2339 | * NL_DESTROY should only be passed in when we've got nat_ref >= 2. |
2340 | * This happens when a nat'd packet is blocked and we want to throw |
2341 | * away the NAT session. |
2342 | */ |
2343 | if (logtype == NL_DESTROY) { |
2344 | if (nat->nat_ref > 2) { |
2345 | nat->nat_ref -= 2; |
2346 | MUTEX_EXIT(&nat->nat_lock); |
2347 | if (removed) |
2348 | softn->ipf_nat_stats.ns_orphans++; |
2349 | return; |
2350 | } |
2351 | } else if (nat->nat_ref > 1) { |
2352 | nat->nat_ref--; |
2353 | MUTEX_EXIT(&nat->nat_lock); |
2354 | if (madeorphan == 1) |
2355 | softn->ipf_nat_stats.ns_orphans++; |
2356 | return; |
2357 | } |
2358 | ASSERT(nat->nat_ref >= 0); |
2359 | MUTEX_EXIT(&nat->nat_lock); |
2360 | |
2361 | nat->nat_ref = 0; |
2362 | |
2363 | if (madeorphan == 0) |
2364 | softn->ipf_nat_stats.ns_orphans--; |
2365 | |
2366 | /* |
2367 | * At this point, nat_ref can be either 0 or -1 |
2368 | */ |
2369 | softn->ipf_nat_stats.ns_proto[nat->nat_pr[0]]--; |
2370 | |
2371 | if (nat->nat_fr != NULL) { |
2372 | (void) ipf_derefrule(softc, &nat->nat_fr); |
2373 | } |
2374 | |
2375 | if (nat->nat_hm != NULL) { |
2376 | ipf_nat_hostmapdel(softc, &nat->nat_hm); |
2377 | } |
2378 | |
2379 | /* |
2380 | * If there is an active reference from the nat entry to its parent |
2381 | * rule, decrement the rule's reference count and free it too if no |
2382 | * longer being used. |
2383 | */ |
2384 | ipn = nat->nat_ptr; |
2385 | nat->nat_ptr = NULL; |
2386 | |
2387 | if (ipn != NULL) { |
2388 | ipn->in_space++; |
2389 | ipf_nat_rule_deref(softc, &ipn); |
2390 | } |
2391 | |
2392 | if (nat->nat_aps != NULL) { |
2393 | ipf_proxy_free(softc, nat->nat_aps); |
2394 | nat->nat_aps = NULL; |
2395 | } |
2396 | |
2397 | MUTEX_DESTROY(&nat->nat_lock); |
2398 | |
2399 | softn->ipf_nat_stats.ns_active--; |
2400 | |
2401 | /* |
2402 | * If there's a fragment table entry too for this nat entry, then |
2403 | * dereference that as well. This is after nat_lock is released |
2404 | * because of Tru64. |
2405 | */ |
2406 | ipf_frag_natforget(softc, (void *)nat); |
2407 | |
2408 | KFREE(nat); |
2409 | } |
2410 | |
2411 | |
2412 | /* ------------------------------------------------------------------------ */ |
2413 | /* Function: ipf_nat_flushtable */ |
2414 | /* Returns: int - number of NAT rules deleted */ |
2415 | /* Parameters: softc(I) - pointer to soft context main structure */ |
2416 | /* softn(I) - pointer to NAT context structure */ |
2417 | /* Write Lock: ipf_nat */ |
2418 | /* */ |
2419 | /* Deletes all currently active NAT sessions. In deleting each NAT entry a */ |
2420 | /* log record should be emitted in ipf_nat_delete() if NAT logging is */ |
2421 | /* enabled. */ |
2422 | /* ------------------------------------------------------------------------ */ |
2423 | /* |
2424 | * nat_flushtable - clear the NAT table of all mapping entries. |
2425 | */ |
2426 | static int |
2427 | ipf_nat_flushtable(ipf_main_softc_t *softc, ipf_nat_softc_t *softn) |
2428 | { |
2429 | nat_t *nat; |
2430 | int j = 0; |
2431 | |
2432 | /* |
2433 | * ALL NAT mappings deleted, so lets just make the deletions |
2434 | * quicker. |
2435 | */ |
2436 | if (softn->ipf_nat_table[0] != NULL) |
2437 | bzero((char *)softn->ipf_nat_table[0], |
2438 | sizeof(softn->ipf_nat_table[0]) * |
2439 | softn->ipf_nat_table_sz); |
2440 | if (softn->ipf_nat_table[1] != NULL) |
2441 | bzero((char *)softn->ipf_nat_table[1], |
2442 | sizeof(softn->ipf_nat_table[1]) * |
2443 | softn->ipf_nat_table_sz); |
2444 | |
2445 | while ((nat = softn->ipf_nat_instances) != NULL) { |
2446 | ipf_nat_delete(softc, nat, NL_FLUSH); |
2447 | j++; |
2448 | } |
2449 | |
2450 | return j; |
2451 | } |
2452 | |
2453 | |
2454 | /* ------------------------------------------------------------------------ */ |
2455 | /* Function: ipf_nat_clearlist */ |
2456 | /* Returns: int - number of NAT/RDR rules deleted */ |
2457 | /* Parameters: softc(I) - pointer to soft context main structure */ |
2458 | /* softn(I) - pointer to NAT context structure */ |
2459 | /* */ |
2460 | /* Delete all rules in the current list of rules. There is nothing elegant */ |
2461 | /* about this cleanup: simply free all entries on the list of rules and */ |
2462 | /* clear out the tables used for hashed NAT rule lookups. */ |
2463 | /* ------------------------------------------------------------------------ */ |
2464 | static int |
2465 | ipf_nat_clearlist(ipf_main_softc_t *softc, ipf_nat_softc_t *softn) |
2466 | { |
2467 | ipnat_t *n; |
2468 | int i = 0; |
2469 | |
2470 | if (softn->ipf_nat_map_rules != NULL) { |
2471 | bzero((char *)softn->ipf_nat_map_rules, |
2472 | sizeof(*softn->ipf_nat_map_rules) * |
2473 | softn->ipf_nat_maprules_sz); |
2474 | } |
2475 | if (softn->ipf_nat_rdr_rules != NULL) { |
2476 | bzero((char *)softn->ipf_nat_rdr_rules, |
2477 | sizeof(*softn->ipf_nat_rdr_rules) * |
2478 | softn->ipf_nat_rdrrules_sz); |
2479 | } |
2480 | |
2481 | while ((n = softn->ipf_nat_list) != NULL) { |
2482 | ipf_nat_delrule(softc, softn, n, 0); |
2483 | i++; |
2484 | } |
2485 | #if SOLARIS && !defined(INSTANCES) |
2486 | pfil_delayed_copy = 1; |
2487 | #endif |
2488 | return i; |
2489 | } |
2490 | |
2491 | |
2492 | /* ------------------------------------------------------------------------ */ |
2493 | /* Function: ipf_nat_delrule */ |
2494 | /* Returns: Nil */ |
2495 | /* Parameters: softc(I) - pointer to soft context main structure */ |
2496 | /* softn(I) - pointer to NAT context structure */ |
2497 | /* np(I) - pointer to NAT rule to delete */ |
2498 | /* purge(I) - 1 == allow purge, 0 == prevent purge */ |
2499 | /* Locks: WRITE(ipf_nat) */ |
2500 | /* */ |
2501 | /* Preventing "purge" from occuring is allowed because when all of the NAT */ |
2502 | /* rules are being removed, allowing the "purge" to walk through the list */ |
2503 | /* of NAT sessions, possibly multiple times, would be a large performance */ |
2504 | /* hit, on the order of O(N^2). */ |
2505 | /* ------------------------------------------------------------------------ */ |
2506 | static void |
2507 | ipf_nat_delrule(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, ipnat_t *np, |
2508 | int purge) |
2509 | { |
2510 | |
2511 | if (np->in_pnext != NULL) { |
2512 | *np->in_pnext = np->in_next; |
2513 | if (np->in_next != NULL) |
2514 | np->in_next->in_pnext = np->in_pnext; |
2515 | if (softn->ipf_nat_list_tail == &np->in_next) |
2516 | softn->ipf_nat_list_tail = np->in_pnext; |
2517 | } |
2518 | |
2519 | if ((purge == 1) && ((np->in_flags & IPN_PURGE) != 0)) { |
2520 | nat_t *next; |
2521 | nat_t *nat; |
2522 | |
2523 | for (next = softn->ipf_nat_instances; (nat = next) != NULL;) { |
2524 | next = nat->nat_next; |
2525 | if (nat->nat_ptr == np) |
2526 | ipf_nat_delete(softc, nat, NL_PURGE); |
2527 | } |
2528 | } |
2529 | |
2530 | if ((np->in_flags & IPN_DELETE) == 0) { |
2531 | if (np->in_redir & NAT_REDIRECT) { |
2532 | switch (np->in_v[0]) |
2533 | { |
2534 | case 4 : |
2535 | ipf_nat_delrdr(softn, np); |
2536 | break; |
2537 | #ifdef USE_INET6 |
2538 | case 6 : |
2539 | ipf_nat6_delrdr(softn, np); |
2540 | break; |
2541 | #endif |
2542 | } |
2543 | } |
2544 | if (np->in_redir & (NAT_MAPBLK|NAT_MAP)) { |
2545 | switch (np->in_v[0]) |
2546 | { |
2547 | case 4 : |
2548 | ipf_nat_delmap(softn, np); |
2549 | break; |
2550 | #ifdef USE_INET6 |
2551 | case 6 : |
2552 | ipf_nat6_delmap(softn, np); |
2553 | break; |
2554 | #endif |
2555 | } |
2556 | } |
2557 | } |
2558 | |
2559 | np->in_flags |= IPN_DELETE; |
2560 | ipf_nat_rule_deref(softc, &np); |
2561 | } |
2562 | |
2563 | |
2564 | /* ------------------------------------------------------------------------ */ |
2565 | /* Function: ipf_nat_newmap */ |
2566 | /* Returns: int - -1 == error, 0 == success */ |
2567 | /* Parameters: fin(I) - pointer to packet information */ |
2568 | /* nat(I) - pointer to NAT entry */ |
2569 | /* ni(I) - pointer to structure with misc. information needed */ |
2570 | /* to create new NAT entry. */ |
2571 | /* */ |
2572 | /* Given an empty NAT structure, populate it with new information about a */ |
2573 | /* new NAT session, as defined by the matching NAT rule. */ |
2574 | /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ |
2575 | /* to the new IP address for the translation. */ |
2576 | /* ------------------------------------------------------------------------ */ |
2577 | static int |
2578 | ipf_nat_newmap(fr_info_t *fin, nat_t *nat, natinfo_t *ni) |
2579 | { |
2580 | ipf_main_softc_t *softc = fin->fin_main_soft; |
2581 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
2582 | u_short st_port, dport, sport, port, sp, dp; |
2583 | struct in_addr in, inb; |
2584 | hostmap_t *hm; |
2585 | u_32_t flags; |
2586 | u_32_t st_ip; |
2587 | ipnat_t *np; |
2588 | nat_t *natl; |
2589 | int l; |
2590 | |
2591 | /* |
2592 | * If it's an outbound packet which doesn't match any existing |
2593 | * record, then create a new port |
2594 | */ |
2595 | l = 0; |
2596 | hm = NULL; |
2597 | np = ni->nai_np; |
2598 | st_ip = np->in_snip; |
2599 | st_port = np->in_spnext; |
2600 | flags = nat->nat_flags; |
2601 | |
2602 | if (flags & IPN_ICMPQUERY) { |
2603 | sport = fin->fin_data[1]; |
2604 | dport = 0; |
2605 | } else { |
2606 | sport = htons(fin->fin_data[0]); |
2607 | dport = htons(fin->fin_data[1]); |
2608 | } |
2609 | |
2610 | /* |
2611 | * Do a loop until we either run out of entries to try or we find |
2612 | * a NAT mapping that isn't currently being used. This is done |
2613 | * because the change to the source is not (usually) being fixed. |
2614 | */ |
2615 | do { |
2616 | port = 0; |
2617 | in.s_addr = htonl(np->in_snip); |
2618 | if (l == 0) { |
2619 | /* |
2620 | * Check to see if there is an existing NAT |
2621 | * setup for this IP address pair. |
2622 | */ |
2623 | hm = ipf_nat_hostmap(softn, np, fin->fin_src, |
2624 | fin->fin_dst, in, 0); |
2625 | if (hm != NULL) |
2626 | in.s_addr = hm->hm_nsrcip.s_addr; |
2627 | } else if ((l == 1) && (hm != NULL)) { |
2628 | ipf_nat_hostmapdel(softc, &hm); |
2629 | } |
2630 | in.s_addr = ntohl(in.s_addr); |
2631 | |
2632 | nat->nat_hm = hm; |
2633 | |
2634 | if ((np->in_nsrcmsk == 0xffffffff) && (np->in_spnext == 0)) { |
2635 | if (l > 0) { |
2636 | NBUMPSIDEX(1, ns_exhausted, ns_exhausted_1); |
2637 | return -1; |
2638 | } |
2639 | } |
2640 | |
2641 | if (np->in_redir == NAT_BIMAP && |
2642 | np->in_osrcmsk == np->in_nsrcmsk) { |
2643 | /* |
2644 | * map the address block in a 1:1 fashion |
2645 | */ |
2646 | in.s_addr = np->in_nsrcaddr; |
2647 | in.s_addr |= fin->fin_saddr & ~np->in_osrcmsk; |
2648 | in.s_addr = ntohl(in.s_addr); |
2649 | |
2650 | } else if (np->in_redir & NAT_MAPBLK) { |
2651 | if ((l >= np->in_ppip) || ((l > 0) && |
2652 | !(flags & IPN_TCPUDP))) { |
2653 | NBUMPSIDEX(1, ns_exhausted, ns_exhausted_2); |
2654 | return -1; |
2655 | } |
2656 | /* |
2657 | * map-block - Calculate destination address. |
2658 | */ |
2659 | in.s_addr = ntohl(fin->fin_saddr); |
2660 | in.s_addr &= ntohl(~np->in_osrcmsk); |
2661 | inb.s_addr = in.s_addr; |
2662 | in.s_addr /= np->in_ippip; |
2663 | in.s_addr &= ntohl(~np->in_nsrcmsk); |
2664 | in.s_addr += ntohl(np->in_nsrcaddr); |
2665 | /* |
2666 | * Calculate destination port. |
2667 | */ |
2668 | if ((flags & IPN_TCPUDP) && |
2669 | (np->in_ppip != 0)) { |
2670 | port = ntohs(sport) + l; |
2671 | port %= np->in_ppip; |
2672 | port += np->in_ppip * |
2673 | (inb.s_addr % np->in_ippip); |
2674 | port += MAPBLK_MINPORT; |
2675 | port = htons(port); |
2676 | } |
2677 | |
2678 | } else if ((np->in_nsrcaddr == 0) && |
2679 | (np->in_nsrcmsk == 0xffffffff)) { |
2680 | i6addr_t in6; |
2681 | |
2682 | /* |
2683 | * 0/32 - use the interface's IP address. |
2684 | */ |
2685 | if ((l > 0) || |
2686 | ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp, |
2687 | &in6, NULL) == -1) { |
2688 | NBUMPSIDEX(1, ns_new_ifpaddr, ns_new_ifpaddr_1); |
2689 | return -1; |
2690 | } |
2691 | in.s_addr = ntohl(in6.in4.s_addr); |
2692 | |
2693 | } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) { |
2694 | /* |
2695 | * 0/0 - use the original source address/port. |
2696 | */ |
2697 | if (l > 0) { |
2698 | NBUMPSIDEX(1, ns_exhausted, ns_exhausted_3); |
2699 | return -1; |
2700 | } |
2701 | in.s_addr = ntohl(fin->fin_saddr); |
2702 | |
2703 | } else if ((np->in_nsrcmsk != 0xffffffff) && |
2704 | (np->in_spnext == 0) && ((l > 0) || (hm == NULL))) |
2705 | np->in_snip++; |
2706 | |
2707 | natl = NULL; |
2708 | |
2709 | if ((flags & IPN_TCPUDP) && |
2710 | ((np->in_redir & NAT_MAPBLK) == 0) && |
2711 | (np->in_flags & IPN_AUTOPORTMAP)) { |
2712 | /* |
2713 | * "ports auto" (without map-block) |
2714 | */ |
2715 | if ((l > 0) && np->in_ppip && (l % np->in_ppip == 0)) { |
2716 | if (l > np->in_space) { |
2717 | return -1; |
2718 | } else if ((l > np->in_ppip) && |
2719 | np->in_nsrcmsk != 0xffffffff) |
2720 | np->in_snip++; |
2721 | } |
2722 | if (np->in_ppip != 0) { |
2723 | port = ntohs(sport); |
2724 | port += (l % np->in_ppip); |
2725 | port %= np->in_ppip; |
2726 | port += np->in_ppip * |
2727 | (ntohl(fin->fin_saddr) % |
2728 | np->in_ippip); |
2729 | port += MAPBLK_MINPORT; |
2730 | port = htons(port); |
2731 | } |
2732 | |
2733 | } else if (((np->in_redir & NAT_MAPBLK) == 0) && |
2734 | (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) { |
2735 | /* |
2736 | * Standard port translation. Select next port. |
2737 | */ |
2738 | if (np->in_flags & IPN_SEQUENTIAL) { |
2739 | port = np->in_spnext; |
2740 | } else { |
2741 | port = ipf_random() % (np->in_spmax - |
2742 | np->in_spmin + 1); |
2743 | port += np->in_spmin; |
2744 | } |
2745 | port = htons(port); |
2746 | np->in_spnext++; |
2747 | |
2748 | if (np->in_spnext > np->in_spmax) { |
2749 | np->in_spnext = np->in_spmin; |
2750 | if (np->in_nsrcmsk != 0xffffffff) |
2751 | np->in_snip++; |
2752 | } |
2753 | } |
2754 | |
2755 | if (np->in_flags & IPN_SIPRANGE) { |
2756 | if (np->in_snip > ntohl(np->in_nsrcmsk)) |
2757 | np->in_snip = ntohl(np->in_nsrcaddr); |
2758 | } else { |
2759 | if ((np->in_nsrcmsk != 0xffffffff) && |
2760 | ((np->in_snip + 1) & ntohl(np->in_nsrcmsk)) > |
2761 | ntohl(np->in_nsrcaddr)) |
2762 | np->in_snip = ntohl(np->in_nsrcaddr) + 1; |
2763 | } |
2764 | |
2765 | if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) |
2766 | port = sport; |
2767 | |
2768 | /* |
2769 | * Here we do a lookup of the connection as seen from |
2770 | * the outside. If an IP# pair already exists, try |
2771 | * again. So if you have A->B becomes C->B, you can |
2772 | * also have D->E become C->E but not D->B causing |
2773 | * another C->B. Also take protocol and ports into |
2774 | * account when determining whether a pre-existing |
2775 | * NAT setup will cause an external conflict where |
2776 | * this is appropriate. |
2777 | */ |
2778 | inb.s_addr = htonl(in.s_addr); |
2779 | sp = fin->fin_data[0]; |
2780 | dp = fin->fin_data[1]; |
2781 | fin->fin_data[0] = fin->fin_data[1]; |
2782 | fin->fin_data[1] = ntohs(port); |
2783 | natl = ipf_nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), |
2784 | (u_int)fin->fin_p, fin->fin_dst, inb); |
2785 | fin->fin_data[0] = sp; |
2786 | fin->fin_data[1] = dp; |
2787 | |
2788 | /* |
2789 | * Has the search wrapped around and come back to the |
2790 | * start ? |
2791 | */ |
2792 | if ((natl != NULL) && |
2793 | (np->in_spnext != 0) && (st_port == np->in_spnext) && |
2794 | (np->in_snip != 0) && (st_ip == np->in_snip)) { |
2795 | NBUMPSIDED(1, ns_wrap); |
2796 | return -1; |
2797 | } |
2798 | l++; |
2799 | } while (natl != NULL); |
2800 | |
2801 | /* Setup the NAT table */ |
2802 | nat->nat_osrcip = fin->fin_src; |
2803 | nat->nat_nsrcaddr = htonl(in.s_addr); |
2804 | nat->nat_odstip = fin->fin_dst; |
2805 | nat->nat_ndstip = fin->fin_dst; |
2806 | if (nat->nat_hm == NULL) |
2807 | nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src, |
2808 | fin->fin_dst, nat->nat_nsrcip, |
2809 | 0); |
2810 | |
2811 | if (flags & IPN_TCPUDP) { |
2812 | nat->nat_osport = sport; |
2813 | nat->nat_nsport = port; /* sport */ |
2814 | nat->nat_odport = dport; |
2815 | nat->nat_ndport = dport; |
2816 | ((tcphdr_t *)fin->fin_dp)->th_sport = port; |
2817 | } else if (flags & IPN_ICMPQUERY) { |
2818 | nat->nat_oicmpid = fin->fin_data[1]; |
2819 | ((icmphdr_t *)fin->fin_dp)->icmp_id = port; |
2820 | nat->nat_nicmpid = port; |
2821 | } |
2822 | return 0; |
2823 | } |
2824 | |
2825 | |
2826 | /* ------------------------------------------------------------------------ */ |
2827 | /* Function: ipf_nat_newrdr */ |
2828 | /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ |
2829 | /* allow rule to be moved if IPN_ROUNDR is set. */ |
2830 | /* Parameters: fin(I) - pointer to packet information */ |
2831 | /* nat(I) - pointer to NAT entry */ |
2832 | /* ni(I) - pointer to structure with misc. information needed */ |
2833 | /* to create new NAT entry. */ |
2834 | /* */ |
2835 | /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ |
2836 | /* to the new IP address for the translation. */ |
2837 | /* ------------------------------------------------------------------------ */ |
2838 | static int |
2839 | ipf_nat_newrdr(fr_info_t *fin, nat_t *nat, natinfo_t *ni) |
2840 | { |
2841 | ipf_main_softc_t *softc = fin->fin_main_soft; |
2842 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
2843 | u_short nport, dport, sport; |
2844 | struct in_addr in, inb; |
2845 | u_short sp, dp; |
2846 | hostmap_t *hm; |
2847 | u_32_t flags; |
2848 | ipnat_t *np; |
2849 | nat_t *natl; |
2850 | int move; |
2851 | |
2852 | move = 1; |
2853 | hm = NULL; |
2854 | in.s_addr = 0; |
2855 | np = ni->nai_np; |
2856 | flags = nat->nat_flags; |
2857 | |
2858 | if (flags & IPN_ICMPQUERY) { |
2859 | dport = fin->fin_data[1]; |
2860 | sport = 0; |
2861 | } else { |
2862 | sport = htons(fin->fin_data[0]); |
2863 | dport = htons(fin->fin_data[1]); |
2864 | } |
2865 | |
2866 | /* TRACE sport, dport */ |
2867 | |
2868 | |
2869 | /* |
2870 | * If the matching rule has IPN_STICKY set, then we want to have the |
2871 | * same rule kick in as before. Why would this happen? If you have |
2872 | * a collection of rdr rules with "round-robin sticky", the current |
2873 | * packet might match a different one to the previous connection but |
2874 | * we want the same destination to be used. |
2875 | */ |
2876 | if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) && |
2877 | ((np->in_flags & IPN_STICKY) != 0)) { |
2878 | hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, fin->fin_dst, |
2879 | in, (u_32_t)dport); |
2880 | if (hm != NULL) { |
2881 | in.s_addr = ntohl(hm->hm_ndstip.s_addr); |
2882 | np = hm->hm_ipnat; |
2883 | ni->nai_np = np; |
2884 | move = 0; |
2885 | ipf_nat_hostmapdel(softc, &hm); |
2886 | } |
2887 | } |
2888 | |
2889 | /* |
2890 | * Otherwise, it's an inbound packet. Most likely, we don't |
2891 | * want to rewrite source ports and source addresses. Instead, |
2892 | * we want to rewrite to a fixed internal address and fixed |
2893 | * internal port. |
2894 | */ |
2895 | if (np->in_flags & IPN_SPLIT) { |
2896 | in.s_addr = np->in_dnip; |
2897 | inb.s_addr = htonl(in.s_addr); |
2898 | |
2899 | if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { |
2900 | hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, |
2901 | fin->fin_dst, inb, (u_32_t)dport); |
2902 | if (hm != NULL) { |
2903 | in.s_addr = hm->hm_ndstip.s_addr; |
2904 | move = 0; |
2905 | } |
2906 | } |
2907 | |
2908 | if (hm == NULL || hm->hm_ref == 1) { |
2909 | if (np->in_ndstaddr == htonl(in.s_addr)) { |
2910 | np->in_dnip = ntohl(np->in_ndstmsk); |
2911 | move = 0; |
2912 | } else { |
2913 | np->in_dnip = ntohl(np->in_ndstaddr); |
2914 | } |
2915 | } |
2916 | if (hm != NULL) |
2917 | ipf_nat_hostmapdel(softc, &hm); |
2918 | |
2919 | } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) { |
2920 | i6addr_t in6; |
2921 | |
2922 | /* |
2923 | * 0/32 - use the interface's IP address. |
2924 | */ |
2925 | if (ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp, |
2926 | &in6, NULL) == -1) { |
2927 | NBUMPSIDEX(0, ns_new_ifpaddr, ns_new_ifpaddr_2); |
2928 | return -1; |
2929 | } |
2930 | in.s_addr = ntohl(in6.in4.s_addr); |
2931 | |
2932 | } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk== 0)) { |
2933 | /* |
2934 | * 0/0 - use the original destination address/port. |
2935 | */ |
2936 | in.s_addr = ntohl(fin->fin_daddr); |
2937 | |
2938 | } else if (np->in_redir == NAT_BIMAP && |
2939 | np->in_ndstmsk == np->in_odstmsk) { |
2940 | /* |
2941 | * map the address block in a 1:1 fashion |
2942 | */ |
2943 | in.s_addr = np->in_ndstaddr; |
2944 | in.s_addr |= fin->fin_daddr & ~np->in_ndstmsk; |
2945 | in.s_addr = ntohl(in.s_addr); |
2946 | } else { |
2947 | in.s_addr = ntohl(np->in_ndstaddr); |
2948 | } |
2949 | |
2950 | if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) |
2951 | nport = dport; |
2952 | else { |
2953 | /* |
2954 | * Whilst not optimized for the case where |
2955 | * pmin == pmax, the gain is not significant. |
2956 | */ |
2957 | if (((np->in_flags & IPN_FIXEDDPORT) == 0) && |
2958 | (np->in_odport != np->in_dtop)) { |
2959 | nport = ntohs(dport) - np->in_odport + np->in_dpmax; |
2960 | nport = htons(nport); |
2961 | } else { |
2962 | nport = htons(np->in_dpnext); |
2963 | np->in_dpnext++; |
2964 | if (np->in_dpnext > np->in_dpmax) |
2965 | np->in_dpnext = np->in_dpmin; |
2966 | } |
2967 | } |
2968 | |
2969 | /* |
2970 | * When the redirect-to address is set to 0.0.0.0, just |
2971 | * assume a blank `forwarding' of the packet. We don't |
2972 | * setup any translation for this either. |
2973 | */ |
2974 | if (in.s_addr == 0) { |
2975 | if (nport == dport) { |
2976 | NBUMPSIDED(0, ns_xlate_null); |
2977 | return -1; |
2978 | } |
2979 | in.s_addr = ntohl(fin->fin_daddr); |
2980 | } |
2981 | |
2982 | /* |
2983 | * Check to see if this redirect mapping already exists and if |
2984 | * it does, return "failure" (allowing it to be created will just |
2985 | * cause one or both of these "connections" to stop working.) |
2986 | */ |
2987 | inb.s_addr = htonl(in.s_addr); |
2988 | sp = fin->fin_data[0]; |
2989 | dp = fin->fin_data[1]; |
2990 | fin->fin_data[1] = fin->fin_data[0]; |
2991 | fin->fin_data[0] = ntohs(nport); |
2992 | natl = ipf_nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), |
2993 | (u_int)fin->fin_p, inb, fin->fin_src); |
2994 | fin->fin_data[0] = sp; |
2995 | fin->fin_data[1] = dp; |
2996 | if (natl != NULL) { |
2997 | DT2(ns_new_xlate_exists, fr_info_t *, fin, nat_t *, natl); |
2998 | NBUMPSIDE(0, ns_xlate_exists); |
2999 | return -1; |
3000 | } |
3001 | |
3002 | nat->nat_ndstaddr = htonl(in.s_addr); |
3003 | nat->nat_odstip = fin->fin_dst; |
3004 | nat->nat_nsrcip = fin->fin_src; |
3005 | nat->nat_osrcip = fin->fin_src; |
3006 | if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0)) |
3007 | nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src, |
3008 | fin->fin_dst, inb, (u_32_t)dport); |
3009 | |
3010 | if (flags & IPN_TCPUDP) { |
3011 | nat->nat_odport = dport; |
3012 | nat->nat_ndport = nport; |
3013 | nat->nat_osport = sport; |
3014 | nat->nat_nsport = sport; |
3015 | ((tcphdr_t *)fin->fin_dp)->th_dport = nport; |
3016 | } else if (flags & IPN_ICMPQUERY) { |
3017 | nat->nat_oicmpid = fin->fin_data[1]; |
3018 | ((icmphdr_t *)fin->fin_dp)->icmp_id = nport; |
3019 | nat->nat_nicmpid = nport; |
3020 | } |
3021 | |
3022 | return move; |
3023 | } |
3024 | |
3025 | /* ------------------------------------------------------------------------ */ |
3026 | /* Function: ipf_nat_add */ |
3027 | /* Returns: nat_t* - NULL == failure to create new NAT structure, */ |
3028 | /* else pointer to new NAT structure */ |
3029 | /* Parameters: fin(I) - pointer to packet information */ |
3030 | /* np(I) - pointer to NAT rule */ |
3031 | /* natsave(I) - pointer to where to store NAT struct pointer */ |
3032 | /* flags(I) - flags describing the current packet */ |
3033 | /* direction(I) - direction of packet (in/out) */ |
3034 | /* Write Lock: ipf_nat */ |
3035 | /* */ |
3036 | /* Attempts to create a new NAT entry. Does not actually change the packet */ |
3037 | /* in any way. */ |
3038 | /* */ |
3039 | /* This function is in three main parts: (1) deal with creating a new NAT */ |
3040 | /* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */ |
3041 | /* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */ |
3042 | /* and (3) building that structure and putting it into the NAT table(s). */ |
3043 | /* */ |
3044 | /* NOTE: natsave should NOT be used to point back to an ipstate_t struct */ |
3045 | /* as it can result in memory being corrupted. */ |
3046 | /* ------------------------------------------------------------------------ */ |
3047 | nat_t * |
3048 | ipf_nat_add(fr_info_t *fin, ipnat_t *np, nat_t **natsave, u_int flags, |
3049 | int direction) |
3050 | { |
3051 | ipf_main_softc_t *softc = fin->fin_main_soft; |
3052 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
3053 | hostmap_t *hm = NULL; |
3054 | nat_t *nat, *natl; |
3055 | natstat_t *nsp; |
3056 | u_int nflags; |
3057 | natinfo_t ni; |
3058 | int move; |
3059 | |
3060 | memset(&ni, 0, sizeof ni); /* XXX gcc */ |
3061 | nsp = &softn->ipf_nat_stats; |
3062 | |
3063 | if ((nsp->ns_active * 100 / softn->ipf_nat_table_max) > |
3064 | softn->ipf_nat_table_wm_high) { |
3065 | softn->ipf_nat_doflush = 1; |
3066 | } |
3067 | |
3068 | if (nsp->ns_active >= softn->ipf_nat_table_max) { |
3069 | NBUMPSIDED(fin->fin_out, ns_table_max); |
3070 | return NULL; |
3071 | } |
3072 | |
3073 | move = 1; |
3074 | nflags = np->in_flags & flags; |
3075 | nflags &= NAT_FROMRULE; |
3076 | |
3077 | ni.nai_np = np; |
3078 | ni.nai_dport = 0; |
3079 | ni.nai_sport = 0; |
3080 | |
3081 | /* Give me a new nat */ |
3082 | KMALLOC(nat, nat_t *); |
3083 | if (nat == NULL) { |
3084 | NBUMPSIDED(fin->fin_out, ns_memfail); |
3085 | /* |
3086 | * Try to automatically tune the max # of entries in the |
3087 | * table allowed to be less than what will cause kmem_alloc() |
3088 | * to fail and try to eliminate panics due to out of memory |
3089 | * conditions arising. |
3090 | */ |
3091 | if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) && |
3092 | (nsp->ns_active > 100)) { |
3093 | softn->ipf_nat_table_max = nsp->ns_active - 100; |
3094 | printf("table_max reduced to %d\n" , |
3095 | softn->ipf_nat_table_max); |
3096 | } |
3097 | return NULL; |
3098 | } |
3099 | |
3100 | if (flags & IPN_ICMPQUERY) { |
3101 | /* |
3102 | * In the ICMP query NAT code, we translate the ICMP id fields |
3103 | * to make them unique. This is indepedent of the ICMP type |
3104 | * (e.g. in the unlikely event that a host sends an echo and |
3105 | * an tstamp request with the same id, both packets will have |
3106 | * their ip address/id field changed in the same way). |
3107 | */ |
3108 | /* The icmp_id field is used by the sender to identify the |
3109 | * process making the icmp request. (the receiver justs |
3110 | * copies it back in its response). So, it closely matches |
3111 | * the concept of source port. We overlay sport, so we can |
3112 | * maximally reuse the existing code. |
3113 | */ |
3114 | ni.nai_sport = fin->fin_data[1]; |
3115 | ni.nai_dport = 0; |
3116 | } |
3117 | |
3118 | bzero((char *)nat, sizeof(*nat)); |
3119 | nat->nat_flags = flags; |
3120 | nat->nat_redir = np->in_redir; |
3121 | nat->nat_dir = direction; |
3122 | nat->nat_pr[0] = fin->fin_p; |
3123 | nat->nat_pr[1] = fin->fin_p; |
3124 | |
3125 | /* |
3126 | * Search the current table for a match and create a new mapping |
3127 | * if there is none found. |
3128 | */ |
3129 | if (np->in_redir & NAT_DIVERTUDP) { |
3130 | move = ipf_nat_newdivert(fin, nat, &ni); |
3131 | |
3132 | } else if (np->in_redir & NAT_REWRITE) { |
3133 | move = ipf_nat_newrewrite(fin, nat, &ni); |
3134 | |
3135 | } else if (direction == NAT_OUTBOUND) { |
3136 | /* |
3137 | * We can now arrange to call this for the same connection |
3138 | * because ipf_nat_new doesn't protect the code path into |
3139 | * this function. |
3140 | */ |
3141 | natl = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p, |
3142 | fin->fin_src, fin->fin_dst); |
3143 | if (natl != NULL) { |
3144 | KFREE(nat); |
3145 | nat = natl; |
3146 | goto done; |
3147 | } |
3148 | |
3149 | move = ipf_nat_newmap(fin, nat, &ni); |
3150 | } else { |
3151 | /* |
3152 | * NAT_INBOUND is used for redirects rules |
3153 | */ |
3154 | natl = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p, |
3155 | fin->fin_src, fin->fin_dst); |
3156 | if (natl != NULL) { |
3157 | KFREE(nat); |
3158 | nat = natl; |
3159 | goto done; |
3160 | } |
3161 | |
3162 | move = ipf_nat_newrdr(fin, nat, &ni); |
3163 | } |
3164 | if (move == -1) |
3165 | goto badnat; |
3166 | |
3167 | np = ni.nai_np; |
3168 | |
3169 | nat->nat_mssclamp = np->in_mssclamp; |
3170 | nat->nat_me = natsave; |
3171 | nat->nat_fr = fin->fin_fr; |
3172 | nat->nat_rev = fin->fin_rev; |
3173 | nat->nat_ptr = np; |
3174 | nat->nat_dlocal = np->in_dlocal; |
3175 | |
3176 | if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) { |
3177 | if (ipf_proxy_new(fin, nat) == -1) { |
3178 | NBUMPSIDED(fin->fin_out, ns_appr_fail); |
3179 | goto badnat; |
3180 | } |
3181 | } |
3182 | |
3183 | nat->nat_ifps[0] = np->in_ifps[0]; |
3184 | if (np->in_ifps[0] != NULL) { |
3185 | COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]); |
3186 | } |
3187 | |
3188 | nat->nat_ifps[1] = np->in_ifps[1]; |
3189 | if (np->in_ifps[1] != NULL) { |
3190 | COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]); |
3191 | } |
3192 | |
3193 | if (ipf_nat_finalise(fin, nat) == -1) { |
3194 | goto badnat; |
3195 | } |
3196 | |
3197 | np->in_use++; |
3198 | |
3199 | if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { |
3200 | if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) { |
3201 | ipf_nat_delrdr(softn, np); |
3202 | ipf_nat_addrdr(softn, np); |
3203 | } else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) { |
3204 | ipf_nat_delmap(softn, np); |
3205 | ipf_nat_addmap(softn, np); |
3206 | } |
3207 | } |
3208 | |
3209 | if (flags & SI_WILDP) |
3210 | nsp->ns_wilds++; |
3211 | nsp->ns_proto[nat->nat_pr[0]]++; |
3212 | |
3213 | goto done; |
3214 | badnat: |
3215 | DT2(ns_badnatnew, fr_info_t *, fin, nat_t *, nat); |
3216 | NBUMPSIDE(fin->fin_out, ns_badnatnew); |
3217 | if ((hm = nat->nat_hm) != NULL) |
3218 | ipf_nat_hostmapdel(softc, &hm); |
3219 | KFREE(nat); |
3220 | nat = NULL; |
3221 | done: |
3222 | if (nat != NULL && np != NULL) |
3223 | np->in_hits++; |
3224 | if (natsave != NULL) |
3225 | *natsave = nat; |
3226 | return nat; |
3227 | } |
3228 | |
3229 | |
3230 | /* ------------------------------------------------------------------------ */ |
3231 | /* Function: ipf_nat_finalise */ |
3232 | /* Returns: int - 0 == sucess, -1 == failure */ |
3233 | /* Parameters: fin(I) - pointer to packet information */ |
3234 | /* nat(I) - pointer to NAT entry */ |
3235 | /* Write Lock: ipf_nat */ |
3236 | /* */ |
3237 | /* This is the tail end of constructing a new NAT entry and is the same */ |
3238 | /* for both IPv4 and IPv6. */ |
3239 | /* ------------------------------------------------------------------------ */ |
3240 | /*ARGSUSED*/ |
3241 | static int |
3242 | ipf_nat_finalise(fr_info_t *fin, nat_t *nat) |
3243 | { |
3244 | ipf_main_softc_t *softc = fin->fin_main_soft; |
3245 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
3246 | u_32_t sum1, sum2, sumd; |
3247 | frentry_t *fr; |
3248 | #if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC) |
3249 | qpktinfo_t *qpi = fin->fin_qpi; |
3250 | #endif |
3251 | |
3252 | switch (nat->nat_pr[0]) |
3253 | { |
3254 | case IPPROTO_ICMP : |
3255 | sum1 = LONG_SUM(ntohs(nat->nat_oicmpid)); |
3256 | sum2 = LONG_SUM(ntohs(nat->nat_nicmpid)); |
3257 | CALC_SUMD(sum1, sum2, sumd); |
3258 | nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); |
3259 | |
3260 | break; |
3261 | |
3262 | default : |
3263 | sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr) + \ |
3264 | ntohs(nat->nat_osport)); |
3265 | sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr) + \ |
3266 | ntohs(nat->nat_nsport)); |
3267 | CALC_SUMD(sum1, sum2, sumd); |
3268 | nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); |
3269 | |
3270 | sum1 = LONG_SUM(ntohl(nat->nat_odstaddr) + \ |
3271 | ntohs(nat->nat_odport)); |
3272 | sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr) + \ |
3273 | ntohs(nat->nat_ndport)); |
3274 | CALC_SUMD(sum1, sum2, sumd); |
3275 | nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); |
3276 | break; |
3277 | } |
3278 | |
3279 | /* |
3280 | * Compute the partial checksum, just in case. |
3281 | * This is only ever placed into outbound packets so care needs |
3282 | * to be taken over which pair of addresses are used. |
3283 | */ |
3284 | if (nat->nat_dir == NAT_OUTBOUND) { |
3285 | sum1 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); |
3286 | sum1 += LONG_SUM(ntohl(nat->nat_ndstaddr)); |
3287 | } else { |
3288 | sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr)); |
3289 | sum1 += LONG_SUM(ntohl(nat->nat_odstaddr)); |
3290 | } |
3291 | sum1 += nat->nat_pr[1]; |
3292 | nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16); |
3293 | |
3294 | sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr)); |
3295 | sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); |
3296 | CALC_SUMD(sum1, sum2, sumd); |
3297 | nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); |
3298 | |
3299 | sum1 = LONG_SUM(ntohl(nat->nat_odstaddr)); |
3300 | sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr)); |
3301 | CALC_SUMD(sum1, sum2, sumd); |
3302 | nat->nat_ipsumd += (sumd & 0xffff) + (sumd >> 16); |
3303 | |
3304 | nat->nat_v[0] = 4; |
3305 | nat->nat_v[1] = 4; |
3306 | |
3307 | if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { |
3308 | nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); |
3309 | } |
3310 | |
3311 | if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { |
3312 | nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); |
3313 | } |
3314 | |
3315 | if ((nat->nat_flags & SI_CLONE) == 0) |
3316 | nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat); |
3317 | |
3318 | if (ipf_nat_insert(softc, softn, nat) == 0) { |
3319 | if (softn->ipf_nat_logging) |
3320 | ipf_nat_log(softc, softn, nat, NL_NEW); |
3321 | fr = nat->nat_fr; |
3322 | if (fr != NULL) { |
3323 | MUTEX_ENTER(&fr->fr_lock); |
3324 | fr->fr_ref++; |
3325 | MUTEX_EXIT(&fr->fr_lock); |
3326 | } |
3327 | return 0; |
3328 | } |
3329 | |
3330 | NBUMPSIDED(fin->fin_out, ns_unfinalised); |
3331 | /* |
3332 | * nat_insert failed, so cleanup time... |
3333 | */ |
3334 | if (nat->nat_sync != NULL) |
3335 | ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); |
3336 | return -1; |
3337 | } |
3338 | |
3339 | |
3340 | /* ------------------------------------------------------------------------ */ |
3341 | /* Function: ipf_nat_insert */ |
3342 | /* Returns: int - 0 == sucess, -1 == failure */ |
3343 | /* Parameters: softc(I) - pointer to soft context main structure */ |
3344 | /* softn(I) - pointer to NAT context structure */ |
3345 | /* nat(I) - pointer to NAT structure */ |
3346 | /* Write Lock: ipf_nat */ |
3347 | /* */ |
3348 | /* Insert a NAT entry into the hash tables for searching and add it to the */ |
3349 | /* list of active NAT entries. Adjust global counters when complete. */ |
3350 | /* ------------------------------------------------------------------------ */ |
3351 | int |
3352 | ipf_nat_insert(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, nat_t *nat) |
3353 | { |
3354 | u_int hv0, hv1; |
3355 | u_int sp, dp; |
3356 | ipnat_t *in; |
3357 | int ret; |
3358 | |
3359 | /* |
3360 | * Try and return an error as early as possible, so calculate the hash |
3361 | * entry numbers first and then proceed. |
3362 | */ |
3363 | if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { |
3364 | if ((nat->nat_flags & IPN_TCPUDP) != 0) { |
3365 | sp = nat->nat_osport; |
3366 | dp = nat->nat_odport; |
3367 | } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { |
3368 | sp = 0; |
3369 | dp = nat->nat_oicmpid; |
3370 | } else { |
3371 | sp = 0; |
3372 | dp = 0; |
3373 | } |
3374 | hv0 = NAT_HASH_FN(nat->nat_osrcaddr, sp, 0xffffffff); |
3375 | hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0 + dp, 0xffffffff); |
3376 | /* |
3377 | * TRACE nat_osrcaddr, nat_osport, nat_odstaddr, |
3378 | * nat_odport, hv0 |
3379 | */ |
3380 | |
3381 | if ((nat->nat_flags & IPN_TCPUDP) != 0) { |
3382 | sp = nat->nat_nsport; |
3383 | dp = nat->nat_ndport; |
3384 | } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { |
3385 | sp = 0; |
3386 | dp = nat->nat_nicmpid; |
3387 | } else { |
3388 | sp = 0; |
3389 | dp = 0; |
3390 | } |
3391 | hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, sp, 0xffffffff); |
3392 | hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1 + dp, 0xffffffff); |
3393 | /* |
3394 | * TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, |
3395 | * nat_ndport, hv1 |
3396 | */ |
3397 | } else { |
3398 | hv0 = NAT_HASH_FN(nat->nat_osrcaddr, 0, 0xffffffff); |
3399 | hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0, 0xffffffff); |
3400 | /* TRACE nat_osrcaddr, nat_odstaddr, hv0 */ |
3401 | |
3402 | hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, 0, 0xffffffff); |
3403 | hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1, 0xffffffff); |
3404 | /* TRACE nat_nsrcaddr, nat_ndstaddr, hv1 */ |
3405 | } |
3406 | |
3407 | if ((nat->nat_dir & NAT_OUTBOUND) == NAT_OUTBOUND) { |
3408 | nat->nat_hv[0] = hv0; |
3409 | nat->nat_hv[1] = hv1; |
3410 | } else { |
3411 | nat->nat_hv[0] = hv1; |
3412 | nat->nat_hv[1] = hv0; |
3413 | } |
3414 | |
3415 | MUTEX_INIT(&nat->nat_lock, "nat entry lock" ); |
3416 | |
3417 | in = nat->nat_ptr; |
3418 | nat->nat_ref = nat->nat_me ? 2 : 1; |
3419 | |
3420 | nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; |
3421 | nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0], 4); |
3422 | |
3423 | if (nat->nat_ifnames[1][0] != '\0') { |
3424 | nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; |
3425 | nat->nat_ifps[1] = ipf_resolvenic(softc, |
3426 | nat->nat_ifnames[1], 4); |
3427 | } else if (in->in_ifnames[1] != -1) { |
3428 | char *name; |
3429 | |
3430 | name = in->in_names + in->in_ifnames[1]; |
3431 | if (name[1] != '\0' && name[0] != '-' && name[0] != '*') { |
3432 | (void) strncpy(nat->nat_ifnames[1], |
3433 | nat->nat_ifnames[0], LIFNAMSIZ); |
3434 | nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; |
3435 | nat->nat_ifps[1] = nat->nat_ifps[0]; |
3436 | } |
3437 | } |
3438 | if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { |
3439 | nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); |
3440 | } |
3441 | if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { |
3442 | nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); |
3443 | } |
3444 | |
3445 | ret = ipf_nat_hashtab_add(softc, softn, nat); |
3446 | if (ret != 0) |
3447 | MUTEX_DESTROY(&nat->nat_lock); |
3448 | return ret; |
3449 | } |
3450 | |
3451 | |
3452 | /* ------------------------------------------------------------------------ */ |
3453 | /* Function: ipf_nat_hashtab_add */ |
3454 | /* Returns: int - 0 == sucess, -1 == failure */ |
3455 | /* Parameters: softc(I) - pointer to soft context main structure */ |
3456 | /* softn(I) - pointer to NAT context structure */ |
3457 | /* nat(I) - pointer to NAT structure */ |
3458 | /* Write Lock: ipf_nat */ |
3459 | /* */ |
3460 | /* Handle the insertion of a NAT entry into the table/list. */ |
3461 | /* ------------------------------------------------------------------------ */ |
3462 | int |
3463 | ipf_nat_hashtab_add(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, nat_t *nat) |
3464 | { |
3465 | nat_t **natp; |
3466 | u_int hv0; |
3467 | u_int hv1; |
3468 | |
3469 | hv0 = nat->nat_hv[0] % softn->ipf_nat_table_sz; |
3470 | hv1 = nat->nat_hv[1] % softn->ipf_nat_table_sz; |
3471 | |
3472 | if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0] >= |
3473 | softn->ipf_nat_maxbucket) { |
3474 | DT1(ns_bucket_max_0, int, |
3475 | softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]); |
3476 | NBUMPSIDE(0, ns_bucket_max); |
3477 | return -1; |
3478 | } |
3479 | |
3480 | if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1] >= |
3481 | softn->ipf_nat_maxbucket) { |
3482 | DT1(ns_bucket_max_1, int, |
3483 | softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]); |
3484 | NBUMPSIDE(1, ns_bucket_max); |
3485 | return -1; |
3486 | } |
3487 | |
3488 | /* |
3489 | * The ordering of operations in the list and hash table insertion |
3490 | * is very important. The last operation for each task should be |
3491 | * to update the top of the list, after all the "nexts" have been |
3492 | * done so that walking the list while it is being done does not |
3493 | * find strange pointers. |
3494 | * |
3495 | * Global list of NAT instances |
3496 | */ |
3497 | nat->nat_next = softn->ipf_nat_instances; |
3498 | nat->nat_pnext = &softn->ipf_nat_instances; |
3499 | if (softn->ipf_nat_instances) |
3500 | softn->ipf_nat_instances->nat_pnext = &nat->nat_next; |
3501 | softn->ipf_nat_instances = nat; |
3502 | |
3503 | /* |
3504 | * Inbound hash table. |
3505 | */ |
3506 | natp = &softn->ipf_nat_table[0][hv0]; |
3507 | nat->nat_phnext[0] = natp; |
3508 | nat->nat_hnext[0] = *natp; |
3509 | if (*natp) { |
3510 | (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; |
3511 | } else { |
3512 | NBUMPSIDE(0, ns_inuse); |
3513 | } |
3514 | *natp = nat; |
3515 | NBUMPSIDE(0, ns_bucketlen[hv0]); |
3516 | |
3517 | /* |
3518 | * Outbound hash table. |
3519 | */ |
3520 | natp = &softn->ipf_nat_table[1][hv1]; |
3521 | nat->nat_phnext[1] = natp; |
3522 | nat->nat_hnext[1] = *natp; |
3523 | if (*natp) |
3524 | (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; |
3525 | else { |
3526 | NBUMPSIDE(1, ns_inuse); |
3527 | } |
3528 | *natp = nat; |
3529 | NBUMPSIDE(1, ns_bucketlen[hv1]); |
3530 | |
3531 | ipf_nat_setqueue(softc, softn, nat); |
3532 | |
3533 | if (nat->nat_dir & NAT_OUTBOUND) { |
3534 | NBUMPSIDE(1, ns_added); |
3535 | } else { |
3536 | NBUMPSIDE(0, ns_added); |
3537 | } |
3538 | softn->ipf_nat_stats.ns_active++; |
3539 | return 0; |
3540 | } |
3541 | |
3542 | |
3543 | /* ------------------------------------------------------------------------ */ |
3544 | /* Function: ipf_nat_icmperrorlookup */ |
3545 | /* Returns: nat_t* - point to matching NAT structure */ |
3546 | /* Parameters: fin(I) - pointer to packet information */ |
3547 | /* dir(I) - direction of packet (in/out) */ |
3548 | /* */ |
3549 | /* Check if the ICMP error message is related to an existing TCP, UDP or */ |
3550 | /* ICMP query nat entry. It is assumed that the packet is already of the */ |
3551 | /* the required length. */ |
3552 | /* ------------------------------------------------------------------------ */ |
3553 | nat_t * |
3554 | ipf_nat_icmperrorlookup(fr_info_t *fin, int dir) |
3555 | { |
3556 | ipf_main_softc_t *softc = fin->fin_main_soft; |
3557 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
3558 | int flags = 0, minlen; |
3559 | icmphdr_t *orgicmp; |
3560 | nat_stat_side_t *nside; |
3561 | tcphdr_t *tcp = NULL; |
3562 | u_short data[2]; |
3563 | nat_t *nat; |
3564 | ip_t *oip; |
3565 | u_int p; |
3566 | |
3567 | nside = &softn->ipf_nat_stats.ns_side[fin->fin_out]; |
3568 | /* |
3569 | * Does it at least have the return (basic) IP header ? |
3570 | * Only a basic IP header (no options) should be with an ICMP error |
3571 | * header. Also, if it's not an error type, then return. |
3572 | */ |
3573 | if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) { |
3574 | ATOMIC_INCL(nside->ns_icmp_basic); |
3575 | return NULL; |
3576 | } |
3577 | |
3578 | /* |
3579 | * Check packet size |
3580 | */ |
3581 | oip = (ip_t *)((char *)fin->fin_dp + 8); |
3582 | minlen = IP_HL(oip) << 2; |
3583 | if ((minlen < sizeof(ip_t)) || |
3584 | (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) { |
3585 | ATOMIC_INCL(nside->ns_icmp_size); |
3586 | return NULL; |
3587 | } |
3588 | |
3589 | /* |
3590 | * Is the buffer big enough for all of it ? It's the size of the IP |
3591 | * header claimed in the encapsulated part which is of concern. It |
3592 | * may be too big to be in this buffer but not so big that it's |
3593 | * outside the ICMP packet, leading to TCP deref's causing problems. |
3594 | * This is possible because we don't know how big oip_hl is when we |
3595 | * do the pullup early in ipf_check() and thus can't gaurantee it is |
3596 | * all here now. |
3597 | */ |
3598 | #ifdef ipf_nat_KERNEL |
3599 | { |
3600 | mb_t *m; |
3601 | |
3602 | m = fin->fin_m; |
3603 | # if defined(MENTAT) |
3604 | if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > |
3605 | (char *)m->b_wptr) { |
3606 | ATOMIC_INCL(nside->ns_icmp_mbuf); |
3607 | return NULL; |
3608 | } |
3609 | # else |
3610 | if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > |
3611 | (char *)fin->fin_ip + M_LEN(m)) { |
3612 | ATOMIC_INCL(nside->ns_icmp_mbuf); |
3613 | return NULL; |
3614 | } |
3615 | # endif |
3616 | } |
3617 | #endif |
3618 | |
3619 | if (fin->fin_daddr != oip->ip_src.s_addr) { |
3620 | ATOMIC_INCL(nside->ns_icmp_address); |
3621 | return NULL; |
3622 | } |
3623 | |
3624 | p = oip->ip_p; |
3625 | if (p == IPPROTO_TCP) |
3626 | flags = IPN_TCP; |
3627 | else if (p == IPPROTO_UDP) |
3628 | flags = IPN_UDP; |
3629 | else if (p == IPPROTO_ICMP) { |
3630 | orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2)); |
3631 | |
3632 | /* see if this is related to an ICMP query */ |
3633 | if (ipf_nat_icmpquerytype(orgicmp->icmp_type)) { |
3634 | data[0] = fin->fin_data[0]; |
3635 | data[1] = fin->fin_data[1]; |
3636 | fin->fin_data[0] = 0; |
3637 | fin->fin_data[1] = orgicmp->icmp_id; |
3638 | |
3639 | flags = IPN_ICMPERR|IPN_ICMPQUERY; |
3640 | /* |
3641 | * NOTE : dir refers to the direction of the original |
3642 | * ip packet. By definition the icmp error |
3643 | * message flows in the opposite direction. |
3644 | */ |
3645 | if (dir == NAT_INBOUND) |
3646 | nat = ipf_nat_inlookup(fin, flags, p, |
3647 | oip->ip_dst, |
3648 | oip->ip_src); |
3649 | else |
3650 | nat = ipf_nat_outlookup(fin, flags, p, |
3651 | oip->ip_dst, |
3652 | oip->ip_src); |
3653 | fin->fin_data[0] = data[0]; |
3654 | fin->fin_data[1] = data[1]; |
3655 | return nat; |
3656 | } |
3657 | } |
3658 | |
3659 | if (flags & IPN_TCPUDP) { |
3660 | minlen += 8; /* + 64bits of data to get ports */ |
3661 | /* TRACE (fin,minlen) */ |
3662 | if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) { |
3663 | ATOMIC_INCL(nside->ns_icmp_short); |
3664 | return NULL; |
3665 | } |
3666 | |
3667 | data[0] = fin->fin_data[0]; |
3668 | data[1] = fin->fin_data[1]; |
3669 | tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2)); |
3670 | fin->fin_data[0] = ntohs(tcp->th_dport); |
3671 | fin->fin_data[1] = ntohs(tcp->th_sport); |
3672 | |
3673 | if (dir == NAT_INBOUND) { |
3674 | nat = ipf_nat_inlookup(fin, flags, p, oip->ip_dst, |
3675 | oip->ip_src); |
3676 | } else { |
3677 | nat = ipf_nat_outlookup(fin, flags, p, oip->ip_dst, |
3678 | oip->ip_src); |
3679 | } |
3680 | fin->fin_data[0] = data[0]; |
3681 | fin->fin_data[1] = data[1]; |
3682 | return nat; |
3683 | } |
3684 | if (dir == NAT_INBOUND) |
3685 | nat = ipf_nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src); |
3686 | else |
3687 | nat = ipf_nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src); |
3688 | |
3689 | return nat; |
3690 | } |
3691 | |
3692 | |
3693 | /* ------------------------------------------------------------------------ */ |
3694 | /* Function: ipf_nat_icmperror */ |
3695 | /* Returns: nat_t* - point to matching NAT structure */ |
3696 | /* Parameters: fin(I) - pointer to packet information */ |
3697 | /* nflags(I) - NAT flags for this packet */ |
3698 | /* dir(I) - direction of packet (in/out) */ |
3699 | /* */ |
3700 | /* Fix up an ICMP packet which is an error message for an existing NAT */ |
3701 | /* session. This will correct both packet header data and checksums. */ |
3702 | /* */ |
3703 | /* This should *ONLY* be used for incoming ICMP error packets to make sure */ |
3704 | /* a NAT'd ICMP packet gets correctly recognised. */ |
3705 | /* ------------------------------------------------------------------------ */ |
3706 | nat_t * |
3707 | ipf_nat_icmperror(fr_info_t *fin, u_int *nflags, int dir) |
3708 | { |
3709 | ipf_main_softc_t *softc = fin->fin_main_soft; |
3710 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
3711 | u_32_t sum1, sum2, sumd, sumd2; |
3712 | struct in_addr a1, a2, a3, a4; |
3713 | int flags, dlen, odst; |
3714 | icmphdr_t *icmp; |
3715 | u_short *csump; |
3716 | tcphdr_t *tcp; |
3717 | nat_t *nat; |
3718 | ip_t *oip; |
3719 | void *dp; |
3720 | |
3721 | if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { |
3722 | NBUMPSIDED(fin->fin_out, ns_icmp_short); |
3723 | return NULL; |
3724 | } |
3725 | |
3726 | /* |
3727 | * ipf_nat_icmperrorlookup() will return NULL for `defective' packets. |
3728 | */ |
3729 | if ((fin->fin_v != 4) || !(nat = ipf_nat_icmperrorlookup(fin, dir))) { |
3730 | NBUMPSIDED(fin->fin_out, ns_icmp_notfound); |
3731 | return NULL; |
3732 | } |
3733 | |
3734 | tcp = NULL; |
3735 | csump = NULL; |
3736 | flags = 0; |
3737 | sumd2 = 0; |
3738 | *nflags = IPN_ICMPERR; |
3739 | icmp = fin->fin_dp; |
3740 | oip = (ip_t *)&icmp->icmp_ip; |
3741 | dp = (((char *)oip) + (IP_HL(oip) << 2)); |
3742 | if (oip->ip_p == IPPROTO_TCP) { |
3743 | tcp = (tcphdr_t *)dp; |
3744 | csump = (u_short *)&tcp->th_sum; |
3745 | flags = IPN_TCP; |
3746 | } else if (oip->ip_p == IPPROTO_UDP) { |
3747 | udphdr_t *udp; |
3748 | |
3749 | udp = (udphdr_t *)dp; |
3750 | tcp = (tcphdr_t *)dp; |
3751 | csump = (u_short *)&udp->uh_sum; |
3752 | flags = IPN_UDP; |
3753 | } else if (oip->ip_p == IPPROTO_ICMP) |
3754 | flags = IPN_ICMPQUERY; |
3755 | dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip); |
3756 | |
3757 | /* |
3758 | * Need to adjust ICMP header to include the real IP#'s and |
3759 | * port #'s. Only apply a checksum change relative to the |
3760 | * IP address change as it will be modified again in ipf_nat_checkout |
3761 | * for both address and port. Two checksum changes are |
3762 | * necessary for the two header address changes. Be careful |
3763 | * to only modify the checksum once for the port # and twice |
3764 | * for the IP#. |
3765 | */ |
3766 | |
3767 | /* |
3768 | * Step 1 |
3769 | * Fix the IP addresses in the offending IP packet. You also need |
3770 | * to adjust the IP header checksum of that offending IP packet. |
3771 | * |
3772 | * Normally, you would expect that the ICMP checksum of the |
3773 | * ICMP error message needs to be adjusted as well for the |
3774 | * IP address change in oip. |
3775 | * However, this is a NOP, because the ICMP checksum is |
3776 | * calculated over the complete ICMP packet, which includes the |
3777 | * changed oip IP addresses and oip->ip_sum. However, these |
3778 | * two changes cancel each other out (if the delta for |
3779 | * the IP address is x, then the delta for ip_sum is minus x), |
3780 | * so no change in the icmp_cksum is necessary. |
3781 | * |
3782 | * Inbound ICMP |
3783 | * ------------ |
3784 | * MAP rule, SRC=a,DST=b -> SRC=c,DST=b |
3785 | * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b) |
3786 | * - OIP_SRC(c)=nat_newsrcip, OIP_DST(b)=nat_newdstip |
3787 | *=> OIP_SRC(c)=nat_oldsrcip, OIP_DST(b)=nat_olddstip |
3788 | * |
3789 | * RDR rule, SRC=a,DST=b -> SRC=a,DST=c |
3790 | * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a) |
3791 | * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip |
3792 | *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip |
3793 | * |
3794 | * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d |
3795 | * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d) |
3796 | * - OIP_SRC(c)=nat_newsrcip, OIP_DST(d)=nat_newdstip |
3797 | *=> OIP_SRC(c)=nat_oldsrcip, OIP_DST(d)=nat_olddstip |
3798 | * |
3799 | * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d |
3800 | * - response to outgoing packet (d,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) |
3801 | * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip |
3802 | *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip |
3803 | * |
3804 | * Outbound ICMP |
3805 | * ------------- |
3806 | * MAP rule, SRC=a,DST=b -> SRC=c,DST=b |
3807 | * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) |
3808 | * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip |
3809 | *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip |
3810 | * |
3811 | * RDR rule, SRC=a,DST=b -> SRC=a,DST=c |
3812 | * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c) |
3813 | * - OIP_SRC(a)=nat_newsrcip, OIP_DST(c)=nat_newdstip |
3814 | *=> OIP_SRC(a)=nat_oldsrcip, OIP_DST(c)=nat_olddstip |
3815 | * |
3816 | * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d |
3817 | * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d) |
3818 | * - OIP_SRC(c)=nat_olddstip, OIP_DST(d)=nat_oldsrcip |
3819 | *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip |
3820 | * |
3821 | * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d |
3822 | * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a) |
3823 | * - OIP_SRC(b)=nat_newsrcip, OIP_DST(a)=nat_newdstip |
3824 | *=> OIP_SRC(a)=nat_oldsrcip, OIP_DST(c)=nat_olddstip |
3825 | */ |
3826 | |
3827 | if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) || |
3828 | ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) { |
3829 | a1.s_addr = ntohl(nat->nat_osrcaddr); |
3830 | a4.s_addr = ntohl(oip->ip_src.s_addr); |
3831 | a3.s_addr = ntohl(nat->nat_odstaddr); |
3832 | a2.s_addr = ntohl(oip->ip_dst.s_addr); |
3833 | oip->ip_src.s_addr = htonl(a1.s_addr); |
3834 | oip->ip_dst.s_addr = htonl(a3.s_addr); |
3835 | odst = 1; |
3836 | } else { |
3837 | a1.s_addr = ntohl(nat->nat_ndstaddr); |
3838 | a2.s_addr = ntohl(oip->ip_dst.s_addr); |
3839 | a3.s_addr = ntohl(nat->nat_nsrcaddr); |
3840 | a4.s_addr = ntohl(oip->ip_src.s_addr); |
3841 | oip->ip_dst.s_addr = htonl(a3.s_addr); |
3842 | oip->ip_src.s_addr = htonl(a1.s_addr); |
3843 | odst = 0; |
3844 | } |
3845 | sum1 = 0; |
3846 | sum2 = 0; |
3847 | sumd = 0; |
3848 | CALC_SUMD(a2.s_addr, a3.s_addr, sum1); |
3849 | CALC_SUMD(a4.s_addr, a1.s_addr, sum2); |
3850 | sumd = sum2 + sum1; |
3851 | if (sumd != 0) |
3852 | ipf_fix_datacksum(&oip->ip_sum, sumd); |
3853 | |
3854 | sumd2 = sumd; |
3855 | sum1 = 0; |
3856 | sum2 = 0; |
3857 | |
3858 | /* |
3859 | * Fix UDP pseudo header checksum to compensate for the |
3860 | * IP address change. |
3861 | */ |
3862 | if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) { |
3863 | u_32_t sum3, sum4, sumt; |
3864 | |
3865 | /* |
3866 | * Step 2 : |
3867 | * For offending TCP/UDP IP packets, translate the ports as |
3868 | * well, based on the NAT specification. Of course such |
3869 | * a change may be reflected in the ICMP checksum as well. |
3870 | * |
3871 | * Since the port fields are part of the TCP/UDP checksum |
3872 | * of the offending IP packet, you need to adjust that checksum |
3873 | * as well... except that the change in the port numbers should |
3874 | * be offset by the checksum change. However, the TCP/UDP |
3875 | * checksum will also need to change if there has been an |
3876 | * IP address change. |
3877 | */ |
3878 | if (odst == 1) { |
3879 | sum1 = ntohs(nat->nat_osport); |
3880 | sum4 = ntohs(tcp->th_sport); |
3881 | sum3 = ntohs(nat->nat_odport); |
3882 | sum2 = ntohs(tcp->th_dport); |
3883 | |
3884 | tcp->th_sport = htons(sum1); |
3885 | tcp->th_dport = htons(sum3); |
3886 | } else { |
3887 | sum1 = ntohs(nat->nat_ndport); |
3888 | sum2 = ntohs(tcp->th_dport); |
3889 | sum3 = ntohs(nat->nat_nsport); |
3890 | sum4 = ntohs(tcp->th_sport); |
3891 | |
3892 | tcp->th_dport = htons(sum3); |
3893 | tcp->th_sport = htons(sum1); |
3894 | } |
3895 | CALC_SUMD(sum4, sum1, sumt); |
3896 | sumd += sumt; |
3897 | CALC_SUMD(sum2, sum3, sumt); |
3898 | sumd += sumt; |
3899 | |
3900 | if (sumd != 0 || sumd2 != 0) { |
3901 | /* |
3902 | * At this point, sumd is the delta to apply to the |
3903 | * TCP/UDP header, given the changes in both the IP |
3904 | * address and the ports and sumd2 is the delta to |
3905 | * apply to the ICMP header, given the IP address |
3906 | * change delta that may need to be applied to the |
3907 | * TCP/UDP checksum instead. |
3908 | * |
3909 | * If we will both the IP and TCP/UDP checksums |
3910 | * then the ICMP checksum changes by the address |
3911 | * delta applied to the TCP/UDP checksum. If we |
3912 | * do not change the TCP/UDP checksum them we |
3913 | * apply the delta in ports to the ICMP checksum. |
3914 | */ |
3915 | if (oip->ip_p == IPPROTO_UDP) { |
3916 | if ((dlen >= 8) && (*csump != 0)) { |
3917 | ipf_fix_datacksum(csump, sumd); |
3918 | } else { |
3919 | CALC_SUMD(sum1, sum4, sumd2); |
3920 | CALC_SUMD(sum3, sum2, sumt); |
3921 | sumd2 += sumt; |
3922 | } |
3923 | } else if (oip->ip_p == IPPROTO_TCP) { |
3924 | if (dlen >= 18) { |
3925 | ipf_fix_datacksum(csump, sumd); |
3926 | } else { |
3927 | CALC_SUMD(sum1, sum4, sumd2); |
3928 | CALC_SUMD(sum3, sum2, sumt); |
3929 | sumd2 += sumt; |
3930 | } |
3931 | } |
3932 | if (sumd2 != 0) { |
3933 | sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); |
3934 | sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); |
3935 | sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); |
3936 | ipf_fix_incksum(0, &icmp->icmp_cksum, sumd2, 0); |
3937 | } |
3938 | } |
3939 | } else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) { |
3940 | icmphdr_t *orgicmp; |
3941 | |
3942 | /* |
3943 | * XXX - what if this is bogus hl and we go off the end ? |
3944 | * In this case, ipf_nat_icmperrorlookup() will have |
3945 | * returned NULL. |
3946 | */ |
3947 | orgicmp = (icmphdr_t *)dp; |
3948 | |
3949 | if (odst == 1) { |
3950 | if (orgicmp->icmp_id != nat->nat_osport) { |
3951 | |
3952 | /* |
3953 | * Fix ICMP checksum (of the offening ICMP |
3954 | * query packet) to compensate the change |
3955 | * in the ICMP id of the offending ICMP |
3956 | * packet. |
3957 | * |
3958 | * Since you modify orgicmp->icmp_id with |
3959 | * a delta (say x) and you compensate that |
3960 | * in origicmp->icmp_cksum with a delta |
3961 | * minus x, you don't have to adjust the |
3962 | * overall icmp->icmp_cksum |
3963 | */ |
3964 | sum1 = ntohs(orgicmp->icmp_id); |
3965 | sum2 = ntohs(nat->nat_oicmpid); |
3966 | CALC_SUMD(sum1, sum2, sumd); |
3967 | orgicmp->icmp_id = nat->nat_oicmpid; |
3968 | ipf_fix_datacksum(&orgicmp->icmp_cksum, sumd); |
3969 | } |
3970 | } /* nat_dir == NAT_INBOUND is impossible for icmp queries */ |
3971 | } |
3972 | return nat; |
3973 | } |
3974 | |
3975 | |
3976 | /* |
3977 | * MAP-IN MAP-OUT RDR-IN RDR-OUT |
3978 | * osrc X == src == src X |
3979 | * odst X == dst == dst X |
3980 | * nsrc == dst X X == dst |
3981 | * ndst == src X X == src |
3982 | * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND |
3983 | */ |
3984 | /* |
3985 | * NB: these lookups don't lock access to the list, it assumed that it has |
3986 | * already been done! |
3987 | */ |
3988 | /* ------------------------------------------------------------------------ */ |
3989 | /* Function: ipf_nat_inlookup */ |
3990 | /* Returns: nat_t* - NULL == no match, */ |
3991 | /* else pointer to matching NAT entry */ |
3992 | /* Parameters: fin(I) - pointer to packet information */ |
3993 | /* flags(I) - NAT flags for this packet */ |
3994 | /* p(I) - protocol for this packet */ |
3995 | /* src(I) - source IP address */ |
3996 | /* mapdst(I) - destination IP address */ |
3997 | /* */ |
3998 | /* Lookup a nat entry based on the mapped destination ip address/port and */ |
3999 | /* real source address/port. We use this lookup when receiving a packet, */ |
4000 | /* we're looking for a table entry, based on the destination address. */ |
4001 | /* */ |
4002 | /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ |
4003 | /* */ |
4004 | /* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ |
4005 | /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ |
4006 | /* */ |
4007 | /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ |
4008 | /* the packet is of said protocol */ |
4009 | /* ------------------------------------------------------------------------ */ |
4010 | nat_t * |
4011 | ipf_nat_inlookup(fr_info_t *fin, u_int flags, u_int p, struct in_addr src, |
4012 | struct in_addr mapdst) |
4013 | { |
4014 | ipf_main_softc_t *softc = fin->fin_main_soft; |
4015 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
4016 | u_short sport, dport; |
4017 | ipnat_t *ipn; |
4018 | nat_t *nat; |
4019 | int nflags; |
4020 | u_32_t dst; |
4021 | void *ifp; |
4022 | u_int hv, rhv; |
4023 | |
4024 | ifp = fin->fin_ifp; |
4025 | dst = mapdst.s_addr; |
4026 | |
4027 | switch (p) |
4028 | { |
4029 | case IPPROTO_TCP : |
4030 | case IPPROTO_UDP : |
4031 | sport = htons(fin->fin_data[0]); |
4032 | dport = htons(fin->fin_data[1]); |
4033 | break; |
4034 | case IPPROTO_ICMP : |
4035 | sport = 0; |
4036 | dport = fin->fin_data[1]; |
4037 | break; |
4038 | default : |
4039 | sport = 0; |
4040 | dport = 0; |
4041 | break; |
4042 | } |
4043 | |
4044 | |
4045 | if ((flags & SI_WILDP) != 0) |
4046 | goto find_in_wild_ports; |
4047 | |
4048 | rhv = NAT_HASH_FN(dst, dport, 0xffffffff); |
4049 | rhv = NAT_HASH_FN(src.s_addr, rhv + sport, 0xffffffff); |
4050 | hv = rhv % softn->ipf_nat_table_sz; |
4051 | nat = softn->ipf_nat_table[1][hv]; |
4052 | /* TRACE dst, dport, src, sport, hv, nat */ |
4053 | |
4054 | for (; nat; nat = nat->nat_hnext[1]) { |
4055 | if (nat->nat_ifps[0] != NULL) { |
4056 | if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) |
4057 | continue; |
4058 | } |
4059 | |
4060 | if (nat->nat_pr[0] != p) |
4061 | continue; |
4062 | |
4063 | switch (nat->nat_dir) |
4064 | { |
4065 | case NAT_INBOUND : |
4066 | case NAT_DIVERTIN : |
4067 | if (nat->nat_v[0] != 4) |
4068 | continue; |
4069 | if (nat->nat_osrcaddr != src.s_addr || |
4070 | nat->nat_odstaddr != dst) |
4071 | continue; |
4072 | if ((nat->nat_flags & IPN_TCPUDP) != 0) { |
4073 | if (nat->nat_osport != sport) |
4074 | continue; |
4075 | if (nat->nat_odport != dport) |
4076 | continue; |
4077 | |
4078 | } else if (p == IPPROTO_ICMP) { |
4079 | if (nat->nat_oicmpid != dport) { |
4080 | continue; |
4081 | } |
4082 | } |
4083 | break; |
4084 | case NAT_DIVERTOUT : |
4085 | if (nat->nat_dlocal) |
4086 | continue; |
4087 | case NAT_OUTBOUND : |
4088 | if (nat->nat_v[1] != 4) |
4089 | continue; |
4090 | if (nat->nat_dlocal) |
4091 | continue; |
4092 | if (nat->nat_dlocal) |
4093 | continue; |
4094 | if (nat->nat_ndstaddr != src.s_addr || |
4095 | nat->nat_nsrcaddr != dst) |
4096 | continue; |
4097 | if ((nat->nat_flags & IPN_TCPUDP) != 0) { |
4098 | if (nat->nat_ndport != sport) |
4099 | continue; |
4100 | if (nat->nat_nsport != dport) |
4101 | continue; |
4102 | |
4103 | } else if (p == IPPROTO_ICMP) { |
4104 | if (nat->nat_nicmpid != dport) { |
4105 | continue; |
4106 | } |
4107 | } |
4108 | break; |
4109 | } |
4110 | |
4111 | |
4112 | if ((nat->nat_flags & IPN_TCPUDP) != 0) { |
4113 | ipn = nat->nat_ptr; |
4114 | if ((ipn != NULL) && (nat->nat_aps != NULL)) |
4115 | if (ipf_proxy_match(fin, nat) != 0) |
4116 | continue; |
4117 | } |
4118 | if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { |
4119 | nat->nat_ifps[0] = ifp; |
4120 | nat->nat_mtu[0] = GETIFMTU_4(ifp); |
4121 | } |
4122 | return nat; |
4123 | } |
4124 | |
4125 | /* |
4126 | * So if we didn't find it but there are wildcard members in the hash |
4127 | * table, go back and look for them. We do this search and update here |
4128 | * because it is modifying the NAT table and we want to do this only |
4129 | * for the first packet that matches. The exception, of course, is |
4130 | * for "dummy" (FI_IGNORE) lookups. |
4131 | */ |
4132 | find_in_wild_ports: |
4133 | if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { |
4134 | NBUMPSIDEX(0, ns_lookup_miss, ns_lookup_miss_0); |
4135 | return NULL; |
4136 | } |
4137 | if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { |
4138 | NBUMPSIDEX(0, ns_lookup_nowild, ns_lookup_nowild_0); |
4139 | return NULL; |
4140 | } |
4141 | |
4142 | RWLOCK_EXIT(&softc->ipf_nat); |
4143 | |
4144 | hv = NAT_HASH_FN(dst, 0, 0xffffffff); |
4145 | hv = NAT_HASH_FN(src.s_addr, hv, softn->ipf_nat_table_sz); |
4146 | WRITE_ENTER(&softc->ipf_nat); |
4147 | |
4148 | nat = softn->ipf_nat_table[1][hv]; |
4149 | /* TRACE dst, src, hv, nat */ |
4150 | for (; nat; nat = nat->nat_hnext[1]) { |
4151 | if (nat->nat_ifps[0] != NULL) { |
4152 | if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) |
4153 | continue; |
4154 | } |
4155 | |
4156 | if (nat->nat_pr[0] != fin->fin_p) |
4157 | continue; |
4158 | |
4159 | switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)) |
4160 | { |
4161 | case NAT_INBOUND : |
4162 | if (nat->nat_v[0] != 4) |
4163 | continue; |
4164 | if (nat->nat_osrcaddr != src.s_addr || |
4165 | nat->nat_odstaddr != dst) |
4166 | continue; |
4167 | break; |
4168 | case NAT_OUTBOUND : |
4169 | if (nat->nat_v[1] != 4) |
4170 | continue; |
4171 | if (nat->nat_ndstaddr != src.s_addr || |
4172 | nat->nat_nsrcaddr != dst) |
4173 | continue; |
4174 | break; |
4175 | } |
4176 | |
4177 | nflags = nat->nat_flags; |
4178 | if (!(nflags & (NAT_TCPUDP|SI_WILDP))) |
4179 | continue; |
4180 | |
4181 | if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags, |
4182 | NAT_INBOUND) == 1) { |
4183 | if ((fin->fin_flx & FI_IGNORE) != 0) |
4184 | break; |
4185 | if ((nflags & SI_CLONE) != 0) { |
4186 | nat = ipf_nat_clone(fin, nat); |
4187 | if (nat == NULL) |
4188 | break; |
4189 | } else { |
4190 | MUTEX_ENTER(&softn->ipf_nat_new); |
4191 | softn->ipf_nat_stats.ns_wilds--; |
4192 | MUTEX_EXIT(&softn->ipf_nat_new); |
4193 | } |
4194 | |
4195 | if (nat->nat_dir == NAT_INBOUND) { |
4196 | if (nat->nat_osport == 0) { |
4197 | nat->nat_osport = sport; |
4198 | nat->nat_nsport = sport; |
4199 | } |
4200 | if (nat->nat_odport == 0) { |
4201 | nat->nat_odport = dport; |
4202 | nat->nat_ndport = dport; |
4203 | } |
4204 | } else if (nat->nat_dir == NAT_OUTBOUND) { |
4205 | if (nat->nat_osport == 0) { |
4206 | nat->nat_osport = dport; |
4207 | nat->nat_nsport = dport; |
4208 | } |
4209 | if (nat->nat_odport == 0) { |
4210 | nat->nat_odport = sport; |
4211 | nat->nat_ndport = sport; |
4212 | } |
4213 | } |
4214 | if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { |
4215 | nat->nat_ifps[0] = ifp; |
4216 | nat->nat_mtu[0] = GETIFMTU_4(ifp); |
4217 | } |
4218 | nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); |
4219 | ipf_nat_tabmove(softn, nat); |
4220 | break; |
4221 | } |
4222 | } |
4223 | |
4224 | MUTEX_DOWNGRADE(&softc->ipf_nat); |
4225 | |
4226 | if (nat == NULL) { |
4227 | NBUMPSIDE(0, ns_lookup_miss); |
4228 | } |
4229 | return nat; |
4230 | } |
4231 | |
4232 | |
4233 | /* ------------------------------------------------------------------------ */ |
4234 | /* Function: ipf_nat_tabmove */ |
4235 | /* Returns: Nil */ |
4236 | /* Parameters: softn(I) - pointer to NAT context structure */ |
4237 | /* nat(I) - pointer to NAT structure */ |
4238 | /* Write Lock: ipf_nat */ |
4239 | /* */ |
4240 | /* This function is only called for TCP/UDP NAT table entries where the */ |
4241 | /* original was placed in the table without hashing on the ports and we now */ |
4242 | /* want to include hashing on port numbers. */ |
4243 | /* ------------------------------------------------------------------------ */ |
4244 | static void |
4245 | ipf_nat_tabmove(ipf_nat_softc_t *softn, nat_t *nat) |
4246 | { |
4247 | u_int hv0, hv1, rhv0, rhv1; |
4248 | natstat_t *nsp; |
4249 | nat_t **natp; |
4250 | |
4251 | if (nat->nat_flags & SI_CLONE) |
4252 | return; |
4253 | |
4254 | nsp = &softn->ipf_nat_stats; |
4255 | /* |
4256 | * Remove the NAT entry from the old location |
4257 | */ |
4258 | if (nat->nat_hnext[0]) |
4259 | nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; |
4260 | *nat->nat_phnext[0] = nat->nat_hnext[0]; |
4261 | hv0 = nat->nat_hv[0] % softn->ipf_nat_table_sz; |
4262 | hv1 = nat->nat_hv[1] % softn->ipf_nat_table_sz; |
4263 | |
4264 | ASSERT(nsp->ns_side[0].ns_bucketlen[hv0] > 0); |
4265 | nsp->ns_side[0].ns_bucketlen[hv0]--; |
4266 | |
4267 | if (nat->nat_hnext[1]) |
4268 | nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; |
4269 | *nat->nat_phnext[1] = nat->nat_hnext[1]; |
4270 | ASSERT(nsp->ns_side[1].ns_bucketlen[hv1] > 0); |
4271 | nsp->ns_side[1].ns_bucketlen[hv1]--; |
4272 | |
4273 | /* |
4274 | * Add into the NAT table in the new position |
4275 | */ |
4276 | rhv0 = NAT_HASH_FN(nat->nat_osrcaddr, nat->nat_osport, 0xffffffff); |
4277 | rhv0 = NAT_HASH_FN(nat->nat_odstaddr, rhv0 + nat->nat_odport, |
4278 | 0xffffffff); |
4279 | rhv1 = NAT_HASH_FN(nat->nat_nsrcaddr, nat->nat_nsport, 0xffffffff); |
4280 | rhv1 = NAT_HASH_FN(nat->nat_ndstaddr, rhv1 + nat->nat_ndport, |
4281 | 0xffffffff); |
4282 | |
4283 | if ((nat->nat_dir & NAT_OUTBOUND) == NAT_OUTBOUND) { |
4284 | nat->nat_hv[0] = rhv0; |
4285 | nat->nat_hv[1] = rhv1; |
4286 | } else { |
4287 | nat->nat_hv[0] = rhv1; |
4288 | nat->nat_hv[1] = rhv0; |
4289 | } |
4290 | |
4291 | hv0 = nat->nat_hv[0] % softn->ipf_nat_table_sz; |
4292 | hv1 = nat->nat_hv[1] % softn->ipf_nat_table_sz; |
4293 | |
4294 | /* TRACE nat_osrcaddr, nat_osport, nat_odstaddr, nat_odport, hv0 */ |
4295 | /* TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, nat_ndport, hv1 */ |
4296 | |
4297 | natp = &softn->ipf_nat_table[0][hv0]; |
4298 | if (*natp) |
4299 | (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; |
4300 | nat->nat_phnext[0] = natp; |
4301 | nat->nat_hnext[0] = *natp; |
4302 | *natp = nat; |
4303 | nsp->ns_side[0].ns_bucketlen[hv0]++; |
4304 | |
4305 | natp = &softn->ipf_nat_table[1][hv1]; |
4306 | if (*natp) |
4307 | (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; |
4308 | nat->nat_phnext[1] = natp; |
4309 | nat->nat_hnext[1] = *natp; |
4310 | *natp = nat; |
4311 | nsp->ns_side[1].ns_bucketlen[hv1]++; |
4312 | } |
4313 | |
4314 | |
4315 | /* ------------------------------------------------------------------------ */ |
4316 | /* Function: ipf_nat_outlookup */ |
4317 | /* Returns: nat_t* - NULL == no match, */ |
4318 | /* else pointer to matching NAT entry */ |
4319 | /* Parameters: fin(I) - pointer to packet information */ |
4320 | /* flags(I) - NAT flags for this packet */ |
4321 | /* p(I) - protocol for this packet */ |
4322 | /* src(I) - source IP address */ |
4323 | /* dst(I) - destination IP address */ |
4324 | /* rw(I) - 1 == write lock on held, 0 == read lock. */ |
4325 | /* */ |
4326 | /* Lookup a nat entry based on the source 'real' ip address/port and */ |
4327 | /* destination address/port. We use this lookup when sending a packet out, */ |
4328 | /* we're looking for a table entry, based on the source address. */ |
4329 | /* */ |
4330 | /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ |
4331 | /* */ |
4332 | /* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ |
4333 | /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ |
4334 | /* */ |
4335 | /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ |
4336 | /* the packet is of said protocol */ |
4337 | /* ------------------------------------------------------------------------ */ |
4338 | nat_t * |
4339 | ipf_nat_outlookup(fr_info_t *fin, u_int flags, u_int p, struct in_addr src, |
4340 | struct in_addr dst) |
4341 | { |
4342 | ipf_main_softc_t *softc = fin->fin_main_soft; |
4343 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
4344 | u_short sport, dport; |
4345 | ipnat_t *ipn; |
4346 | nat_t *nat; |
4347 | void *ifp; |
4348 | u_int hv; |
4349 | |
4350 | ifp = fin->fin_ifp; |
4351 | |
4352 | switch (p) |
4353 | { |
4354 | case IPPROTO_TCP : |
4355 | case IPPROTO_UDP : |
4356 | sport = htons(fin->fin_data[0]); |
4357 | dport = htons(fin->fin_data[1]); |
4358 | break; |
4359 | case IPPROTO_ICMP : |
4360 | sport = 0; |
4361 | dport = fin->fin_data[1]; |
4362 | break; |
4363 | default : |
4364 | sport = 0; |
4365 | dport = 0; |
4366 | break; |
4367 | } |
4368 | |
4369 | if ((flags & SI_WILDP) != 0) |
4370 | goto find_out_wild_ports; |
4371 | |
4372 | hv = NAT_HASH_FN(src.s_addr, sport, 0xffffffff); |
4373 | hv = NAT_HASH_FN(dst.s_addr, hv + dport, softn->ipf_nat_table_sz); |
4374 | nat = softn->ipf_nat_table[0][hv]; |
4375 | |
4376 | /* TRACE src, sport, dst, dport, hv, nat */ |
4377 | |
4378 | for (; nat; nat = nat->nat_hnext[0]) { |
4379 | if (nat->nat_ifps[1] != NULL) { |
4380 | if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) |
4381 | continue; |
4382 | } |
4383 | |
4384 | if (nat->nat_pr[1] != p) |
4385 | continue; |
4386 | |
4387 | switch (nat->nat_dir) |
4388 | { |
4389 | case NAT_INBOUND : |
4390 | case NAT_DIVERTIN : |
4391 | if (nat->nat_v[1] != 4) |
4392 | continue; |
4393 | if (nat->nat_ndstaddr != src.s_addr || |
4394 | nat->nat_nsrcaddr != dst.s_addr) |
4395 | continue; |
4396 | |
4397 | if ((nat->nat_flags & IPN_TCPUDP) != 0) { |
4398 | if (nat->nat_ndport != sport) |
4399 | continue; |
4400 | if (nat->nat_nsport != dport) |
4401 | continue; |
4402 | |
4403 | } else if (p == IPPROTO_ICMP) { |
4404 | if (nat->nat_nicmpid != dport) { |
4405 | continue; |
4406 | } |
4407 | } |
4408 | break; |
4409 | case NAT_OUTBOUND : |
4410 | case NAT_DIVERTOUT : |
4411 | if (nat->nat_v[0] != 4) |
4412 | continue; |
4413 | if (nat->nat_osrcaddr != src.s_addr || |
4414 | nat->nat_odstaddr != dst.s_addr) |
4415 | continue; |
4416 | |
4417 | if ((nat->nat_flags & IPN_TCPUDP) != 0) { |
4418 | if (nat->nat_odport != dport) |
4419 | continue; |
4420 | if (nat->nat_osport != sport) |
4421 | continue; |
4422 | |
4423 | } else if (p == IPPROTO_ICMP) { |
4424 | if (nat->nat_oicmpid != dport) { |
4425 | continue; |
4426 | } |
4427 | } |
4428 | break; |
4429 | } |
4430 | |
4431 | ipn = nat->nat_ptr; |
4432 | if ((ipn != NULL) && (nat->nat_aps != NULL)) |
4433 | if (ipf_proxy_match(fin, nat) != 0) |
4434 | continue; |
4435 | |
4436 | if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { |
4437 | nat->nat_ifps[1] = ifp; |
4438 | nat->nat_mtu[1] = GETIFMTU_4(ifp); |
4439 | } |
4440 | return nat; |
4441 | } |
4442 | |
4443 | /* |
4444 | * So if we didn't find it but there are wildcard members in the hash |
4445 | * table, go back and look for them. We do this search and update here |
4446 | * because it is modifying the NAT table and we want to do this only |
4447 | * for the first packet that matches. The exception, of course, is |
4448 | * for "dummy" (FI_IGNORE) lookups. |
4449 | */ |
4450 | find_out_wild_ports: |
4451 | if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { |
4452 | NBUMPSIDEX(1, ns_lookup_miss, ns_lookup_miss_1); |
4453 | return NULL; |
4454 | } |
4455 | if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { |
4456 | NBUMPSIDEX(1, ns_lookup_nowild, ns_lookup_nowild_1); |
4457 | return NULL; |
4458 | } |
4459 | |
4460 | RWLOCK_EXIT(&softc->ipf_nat); |
4461 | |
4462 | hv = NAT_HASH_FN(src.s_addr, 0, 0xffffffff); |
4463 | hv = NAT_HASH_FN(dst.s_addr, hv, softn->ipf_nat_table_sz); |
4464 | |
4465 | WRITE_ENTER(&softc->ipf_nat); |
4466 | |
4467 | nat = softn->ipf_nat_table[0][hv]; |
4468 | for (; nat; nat = nat->nat_hnext[0]) { |
4469 | if (nat->nat_ifps[1] != NULL) { |
4470 | if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) |
4471 | continue; |
4472 | } |
4473 | |
4474 | if (nat->nat_pr[1] != fin->fin_p) |
4475 | continue; |
4476 | |
4477 | switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)) |
4478 | { |
4479 | case NAT_INBOUND : |
4480 | if (nat->nat_v[1] != 4) |
4481 | continue; |
4482 | if (nat->nat_ndstaddr != src.s_addr || |
4483 | nat->nat_nsrcaddr != dst.s_addr) |
4484 | continue; |
4485 | break; |
4486 | case NAT_OUTBOUND : |
4487 | if (nat->nat_v[0] != 4) |
4488 | continue; |
4489 | if (nat->nat_osrcaddr != src.s_addr || |
4490 | nat->nat_odstaddr != dst.s_addr) |
4491 | continue; |
4492 | break; |
4493 | } |
4494 | |
4495 | if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP))) |
4496 | continue; |
4497 | |
4498 | if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags, |
4499 | NAT_OUTBOUND) == 1) { |
4500 | if ((fin->fin_flx & FI_IGNORE) != 0) |
4501 | break; |
4502 | if ((nat->nat_flags & SI_CLONE) != 0) { |
4503 | nat = ipf_nat_clone(fin, nat); |
4504 | if (nat == NULL) |
4505 | break; |
4506 | } else { |
4507 | MUTEX_ENTER(&softn->ipf_nat_new); |
4508 | softn->ipf_nat_stats.ns_wilds--; |
4509 | MUTEX_EXIT(&softn->ipf_nat_new); |
4510 | } |
4511 | |
4512 | if (nat->nat_dir == NAT_OUTBOUND) { |
4513 | if (nat->nat_osport == 0) { |
4514 | nat->nat_osport = sport; |
4515 | nat->nat_nsport = sport; |
4516 | } |
4517 | if (nat->nat_odport == 0) { |
4518 | nat->nat_odport = dport; |
4519 | nat->nat_ndport = dport; |
4520 | } |
4521 | } else if (nat->nat_dir == NAT_INBOUND) { |
4522 | if (nat->nat_osport == 0) { |
4523 | nat->nat_osport = dport; |
4524 | nat->nat_nsport = dport; |
4525 | } |
4526 | if (nat->nat_odport == 0) { |
4527 | nat->nat_odport = sport; |
4528 | nat->nat_ndport = sport; |
4529 | } |
4530 | } |
4531 | if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { |
4532 | nat->nat_ifps[1] = ifp; |
4533 | nat->nat_mtu[1] = GETIFMTU_4(ifp); |
4534 | } |
4535 | nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); |
4536 | ipf_nat_tabmove(softn, nat); |
4537 | break; |
4538 | } |
4539 | } |
4540 | |
4541 | MUTEX_DOWNGRADE(&softc->ipf_nat); |
4542 | |
4543 | if (nat == NULL) { |
4544 | NBUMPSIDE(1, ns_lookup_miss); |
4545 | } |
4546 | return nat; |
4547 | } |
4548 | |
4549 | |
4550 | /* ------------------------------------------------------------------------ */ |
4551 | /* Function: ipf_nat_lookupredir */ |
4552 | /* Returns: nat_t* - NULL == no match, */ |
4553 | /* else pointer to matching NAT entry */ |
4554 | /* Parameters: softc(I) - pointer to soft context main structure */ |
4555 | /* np(I) - pointer to description of packet to find NAT */ |
4556 | /* table entry for. */ |
4557 | /* */ |
4558 | /* Lookup the NAT tables to search for a matching redirect */ |
4559 | /* The contents of natlookup_t should imitate those found in a packet that */ |
4560 | /* would be translated - ie a packet coming in for RDR or going out for MAP.*/ |
4561 | /* We can do the lookup in one of two ways, imitating an inbound or */ |
4562 | /* outbound packet. By default we assume outbound, unless IPN_IN is set. */ |
4563 | /* For IN, the fields are set as follows: */ |
4564 | /* nl_real* = source information */ |
4565 | /* nl_out* = destination information (translated) */ |
4566 | /* For an out packet, the fields are set like this: */ |
4567 | /* nl_in* = source information (untranslated) */ |
4568 | /* nl_out* = destination information (translated) */ |
4569 | /* ------------------------------------------------------------------------ */ |
4570 | nat_t * |
4571 | ipf_nat_lookupredir(ipf_main_softc_t *softc, natlookup_t *np) |
4572 | { |
4573 | fr_info_t fi; |
4574 | nat_t *nat; |
4575 | |
4576 | bzero((char *)&fi, sizeof(fi)); |
4577 | fi.fin_main_soft = softc; |
4578 | if (np->nl_flags & IPN_IN) { |
4579 | fi.fin_data[0] = ntohs(np->nl_realport); |
4580 | fi.fin_data[1] = ntohs(np->nl_outport); |
4581 | } else { |
4582 | fi.fin_data[0] = ntohs(np->nl_inport); |
4583 | fi.fin_data[1] = ntohs(np->nl_outport); |
4584 | } |
4585 | if (np->nl_flags & IPN_TCP) |
4586 | fi.fin_p = IPPROTO_TCP; |
4587 | else if (np->nl_flags & IPN_UDP) |
4588 | fi.fin_p = IPPROTO_UDP; |
4589 | else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) |
4590 | fi.fin_p = IPPROTO_ICMP; |
4591 | |
4592 | /* |
4593 | * We can do two sorts of lookups: |
4594 | * - IPN_IN: we have the `real' and `out' address, look for `in'. |
4595 | * - default: we have the `in' and `out' address, look for `real'. |
4596 | */ |
4597 | if (np->nl_flags & IPN_IN) { |
4598 | if ((nat = ipf_nat_inlookup(&fi, np->nl_flags, fi.fin_p, |
4599 | np->nl_realip, np->nl_outip))) { |
4600 | np->nl_inip = nat->nat_odstip; |
4601 | np->nl_inport = nat->nat_odport; |
4602 | } |
4603 | } else { |
4604 | /* |
4605 | * If nl_inip is non null, this is a lookup based on the real |
4606 | * ip address. Else, we use the fake. |
4607 | */ |
4608 | if ((nat = ipf_nat_outlookup(&fi, np->nl_flags, fi.fin_p, |
4609 | np->nl_inip, np->nl_outip))) { |
4610 | |
4611 | if ((np->nl_flags & IPN_FINDFORWARD) != 0) { |
4612 | fr_info_t fin; |
4613 | bzero((char *)&fin, sizeof(fin)); |
4614 | fin.fin_p = nat->nat_pr[0]; |
4615 | fin.fin_data[0] = ntohs(nat->nat_ndport); |
4616 | fin.fin_data[1] = ntohs(nat->nat_nsport); |
4617 | if (ipf_nat_inlookup(&fin, np->nl_flags, |
4618 | fin.fin_p, nat->nat_ndstip, |
4619 | nat->nat_nsrcip) != NULL) { |
4620 | np->nl_flags &= ~IPN_FINDFORWARD; |
4621 | } |
4622 | } |
4623 | |
4624 | np->nl_realip = nat->nat_odstip; |
4625 | np->nl_realport = nat->nat_odport; |
4626 | } |
4627 | } |
4628 | |
4629 | return nat; |
4630 | } |
4631 | |
4632 | |
4633 | /* ------------------------------------------------------------------------ */ |
4634 | /* Function: ipf_nat_match */ |
4635 | /* Returns: int - 0 == no match, 1 == match */ |
4636 | /* Parameters: fin(I) - pointer to packet information */ |
4637 | /* np(I) - pointer to NAT rule */ |
4638 | /* */ |
4639 | /* Pull the matching of a packet against a NAT rule out of that complex */ |
4640 | /* loop inside ipf_nat_checkin() and lay it out properly in its own function. */ |
4641 | /* ------------------------------------------------------------------------ */ |
4642 | static int |
4643 | ipf_nat_match(fr_info_t *fin, ipnat_t *np) |
4644 | { |
4645 | ipf_main_softc_t *softc = fin->fin_main_soft; |
4646 | frtuc_t *ft; |
4647 | int match; |
4648 | |
4649 | match = 0; |
4650 | switch (np->in_osrcatype) |
4651 | { |
4652 | case FRI_NORMAL : |
4653 | match = ((fin->fin_saddr & np->in_osrcmsk) != np->in_osrcaddr); |
4654 | break; |
4655 | case FRI_LOOKUP : |
4656 | match = (*np->in_osrcfunc)(softc, np->in_osrcptr, |
4657 | 4, &fin->fin_saddr, fin->fin_plen); |
4658 | break; |
4659 | } |
4660 | match ^= ((np->in_flags & IPN_NOTSRC) != 0); |
4661 | if (match) |
4662 | return 0; |
4663 | |
4664 | match = 0; |
4665 | switch (np->in_odstatype) |
4666 | { |
4667 | case FRI_NORMAL : |
4668 | match = ((fin->fin_daddr & np->in_odstmsk) != np->in_odstaddr); |
4669 | break; |
4670 | case FRI_LOOKUP : |
4671 | match = (*np->in_odstfunc)(softc, np->in_odstptr, |
4672 | 4, &fin->fin_daddr, fin->fin_plen); |
4673 | break; |
4674 | } |
4675 | |
4676 | match ^= ((np->in_flags & IPN_NOTDST) != 0); |
4677 | if (match) |
4678 | return 0; |
4679 | |
4680 | ft = &np->in_tuc; |
4681 | if (!(fin->fin_flx & FI_TCPUDP) || |
4682 | (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { |
4683 | if (ft->ftu_scmp || ft->ftu_dcmp) |
4684 | return 0; |
4685 | return 1; |
4686 | } |
4687 | |
4688 | return ipf_tcpudpchk(&fin->fin_fi, ft); |
4689 | } |
4690 | |
4691 | |
4692 | /* ------------------------------------------------------------------------ */ |
4693 | /* Function: ipf_nat_update */ |
4694 | /* Returns: Nil */ |
4695 | /* Parameters: fin(I) - pointer to packet information */ |
4696 | /* nat(I) - pointer to NAT structure */ |
4697 | /* */ |
4698 | /* Updates the lifetime of a NAT table entry for non-TCP packets. Must be */ |
4699 | /* called with fin_rev updated - i.e. after calling ipf_nat_proto(). */ |
4700 | /* */ |
4701 | /* This *MUST* be called after ipf_nat_proto() as it expects fin_rev to */ |
4702 | /* already be set. */ |
4703 | /* ------------------------------------------------------------------------ */ |
4704 | void |
4705 | ipf_nat_update(fr_info_t *fin, nat_t *nat) |
4706 | { |
4707 | ipf_main_softc_t *softc = fin->fin_main_soft; |
4708 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
4709 | ipftq_t *ifq, *ifq2; |
4710 | ipftqent_t *tqe; |
4711 | ipnat_t *np = nat->nat_ptr; |
4712 | |
4713 | tqe = &nat->nat_tqe; |
4714 | ifq = tqe->tqe_ifq; |
4715 | |
4716 | /* |
4717 | * We allow over-riding of NAT timeouts from NAT rules, even for |
4718 | * TCP, however, if it is TCP and there is no rule timeout set, |
4719 | * then do not update the timeout here. |
4720 | */ |
4721 | if (np != NULL) { |
4722 | np->in_bytes[fin->fin_rev] += fin->fin_plen; |
4723 | ifq2 = np->in_tqehead[fin->fin_rev]; |
4724 | } else { |
4725 | ifq2 = NULL; |
4726 | } |
4727 | |
4728 | if (nat->nat_pr[0] == IPPROTO_TCP && ifq2 == NULL) { |
4729 | (void) ipf_tcp_age(&nat->nat_tqe, fin, softn->ipf_nat_tcptq, |
4730 | 0, 2); |
4731 | } else { |
4732 | if (ifq2 == NULL) { |
4733 | if (nat->nat_pr[0] == IPPROTO_UDP) |
4734 | ifq2 = fin->fin_rev ? &softn->ipf_nat_udpacktq : |
4735 | &softn->ipf_nat_udptq; |
4736 | else if (nat->nat_pr[0] == IPPROTO_ICMP || |
4737 | nat->nat_pr[0] == IPPROTO_ICMPV6) |
4738 | ifq2 = fin->fin_rev ? &softn->ipf_nat_icmpacktq: |
4739 | &softn->ipf_nat_icmptq; |
4740 | else |
4741 | ifq2 = &softn->ipf_nat_iptq; |
4742 | } |
4743 | |
4744 | ipf_movequeue(softc->ipf_ticks, tqe, ifq, ifq2); |
4745 | } |
4746 | } |
4747 | |
4748 | |
4749 | /* ------------------------------------------------------------------------ */ |
4750 | /* Function: ipf_nat_checkout */ |
4751 | /* Returns: int - -1 == packet failed NAT checks so block it, */ |
4752 | /* 0 == no packet translation occurred, */ |
4753 | /* 1 == packet was successfully translated. */ |
4754 | /* Parameters: fin(I) - pointer to packet information */ |
4755 | /* passp(I) - pointer to filtering result flags */ |
4756 | /* */ |
4757 | /* Check to see if an outcoming packet should be changed. ICMP packets are */ |
4758 | /* first checked to see if they match an existing entry (if an error), */ |
4759 | /* otherwise a search of the current NAT table is made. If neither results */ |
4760 | /* in a match then a search for a matching NAT rule is made. Create a new */ |
4761 | /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ |
4762 | /* packet header(s) as required. */ |
4763 | /* ------------------------------------------------------------------------ */ |
4764 | int |
4765 | ipf_nat_checkout(fr_info_t *fin, u_32_t *passp) |
4766 | { |
4767 | ipnat_t *np = NULL, *npnext; |
4768 | struct ifnet *ifp, *sifp; |
4769 | ipf_main_softc_t *softc; |
4770 | ipf_nat_softc_t *softn; |
4771 | tcphdr_t *tcp = NULL; |
4772 | int rval, natfailed; |
4773 | u_int nflags = 0; |
4774 | u_32_t ipa, iph; |
4775 | int natadd = 1; |
4776 | frentry_t *fr; |
4777 | nat_t *nat; |
4778 | |
4779 | if (fin->fin_v == 6) { |
4780 | #ifdef USE_INET6 |
4781 | return ipf_nat6_checkout(fin, passp); |
4782 | #else |
4783 | return 0; |
4784 | #endif |
4785 | } |
4786 | |
4787 | softc = fin->fin_main_soft; |
4788 | softn = softc->ipf_nat_soft; |
4789 | |
4790 | if (softn->ipf_nat_lock != 0) |
4791 | return 0; |
4792 | if (softn->ipf_nat_stats.ns_rules == 0 && |
4793 | softn->ipf_nat_instances == NULL) |
4794 | return 0; |
4795 | |
4796 | natfailed = 0; |
4797 | fr = fin->fin_fr; |
4798 | sifp = fin->fin_ifp; |
4799 | if (fr != NULL) { |
4800 | ifp = fr->fr_tifs[fin->fin_rev].fd_ptr; |
4801 | if ((ifp != NULL) && (ifp != (void *)-1)) |
4802 | fin->fin_ifp = ifp; |
4803 | } |
4804 | ifp = fin->fin_ifp; |
4805 | |
4806 | if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { |
4807 | switch (fin->fin_p) |
4808 | { |
4809 | case IPPROTO_TCP : |
4810 | nflags = IPN_TCP; |
4811 | break; |
4812 | case IPPROTO_UDP : |
4813 | nflags = IPN_UDP; |
4814 | break; |
4815 | case IPPROTO_ICMP : |
4816 | /* |
4817 | * This is an incoming packet, so the destination is |
4818 | * the icmp_id and the source port equals 0 |
4819 | */ |
4820 | if ((fin->fin_flx & FI_ICMPQUERY) != 0) |
4821 | nflags = IPN_ICMPQUERY; |
4822 | break; |
4823 | default : |
4824 | break; |
4825 | } |
4826 | |
4827 | if ((nflags & IPN_TCPUDP)) |
4828 | tcp = fin->fin_dp; |
4829 | } |
4830 | |
4831 | ipa = fin->fin_saddr; |
4832 | |
4833 | READ_ENTER(&softc->ipf_nat); |
4834 | |
4835 | if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && |
4836 | (nat = ipf_nat_icmperror(fin, &nflags, NAT_OUTBOUND))) |
4837 | /*EMPTY*/; |
4838 | else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) |
4839 | natadd = 0; |
4840 | else if ((nat = ipf_nat_outlookup(fin, nflags|NAT_SEARCH, |
4841 | (u_int)fin->fin_p, fin->fin_src, |
4842 | fin->fin_dst))) { |
4843 | nflags = nat->nat_flags; |
4844 | } else if (fin->fin_off == 0) { |
4845 | u_32_t hv, msk, nmsk = 0; |
4846 | |
4847 | /* |
4848 | * If there is no current entry in the nat table for this IP#, |
4849 | * create one for it (if there is a matching rule). |
4850 | */ |
4851 | maskloop: |
4852 | msk = softn->ipf_nat_map_active_masks[nmsk]; |
4853 | iph = ipa & msk; |
4854 | hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_maprules_sz); |
4855 | retry_roundrobin: |
4856 | for (np = softn->ipf_nat_map_rules[hv]; np; np = npnext) { |
4857 | npnext = np->in_mnext; |
4858 | if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) |
4859 | continue; |
4860 | if (np->in_v[0] != 4) |
4861 | continue; |
4862 | if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p)) |
4863 | continue; |
4864 | if ((np->in_flags & IPN_RF) && |
4865 | !(np->in_flags & nflags)) |
4866 | continue; |
4867 | if (np->in_flags & IPN_FILTER) { |
4868 | switch (ipf_nat_match(fin, np)) |
4869 | { |
4870 | case 0 : |
4871 | continue; |
4872 | case -1 : |
4873 | rval = -1; |
4874 | goto outmatchfail; |
4875 | case 1 : |
4876 | default : |
4877 | break; |
4878 | } |
4879 | } else if ((ipa & np->in_osrcmsk) != np->in_osrcaddr) |
4880 | continue; |
4881 | |
4882 | if ((fr != NULL) && |
4883 | !ipf_matchtag(&np->in_tag, &fr->fr_nattag)) |
4884 | continue; |
4885 | |
4886 | if (np->in_plabel != -1) { |
4887 | if (((np->in_flags & IPN_FILTER) == 0) && |
4888 | (np->in_odport != fin->fin_data[1])) |
4889 | continue; |
4890 | if (ipf_proxy_ok(fin, tcp, np) == 0) |
4891 | continue; |
4892 | } |
4893 | |
4894 | if (np->in_flags & IPN_NO) { |
4895 | np->in_hits++; |
4896 | break; |
4897 | } |
4898 | MUTEX_ENTER(&softn->ipf_nat_new); |
4899 | /* |
4900 | * If we've matched a round-robin rule but it has |
4901 | * moved in the list since we got it, start over as |
4902 | * this is now no longer correct. |
4903 | */ |
4904 | if (npnext != np->in_mnext) { |
4905 | if ((np->in_flags & IPN_ROUNDR) != 0) { |
4906 | MUTEX_EXIT(&softn->ipf_nat_new); |
4907 | goto retry_roundrobin; |
4908 | } |
4909 | npnext = np->in_mnext; |
4910 | } |
4911 | |
4912 | nat = ipf_nat_add(fin, np, NULL, nflags, NAT_OUTBOUND); |
4913 | MUTEX_EXIT(&softn->ipf_nat_new); |
4914 | if (nat != NULL) { |
4915 | natfailed = 0; |
4916 | break; |
4917 | } |
4918 | natfailed = -1; |
4919 | } |
4920 | if ((np == NULL) && (nmsk < softn->ipf_nat_map_max)) { |
4921 | nmsk++; |
4922 | goto maskloop; |
4923 | } |
4924 | } |
4925 | |
4926 | if (nat != NULL) { |
4927 | rval = ipf_nat_out(fin, nat, natadd, nflags); |
4928 | if (rval == 1) { |
4929 | MUTEX_ENTER(&nat->nat_lock); |
4930 | ipf_nat_update(fin, nat); |
4931 | nat->nat_bytes[1] += fin->fin_plen; |
4932 | nat->nat_pkts[1]++; |
4933 | fin->fin_pktnum = nat->nat_pkts[1]; |
4934 | MUTEX_EXIT(&nat->nat_lock); |
4935 | } |
4936 | } else |
4937 | rval = natfailed; |
4938 | outmatchfail: |
4939 | RWLOCK_EXIT(&softc->ipf_nat); |
4940 | |
4941 | switch (rval) |
4942 | { |
4943 | case -1 : |
4944 | if (passp != NULL) { |
4945 | DT1(frb_natv4out, fr_info_t *, fin); |
4946 | NBUMPSIDED(1, ns_drop); |
4947 | *passp = FR_BLOCK; |
4948 | fin->fin_reason = FRB_NATV4; |
4949 | } |
4950 | fin->fin_flx |= FI_BADNAT; |
4951 | NBUMPSIDED(1, ns_badnat); |
4952 | break; |
4953 | case 0 : |
4954 | NBUMPSIDE(1, ns_ignored); |
4955 | break; |
4956 | case 1 : |
4957 | NBUMPSIDE(1, ns_translated); |
4958 | break; |
4959 | } |
4960 | fin->fin_ifp = sifp; |
4961 | return rval; |
4962 | } |
4963 | |
4964 | /* ------------------------------------------------------------------------ */ |
4965 | /* Function: ipf_nat_out */ |
4966 | /* Returns: int - -1 == packet failed NAT checks so block it, */ |
4967 | /* 1 == packet was successfully translated. */ |
4968 | /* Parameters: fin(I) - pointer to packet information */ |
4969 | /* nat(I) - pointer to NAT structure */ |
4970 | /* natadd(I) - flag indicating if it is safe to add frag cache */ |
4971 | /* nflags(I) - NAT flags set for this packet */ |
4972 | /* */ |
4973 | /* Translate a packet coming "out" on an interface. */ |
4974 | /* ------------------------------------------------------------------------ */ |
4975 | int |
4976 | ipf_nat_out(fr_info_t *fin, nat_t *nat, int natadd, u_32_t nflags) |
4977 | { |
4978 | ipf_main_softc_t *softc = fin->fin_main_soft; |
4979 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
4980 | icmphdr_t *icmp; |
4981 | tcphdr_t *tcp; |
4982 | ipnat_t *np; |
4983 | int skip; |
4984 | int i; |
4985 | |
4986 | tcp = NULL; |
4987 | icmp = NULL; |
4988 | np = nat->nat_ptr; |
4989 | |
4990 | if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL)) |
4991 | (void) ipf_frag_natnew(softc, fin, 0, nat); |
4992 | |
4993 | /* |
4994 | * Fix up checksums, not by recalculating them, but |
4995 | * simply computing adjustments. |
4996 | * This is only done for STREAMS based IP implementations where the |
4997 | * checksum has already been calculated by IP. In all other cases, |
4998 | * IPFilter is called before the checksum needs calculating so there |
4999 | * is no call to modify whatever is in the header now. |
5000 | */ |
5001 | if (nflags == IPN_ICMPERR) { |
5002 | u_32_t s1, s2, sumd, msumd; |
5003 | |
5004 | s1 = LONG_SUM(ntohl(fin->fin_saddr)); |
5005 | if (nat->nat_dir == NAT_OUTBOUND) { |
5006 | s2 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); |
5007 | } else { |
5008 | s2 = LONG_SUM(ntohl(nat->nat_odstaddr)); |
5009 | } |
5010 | CALC_SUMD(s1, s2, sumd); |
5011 | msumd = sumd; |
5012 | |
5013 | s1 = LONG_SUM(ntohl(fin->fin_daddr)); |
5014 | if (nat->nat_dir == NAT_OUTBOUND) { |
5015 | s2 = LONG_SUM(ntohl(nat->nat_ndstaddr)); |
5016 | } else { |
5017 | s2 = LONG_SUM(ntohl(nat->nat_osrcaddr)); |
5018 | } |
5019 | CALC_SUMD(s1, s2, sumd); |
5020 | msumd += sumd; |
5021 | |
5022 | ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, msumd, 0); |
5023 | } |
5024 | #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ |
5025 | defined(linux) || defined(BRIDGE_IPF) |
5026 | else { |
5027 | /* |
5028 | * Strictly speaking, this isn't necessary on BSD |
5029 | * kernels because they do checksum calculation after |
5030 | * this code has run BUT if ipfilter is being used |
5031 | * to do NAT as a bridge, that code doesn't exist. |
5032 | */ |
5033 | switch (nat->nat_dir) |
5034 | { |
5035 | case NAT_OUTBOUND : |
5036 | ipf_fix_outcksum(fin->fin_cksum & FI_CK_L4PART, |
5037 | &fin->fin_ip->ip_sum, |
5038 | nat->nat_ipsumd, 0); |
5039 | break; |
5040 | |
5041 | case NAT_INBOUND : |
5042 | ipf_fix_incksum(fin->fin_cksum & FI_CK_L4PART, |
5043 | &fin->fin_ip->ip_sum, |
5044 | nat->nat_ipsumd, 0); |
5045 | break; |
5046 | |
5047 | default : |
5048 | break; |
5049 | } |
5050 | } |
5051 | #endif |
5052 | |
5053 | /* |
5054 | * Address assignment is after the checksum modification because |
5055 | * we are using the address in the packet for determining the |
5056 | * correct checksum offset (the ICMP error could be coming from |
5057 | * anyone...) |
5058 | */ |
5059 | switch (nat->nat_dir) |
5060 | { |
5061 | case NAT_OUTBOUND : |
5062 | fin->fin_ip->ip_src = nat->nat_nsrcip; |
5063 | fin->fin_saddr = nat->nat_nsrcaddr; |
5064 | fin->fin_ip->ip_dst = nat->nat_ndstip; |
5065 | fin->fin_daddr = nat->nat_ndstaddr; |
5066 | break; |
5067 | |
5068 | case NAT_INBOUND : |
5069 | fin->fin_ip->ip_src = nat->nat_odstip; |
5070 | fin->fin_saddr = nat->nat_ndstaddr; |
5071 | fin->fin_ip->ip_dst = nat->nat_osrcip; |
5072 | fin->fin_daddr = nat->nat_nsrcaddr; |
5073 | break; |
5074 | |
5075 | case NAT_DIVERTIN : |
5076 | { |
5077 | mb_t *m; |
5078 | |
5079 | skip = ipf_nat_decap(fin, nat); |
5080 | if (skip <= 0) { |
5081 | NBUMPSIDED(1, ns_decap_fail); |
5082 | return -1; |
5083 | } |
5084 | |
5085 | m = fin->fin_m; |
5086 | |
5087 | #if defined(MENTAT) && defined(_KERNEL) |
5088 | m->b_rptr += skip; |
5089 | #else |
5090 | m->m_data += skip; |
5091 | m->m_len -= skip; |
5092 | |
5093 | # ifdef M_PKTHDR |
5094 | if (m->m_flags & M_PKTHDR) |
5095 | m->m_pkthdr.len -= skip; |
5096 | # endif |
5097 | #endif |
5098 | |
5099 | MUTEX_ENTER(&nat->nat_lock); |
5100 | ipf_nat_update(fin, nat); |
5101 | MUTEX_EXIT(&nat->nat_lock); |
5102 | fin->fin_flx |= FI_NATED; |
5103 | if (np != NULL && np->in_tag.ipt_num[0] != 0) |
5104 | fin->fin_nattag = &np->in_tag; |
5105 | return 1; |
5106 | /* NOTREACHED */ |
5107 | } |
5108 | |
5109 | case NAT_DIVERTOUT : |
5110 | { |
5111 | u_32_t s1, s2, sumd; |
5112 | udphdr_t *uh; |
5113 | ip_t *ip; |
5114 | mb_t *m; |
5115 | |
5116 | m = M_DUP(np->in_divmp); |
5117 | if (m == NULL) { |
5118 | NBUMPSIDED(1, ns_divert_dup); |
5119 | return -1; |
5120 | } |
5121 | |
5122 | ip = MTOD(m, ip_t *); |
5123 | ip->ip_id = htons(ipf_nextipid(fin)); |
5124 | s2 = ntohs(ip->ip_id); |
5125 | |
5126 | s1 = ip->ip_len; |
5127 | ip->ip_len = ntohs(ip->ip_len); |
5128 | ip->ip_len += fin->fin_plen; |
5129 | ip->ip_len = htons(ip->ip_len); |
5130 | s2 += ntohs(ip->ip_len); |
5131 | CALC_SUMD(s1, s2, sumd); |
5132 | |
5133 | uh = (udphdr_t *)(ip + 1); |
5134 | uh->uh_ulen += fin->fin_plen; |
5135 | uh->uh_ulen = htons(uh->uh_ulen); |
5136 | #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ |
5137 | defined(linux) || defined(BRIDGE_IPF) |
5138 | ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0); |
5139 | #endif |
5140 | |
5141 | PREP_MB_T(fin, m); |
5142 | |
5143 | fin->fin_src = ip->ip_src; |
5144 | fin->fin_dst = ip->ip_dst; |
5145 | fin->fin_ip = ip; |
5146 | fin->fin_plen += sizeof(ip_t) + 8; /* UDP + IPv4 hdr */ |
5147 | fin->fin_dlen += sizeof(ip_t) + 8; /* UDP + IPv4 hdr */ |
5148 | |
5149 | nflags &= ~IPN_TCPUDPICMP; |
5150 | |
5151 | break; |
5152 | } |
5153 | |
5154 | default : |
5155 | break; |
5156 | } |
5157 | |
5158 | if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { |
5159 | u_short *csump; |
5160 | |
5161 | if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) { |
5162 | tcp = fin->fin_dp; |
5163 | |
5164 | switch (nat->nat_dir) |
5165 | { |
5166 | case NAT_OUTBOUND : |
5167 | tcp->th_sport = nat->nat_nsport; |
5168 | fin->fin_data[0] = ntohs(nat->nat_nsport); |
5169 | tcp->th_dport = nat->nat_ndport; |
5170 | fin->fin_data[1] = ntohs(nat->nat_ndport); |
5171 | break; |
5172 | |
5173 | case NAT_INBOUND : |
5174 | tcp->th_sport = nat->nat_odport; |
5175 | fin->fin_data[0] = ntohs(nat->nat_odport); |
5176 | tcp->th_dport = nat->nat_osport; |
5177 | fin->fin_data[1] = ntohs(nat->nat_osport); |
5178 | break; |
5179 | } |
5180 | } |
5181 | |
5182 | if ((nat->nat_oicmpid != 0) && (nflags & IPN_ICMPQUERY)) { |
5183 | icmp = fin->fin_dp; |
5184 | |
5185 | switch (nat->nat_dir) |
5186 | { |
5187 | case NAT_OUTBOUND : |
5188 | icmp->icmp_id = nat->nat_nicmpid; |
5189 | break; |
5190 | case NAT_INBOUND : |
5191 | icmp->icmp_id = nat->nat_oicmpid; |
5192 | break; |
5193 | } |
5194 | } |
5195 | |
5196 | csump = ipf_nat_proto(fin, nat, nflags); |
5197 | |
5198 | /* |
5199 | * The above comments do not hold for layer 4 (or higher) |
5200 | * checksums... |
5201 | */ |
5202 | if (csump != NULL) { |
5203 | if (nat->nat_dir == NAT_OUTBOUND) |
5204 | ipf_fix_outcksum(fin->fin_cksum, csump, |
5205 | nat->nat_sumd[0], |
5206 | nat->nat_sumd[1] + |
5207 | fin->fin_dlen); |
5208 | else |
5209 | ipf_fix_incksum(fin->fin_cksum, csump, |
5210 | nat->nat_sumd[0], |
5211 | nat->nat_sumd[1] + |
5212 | fin->fin_dlen); |
5213 | } |
5214 | } |
5215 | |
5216 | ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); |
5217 | /* ------------------------------------------------------------- */ |
5218 | /* A few quick notes: */ |
5219 | /* Following are test conditions prior to calling the */ |
5220 | /* ipf_proxy_check routine. */ |
5221 | /* */ |
5222 | /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ |
5223 | /* with a redirect rule, we attempt to match the packet's */ |
5224 | /* source port against in_dport, otherwise we'd compare the */ |
5225 | /* packet's destination. */ |
5226 | /* ------------------------------------------------------------- */ |
5227 | if ((np != NULL) && (np->in_apr != NULL)) { |
5228 | i = ipf_proxy_check(fin, nat); |
5229 | if (i == 0) { |
5230 | i = 1; |
5231 | } else if (i == -1) { |
5232 | NBUMPSIDED(1, ns_ipf_proxy_fail); |
5233 | } |
5234 | } else { |
5235 | i = 1; |
5236 | } |
5237 | fin->fin_flx |= FI_NATED; |
5238 | return i; |
5239 | } |
5240 | |
5241 | |
5242 | /* ------------------------------------------------------------------------ */ |
5243 | /* Function: ipf_nat_checkin */ |
5244 | /* Returns: int - -1 == packet failed NAT checks so block it, */ |
5245 | /* 0 == no packet translation occurred, */ |
5246 | /* 1 == packet was successfully translated. */ |
5247 | /* Parameters: fin(I) - pointer to packet information */ |
5248 | /* passp(I) - pointer to filtering result flags */ |
5249 | /* */ |
5250 | /* Check to see if an incoming packet should be changed. ICMP packets are */ |
5251 | /* first checked to see if they match an existing entry (if an error), */ |
5252 | /* otherwise a search of the current NAT table is made. If neither results */ |
5253 | /* in a match then a search for a matching NAT rule is made. Create a new */ |
5254 | /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ |
5255 | /* packet header(s) as required. */ |
5256 | /* ------------------------------------------------------------------------ */ |
5257 | int |
5258 | ipf_nat_checkin(fr_info_t *fin, u_32_t *passp) |
5259 | { |
5260 | ipf_main_softc_t *softc; |
5261 | ipf_nat_softc_t *softn; |
5262 | u_int nflags, natadd; |
5263 | ipnat_t *np, *npnext; |
5264 | int rval, natfailed; |
5265 | struct ifnet *ifp; |
5266 | struct in_addr in; |
5267 | icmphdr_t *icmp; |
5268 | tcphdr_t *tcp; |
5269 | u_short dport; |
5270 | nat_t *nat; |
5271 | u_32_t iph; |
5272 | |
5273 | softc = fin->fin_main_soft; |
5274 | softn = softc->ipf_nat_soft; |
5275 | |
5276 | if (softn->ipf_nat_lock != 0) |
5277 | return 0; |
5278 | if (softn->ipf_nat_stats.ns_rules == 0 && |
5279 | softn->ipf_nat_instances == NULL) |
5280 | return 0; |
5281 | |
5282 | tcp = NULL; |
5283 | icmp = NULL; |
5284 | dport = 0; |
5285 | natadd = 1; |
5286 | nflags = 0; |
5287 | natfailed = 0; |
5288 | ifp = fin->fin_ifp; |
5289 | |
5290 | if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { |
5291 | switch (fin->fin_p) |
5292 | { |
5293 | case IPPROTO_TCP : |
5294 | nflags = IPN_TCP; |
5295 | break; |
5296 | case IPPROTO_UDP : |
5297 | nflags = IPN_UDP; |
5298 | break; |
5299 | case IPPROTO_ICMP : |
5300 | icmp = fin->fin_dp; |
5301 | |
5302 | /* |
5303 | * This is an incoming packet, so the destination is |
5304 | * the icmp_id and the source port equals 0 |
5305 | */ |
5306 | if ((fin->fin_flx & FI_ICMPQUERY) != 0) { |
5307 | nflags = IPN_ICMPQUERY; |
5308 | dport = icmp->icmp_id; |
5309 | } break; |
5310 | default : |
5311 | break; |
5312 | } |
5313 | |
5314 | if ((nflags & IPN_TCPUDP)) { |
5315 | tcp = fin->fin_dp; |
5316 | dport = fin->fin_data[1]; |
5317 | } |
5318 | } |
5319 | |
5320 | in = fin->fin_dst; |
5321 | |
5322 | READ_ENTER(&softc->ipf_nat); |
5323 | |
5324 | if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && |
5325 | (nat = ipf_nat_icmperror(fin, &nflags, NAT_INBOUND))) |
5326 | /*EMPTY*/; |
5327 | else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) |
5328 | natadd = 0; |
5329 | else if ((nat = ipf_nat_inlookup(fin, nflags|NAT_SEARCH, |
5330 | (u_int)fin->fin_p, |
5331 | fin->fin_src, in))) { |
5332 | nflags = nat->nat_flags; |
5333 | } else if (fin->fin_off == 0) { |
5334 | u_32_t hv, msk, rmsk = 0; |
5335 | |
5336 | /* |
5337 | * If there is no current entry in the nat table for this IP#, |
5338 | * create one for it (if there is a matching rule). |
5339 | */ |
5340 | maskloop: |
5341 | msk = softn->ipf_nat_rdr_active_masks[rmsk]; |
5342 | iph = in.s_addr & msk; |
5343 | hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_rdrrules_sz); |
5344 | retry_roundrobin: |
5345 | /* TRACE (iph,msk,rmsk,hv,softn->ipf_nat_rdrrules_sz) */ |
5346 | for (np = softn->ipf_nat_rdr_rules[hv]; np; np = npnext) { |
5347 | npnext = np->in_rnext; |
5348 | if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) |
5349 | continue; |
5350 | if (np->in_v[0] != 4) |
5351 | continue; |
5352 | if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p)) |
5353 | continue; |
5354 | if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) |
5355 | continue; |
5356 | if (np->in_flags & IPN_FILTER) { |
5357 | switch (ipf_nat_match(fin, np)) |
5358 | { |
5359 | case 0 : |
5360 | continue; |
5361 | case -1 : |
5362 | rval = -1; |
5363 | goto inmatchfail; |
5364 | case 1 : |
5365 | default : |
5366 | break; |
5367 | } |
5368 | } else { |
5369 | if ((in.s_addr & np->in_odstmsk) != |
5370 | np->in_odstaddr) |
5371 | continue; |
5372 | if (np->in_odport && |
5373 | ((np->in_dtop < dport) || |
5374 | (dport < np->in_odport))) |
5375 | continue; |
5376 | } |
5377 | |
5378 | if (np->in_plabel != -1) { |
5379 | if (!ipf_proxy_ok(fin, tcp, np)) { |
5380 | continue; |
5381 | } |
5382 | } |
5383 | |
5384 | if (np->in_flags & IPN_NO) { |
5385 | np->in_hits++; |
5386 | break; |
5387 | } |
5388 | |
5389 | MUTEX_ENTER(&softn->ipf_nat_new); |
5390 | /* |
5391 | * If we've matched a round-robin rule but it has |
5392 | * moved in the list since we got it, start over as |
5393 | * this is now no longer correct. |
5394 | */ |
5395 | if (npnext != np->in_rnext) { |
5396 | if ((np->in_flags & IPN_ROUNDR) != 0) { |
5397 | MUTEX_EXIT(&softn->ipf_nat_new); |
5398 | goto retry_roundrobin; |
5399 | } |
5400 | npnext = np->in_rnext; |
5401 | } |
5402 | |
5403 | nat = ipf_nat_add(fin, np, NULL, nflags, NAT_INBOUND); |
5404 | MUTEX_EXIT(&softn->ipf_nat_new); |
5405 | if (nat != NULL) { |
5406 | natfailed = 0; |
5407 | break; |
5408 | } |
5409 | natfailed = -1; |
5410 | } |
5411 | if ((np == NULL) && (rmsk < softn->ipf_nat_rdr_max)) { |
5412 | rmsk++; |
5413 | goto maskloop; |
5414 | } |
5415 | } |
5416 | |
5417 | if (nat != NULL) { |
5418 | rval = ipf_nat_in(fin, nat, natadd, nflags); |
5419 | if (rval == 1) { |
5420 | MUTEX_ENTER(&nat->nat_lock); |
5421 | ipf_nat_update(fin, nat); |
5422 | nat->nat_bytes[0] += fin->fin_plen; |
5423 | nat->nat_pkts[0]++; |
5424 | fin->fin_pktnum = nat->nat_pkts[0]; |
5425 | MUTEX_EXIT(&nat->nat_lock); |
5426 | } |
5427 | } else |
5428 | rval = natfailed; |
5429 | inmatchfail: |
5430 | RWLOCK_EXIT(&softc->ipf_nat); |
5431 | |
5432 | switch (rval) |
5433 | { |
5434 | case -1 : |
5435 | if (passp != NULL) { |
5436 | DT1(frb_natv4in, fr_info_t *, fin); |
5437 | NBUMPSIDED(0, ns_drop); |
5438 | *passp = FR_BLOCK; |
5439 | fin->fin_reason = FRB_NATV4; |
5440 | } |
5441 | fin->fin_flx |= FI_BADNAT; |
5442 | NBUMPSIDED(0, ns_badnat); |
5443 | break; |
5444 | case 0 : |
5445 | NBUMPSIDE(0, ns_ignored); |
5446 | break; |
5447 | case 1 : |
5448 | NBUMPSIDE(0, ns_translated); |
5449 | break; |
5450 | } |
5451 | return rval; |
5452 | } |
5453 | |
5454 | |
5455 | /* ------------------------------------------------------------------------ */ |
5456 | /* Function: ipf_nat_in */ |
5457 | /* Returns: int - -1 == packet failed NAT checks so block it, */ |
5458 | /* 1 == packet was successfully translated. */ |
5459 | /* Parameters: fin(I) - pointer to packet information */ |
5460 | /* nat(I) - pointer to NAT structure */ |
5461 | /* natadd(I) - flag indicating if it is safe to add frag cache */ |
5462 | /* nflags(I) - NAT flags set for this packet */ |
5463 | /* Locks Held: ipf_nat(READ) */ |
5464 | /* */ |
5465 | /* Translate a packet coming "in" on an interface. */ |
5466 | /* ------------------------------------------------------------------------ */ |
5467 | int |
5468 | ipf_nat_in(fr_info_t *fin, nat_t *nat, int natadd, u_32_t nflags) |
5469 | { |
5470 | ipf_main_softc_t *softc = fin->fin_main_soft; |
5471 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
5472 | u_32_t sumd, ipsumd, sum1, sum2; |
5473 | icmphdr_t *icmp; |
5474 | tcphdr_t *tcp; |
5475 | ipnat_t *np; |
5476 | int skip; |
5477 | int i; |
5478 | |
5479 | tcp = NULL; |
5480 | np = nat->nat_ptr; |
5481 | fin->fin_fr = nat->nat_fr; |
5482 | |
5483 | if (np != NULL) { |
5484 | if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) |
5485 | (void) ipf_frag_natnew(softc, fin, 0, nat); |
5486 | |
5487 | /* ------------------------------------------------------------- */ |
5488 | /* A few quick notes: */ |
5489 | /* Following are test conditions prior to calling the */ |
5490 | /* ipf_proxy_check routine. */ |
5491 | /* */ |
5492 | /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ |
5493 | /* with a map rule, we attempt to match the packet's */ |
5494 | /* source port against in_dport, otherwise we'd compare the */ |
5495 | /* packet's destination. */ |
5496 | /* ------------------------------------------------------------- */ |
5497 | if (np->in_apr != NULL) { |
5498 | i = ipf_proxy_check(fin, nat); |
5499 | if (i == -1) { |
5500 | NBUMPSIDED(0, ns_ipf_proxy_fail); |
5501 | return -1; |
5502 | } |
5503 | } |
5504 | } |
5505 | |
5506 | ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); |
5507 | |
5508 | ipsumd = nat->nat_ipsumd; |
5509 | /* |
5510 | * Fix up checksums, not by recalculating them, but |
5511 | * simply computing adjustments. |
5512 | * Why only do this for some platforms on inbound packets ? |
5513 | * Because for those that it is done, IP processing is yet to happen |
5514 | * and so the IPv4 header checksum has not yet been evaluated. |
5515 | * Perhaps it should always be done for the benefit of things like |
5516 | * fast forwarding (so that it doesn't need to be recomputed) but with |
5517 | * header checksum offloading, perhaps it is a moot point. |
5518 | */ |
5519 | |
5520 | switch (nat->nat_dir) |
5521 | { |
5522 | case NAT_INBOUND : |
5523 | if ((fin->fin_flx & FI_ICMPERR) == 0) { |
5524 | fin->fin_ip->ip_src = nat->nat_nsrcip; |
5525 | fin->fin_saddr = nat->nat_nsrcaddr; |
5526 | } else { |
5527 | sum1 = nat->nat_osrcaddr; |
5528 | sum2 = nat->nat_nsrcaddr; |
5529 | CALC_SUMD(sum1, sum2, sumd); |
5530 | ipsumd -= sumd; |
5531 | } |
5532 | fin->fin_ip->ip_dst = nat->nat_ndstip; |
5533 | fin->fin_daddr = nat->nat_ndstaddr; |
5534 | #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ |
5535 | defined(__osf__) || defined(linux) |
5536 | ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, ipsumd, 0); |
5537 | #endif |
5538 | break; |
5539 | |
5540 | case NAT_OUTBOUND : |
5541 | if ((fin->fin_flx & FI_ICMPERR) == 0) { |
5542 | fin->fin_ip->ip_src = nat->nat_odstip; |
5543 | fin->fin_saddr = nat->nat_odstaddr; |
5544 | } else { |
5545 | sum1 = nat->nat_odstaddr; |
5546 | sum2 = nat->nat_ndstaddr; |
5547 | CALC_SUMD(sum1, sum2, sumd); |
5548 | ipsumd -= sumd; |
5549 | } |
5550 | fin->fin_ip->ip_dst = nat->nat_osrcip; |
5551 | fin->fin_daddr = nat->nat_osrcaddr; |
5552 | #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ |
5553 | defined(__osf__) || defined(linux) |
5554 | ipf_fix_incksum(0, &fin->fin_ip->ip_sum, ipsumd, 0); |
5555 | #endif |
5556 | break; |
5557 | |
5558 | case NAT_DIVERTIN : |
5559 | { |
5560 | udphdr_t *uh; |
5561 | ip_t *ip; |
5562 | mb_t *m; |
5563 | |
5564 | m = M_DUP(np->in_divmp); |
5565 | if (m == NULL) { |
5566 | NBUMPSIDED(0, ns_divert_dup); |
5567 | return -1; |
5568 | } |
5569 | |
5570 | ip = MTOD(m, ip_t *); |
5571 | ip->ip_id = htons(ipf_nextipid(fin)); |
5572 | sum1 = ntohs(ip->ip_len); |
5573 | ip->ip_len = ntohs(ip->ip_len); |
5574 | ip->ip_len += fin->fin_plen; |
5575 | ip->ip_len = htons(ip->ip_len); |
5576 | |
5577 | uh = (udphdr_t *)(ip + 1); |
5578 | uh->uh_ulen += fin->fin_plen; |
5579 | uh->uh_ulen = htons(uh->uh_ulen); |
5580 | |
5581 | sum2 = ntohs(ip->ip_id) + ntohs(ip->ip_len); |
5582 | sum2 += ntohs(ip->ip_off) & IP_DF; |
5583 | CALC_SUMD(sum1, sum2, sumd); |
5584 | |
5585 | #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ |
5586 | defined(__osf__) || defined(linux) |
5587 | ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0); |
5588 | #endif |
5589 | PREP_MB_T(fin, m); |
5590 | |
5591 | fin->fin_ip = ip; |
5592 | fin->fin_plen += sizeof(ip_t) + 8; /* UDP + new IPv4 hdr */ |
5593 | fin->fin_dlen += sizeof(ip_t) + 8; /* UDP + old IPv4 hdr */ |
5594 | |
5595 | nflags &= ~IPN_TCPUDPICMP; |
5596 | |
5597 | break; |
5598 | } |
5599 | |
5600 | case NAT_DIVERTOUT : |
5601 | { |
5602 | mb_t *m; |
5603 | |
5604 | skip = ipf_nat_decap(fin, nat); |
5605 | if (skip <= 0) { |
5606 | NBUMPSIDED(0, ns_decap_fail); |
5607 | return -1; |
5608 | } |
5609 | |
5610 | m = fin->fin_m; |
5611 | |
5612 | #if defined(MENTAT) && defined(_KERNEL) |
5613 | m->b_rptr += skip; |
5614 | #else |
5615 | m->m_data += skip; |
5616 | m->m_len -= skip; |
5617 | |
5618 | # ifdef M_PKTHDR |
5619 | if (m->m_flags & M_PKTHDR) |
5620 | m->m_pkthdr.len -= skip; |
5621 | # endif |
5622 | #endif |
5623 | |
5624 | ipf_nat_update(fin, nat); |
5625 | nflags &= ~IPN_TCPUDPICMP; |
5626 | fin->fin_flx |= FI_NATED; |
5627 | if (np != NULL && np->in_tag.ipt_num[0] != 0) |
5628 | fin->fin_nattag = &np->in_tag; |
5629 | return 1; |
5630 | /* NOTREACHED */ |
5631 | } |
5632 | } |
5633 | if (nflags & IPN_TCPUDP) |
5634 | tcp = fin->fin_dp; |
5635 | |
5636 | if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { |
5637 | u_short *csump; |
5638 | |
5639 | if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) { |
5640 | switch (nat->nat_dir) |
5641 | { |
5642 | case NAT_INBOUND : |
5643 | tcp->th_sport = nat->nat_nsport; |
5644 | fin->fin_data[0] = ntohs(nat->nat_nsport); |
5645 | tcp->th_dport = nat->nat_ndport; |
5646 | fin->fin_data[1] = ntohs(nat->nat_ndport); |
5647 | break; |
5648 | |
5649 | case NAT_OUTBOUND : |
5650 | tcp->th_sport = nat->nat_odport; |
5651 | fin->fin_data[0] = ntohs(nat->nat_odport); |
5652 | tcp->th_dport = nat->nat_osport; |
5653 | fin->fin_data[1] = ntohs(nat->nat_osport); |
5654 | break; |
5655 | } |
5656 | } |
5657 | |
5658 | |
5659 | if ((nat->nat_oicmpid != 0) && (nflags & IPN_ICMPQUERY)) { |
5660 | icmp = fin->fin_dp; |
5661 | |
5662 | switch (nat->nat_dir) |
5663 | { |
5664 | case NAT_INBOUND : |
5665 | icmp->icmp_id = nat->nat_nicmpid; |
5666 | break; |
5667 | case NAT_OUTBOUND : |
5668 | icmp->icmp_id = nat->nat_oicmpid; |
5669 | break; |
5670 | } |
5671 | } |
5672 | |
5673 | csump = ipf_nat_proto(fin, nat, nflags); |
5674 | |
5675 | /* |
5676 | * The above comments do not hold for layer 4 (or higher) |
5677 | * checksums... |
5678 | */ |
5679 | if (csump != NULL) { |
5680 | if (nat->nat_dir == NAT_OUTBOUND) |
5681 | ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0); |
5682 | else |
5683 | ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0); |
5684 | } |
5685 | } |
5686 | |
5687 | fin->fin_flx |= FI_NATED; |
5688 | if (np != NULL && np->in_tag.ipt_num[0] != 0) |
5689 | fin->fin_nattag = &np->in_tag; |
5690 | return 1; |
5691 | } |
5692 | |
5693 | |
5694 | /* ------------------------------------------------------------------------ */ |
5695 | /* Function: ipf_nat_proto */ |
5696 | /* Returns: u_short* - pointer to transport header checksum to update, */ |
5697 | /* NULL if the transport protocol is not recognised */ |
5698 | /* as needing a checksum update. */ |
5699 | /* Parameters: fin(I) - pointer to packet information */ |
5700 | /* nat(I) - pointer to NAT structure */ |
5701 | /* nflags(I) - NAT flags set for this packet */ |
5702 | /* */ |
5703 | /* Return the pointer to the checksum field for each protocol so understood.*/ |
5704 | /* If support for making other changes to a protocol header is required, */ |
5705 | /* that is not strictly 'address' translation, such as clamping the MSS in */ |
5706 | /* TCP down to a specific value, then do it from here. */ |
5707 | /* ------------------------------------------------------------------------ */ |
5708 | u_short * |
5709 | ipf_nat_proto(fr_info_t *fin, nat_t *nat, u_int nflags) |
5710 | { |
5711 | icmphdr_t *icmp; |
5712 | u_short *csump; |
5713 | tcphdr_t *tcp; |
5714 | udphdr_t *udp; |
5715 | |
5716 | csump = NULL; |
5717 | if (fin->fin_out == 0) { |
5718 | fin->fin_rev = (nat->nat_dir & NAT_OUTBOUND); |
5719 | } else { |
5720 | fin->fin_rev = ((nat->nat_dir & NAT_OUTBOUND) == 0); |
5721 | } |
5722 | |
5723 | switch (fin->fin_p) |
5724 | { |
5725 | case IPPROTO_TCP : |
5726 | tcp = fin->fin_dp; |
5727 | |
5728 | if ((nflags & IPN_TCP) != 0) |
5729 | csump = &tcp->th_sum; |
5730 | |
5731 | /* |
5732 | * Do a MSS CLAMPING on a SYN packet, |
5733 | * only deal IPv4 for now. |
5734 | */ |
5735 | if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0) |
5736 | ipf_nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump); |
5737 | |
5738 | break; |
5739 | |
5740 | case IPPROTO_UDP : |
5741 | udp = fin->fin_dp; |
5742 | |
5743 | if ((nflags & IPN_UDP) != 0) { |
5744 | if (udp->uh_sum != 0) |
5745 | csump = &udp->uh_sum; |
5746 | } |
5747 | break; |
5748 | |
5749 | case IPPROTO_ICMP : |
5750 | icmp = fin->fin_dp; |
5751 | |
5752 | if ((nflags & IPN_ICMPQUERY) != 0) { |
5753 | if (icmp->icmp_cksum != 0) |
5754 | csump = &icmp->icmp_cksum; |
5755 | } |
5756 | break; |
5757 | |
5758 | #ifdef USE_INET6 |
5759 | case IPPROTO_ICMPV6 : |
5760 | { |
5761 | struct icmp6_hdr *icmp6 = (struct icmp6_hdr *)fin->fin_dp; |
5762 | |
5763 | icmp6 = fin->fin_dp; |
5764 | |
5765 | if ((nflags & IPN_ICMPQUERY) != 0) { |
5766 | if (icmp6->icmp6_cksum != 0) |
5767 | csump = &icmp6->icmp6_cksum; |
5768 | } |
5769 | break; |
5770 | } |
5771 | #endif |
5772 | } |
5773 | return csump; |
5774 | } |
5775 | |
5776 | |
5777 | /* ------------------------------------------------------------------------ */ |
5778 | /* Function: ipf_nat_expire */ |
5779 | /* Returns: Nil */ |
5780 | /* Parameters: softc(I) - pointer to soft context main structure */ |
5781 | /* */ |
5782 | /* Check all of the timeout queues for entries at the top which need to be */ |
5783 | /* expired. */ |
5784 | /* ------------------------------------------------------------------------ */ |
5785 | void |
5786 | ipf_nat_expire(ipf_main_softc_t *softc) |
5787 | { |
5788 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
5789 | ipftq_t *ifq, *ifqnext; |
5790 | ipftqent_t *tqe, *tqn; |
5791 | int i; |
5792 | SPL_INT(s); |
5793 | |
5794 | SPL_NET(s); |
5795 | WRITE_ENTER(&softc->ipf_nat); |
5796 | for (ifq = softn->ipf_nat_tcptq, i = 0; ifq != NULL; |
5797 | ifq = ifq->ifq_next) { |
5798 | for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { |
5799 | if (tqe->tqe_die > softc->ipf_ticks) |
5800 | break; |
5801 | tqn = tqe->tqe_next; |
5802 | ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE); |
5803 | } |
5804 | } |
5805 | |
5806 | for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifq->ifq_next) { |
5807 | for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { |
5808 | if (tqe->tqe_die > softc->ipf_ticks) |
5809 | break; |
5810 | tqn = tqe->tqe_next; |
5811 | ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE); |
5812 | } |
5813 | } |
5814 | |
5815 | for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) { |
5816 | ifqnext = ifq->ifq_next; |
5817 | |
5818 | if (((ifq->ifq_flags & IFQF_DELETE) != 0) && |
5819 | (ifq->ifq_ref == 0)) { |
5820 | ipf_freetimeoutqueue(softc, ifq); |
5821 | } |
5822 | } |
5823 | |
5824 | if (softn->ipf_nat_doflush != 0) { |
5825 | ipf_nat_extraflush(softc, softn, 2); |
5826 | softn->ipf_nat_doflush = 0; |
5827 | } |
5828 | |
5829 | RWLOCK_EXIT(&softc->ipf_nat); |
5830 | SPL_X(s); |
5831 | } |
5832 | |
5833 | |
5834 | /* ------------------------------------------------------------------------ */ |
5835 | /* Function: ipf_nat_sync */ |
5836 | /* Returns: Nil */ |
5837 | /* Parameters: softc(I) - pointer to soft context main structure */ |
5838 | /* ifp(I) - pointer to network interface */ |
5839 | /* */ |
5840 | /* Walk through all of the currently active NAT sessions, looking for those */ |
5841 | /* which need to have their translated address updated. */ |
5842 | /* ------------------------------------------------------------------------ */ |
5843 | void |
5844 | ipf_nat_sync(ipf_main_softc_t *softc, void *ifp) |
5845 | { |
5846 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
5847 | u_32_t sum1, sum2, sumd; |
5848 | i6addr_t in; |
5849 | ipnat_t *n; |
5850 | nat_t *nat; |
5851 | void *ifp2; |
5852 | int idx; |
5853 | SPL_INT(s); |
5854 | |
5855 | if (softc->ipf_running <= 0) |
5856 | return; |
5857 | |
5858 | /* |
5859 | * Change IP addresses for NAT sessions for any protocol except TCP |
5860 | * since it will break the TCP connection anyway. The only rules |
5861 | * which will get changed are those which are "map ... -> 0/32", |
5862 | * where the rule specifies the address is taken from the interface. |
5863 | */ |
5864 | SPL_NET(s); |
5865 | WRITE_ENTER(&softc->ipf_nat); |
5866 | |
5867 | if (softc->ipf_running <= 0) { |
5868 | RWLOCK_EXIT(&softc->ipf_nat); |
5869 | return; |
5870 | } |
5871 | |
5872 | for (nat = softn->ipf_nat_instances; nat; nat = nat->nat_next) { |
5873 | if ((nat->nat_flags & IPN_TCP) != 0) |
5874 | continue; |
5875 | |
5876 | n = nat->nat_ptr; |
5877 | if (n != NULL) { |
5878 | if (n->in_v[1] == 4) { |
5879 | if (n->in_redir & NAT_MAP) { |
5880 | if ((n->in_nsrcaddr != 0) || |
5881 | (n->in_nsrcmsk != 0xffffffff)) |
5882 | continue; |
5883 | } else if (n->in_redir & NAT_REDIRECT) { |
5884 | if ((n->in_ndstaddr != 0) || |
5885 | (n->in_ndstmsk != 0xffffffff)) |
5886 | continue; |
5887 | } |
5888 | } |
5889 | #ifdef USE_INET6 |
5890 | if (n->in_v[1] == 4) { |
5891 | if (n->in_redir & NAT_MAP) { |
5892 | if (!IP6_ISZERO(&n->in_nsrcaddr) || |
5893 | !IP6_ISONES(&n->in_nsrcmsk)) |
5894 | continue; |
5895 | } else if (n->in_redir & NAT_REDIRECT) { |
5896 | if (!IP6_ISZERO(&n->in_ndstaddr) || |
5897 | !IP6_ISONES(&n->in_ndstmsk)) |
5898 | continue; |
5899 | } |
5900 | } |
5901 | #endif |
5902 | } |
5903 | |
5904 | if (((ifp == NULL) || (ifp == nat->nat_ifps[0]) || |
5905 | (ifp == nat->nat_ifps[1]))) { |
5906 | nat->nat_ifps[0] = GETIFP(nat->nat_ifnames[0], |
5907 | nat->nat_v[0]); |
5908 | if ((nat->nat_ifps[0] != NULL) && |
5909 | (nat->nat_ifps[0] != (void *)-1)) { |
5910 | nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); |
5911 | } |
5912 | if (nat->nat_ifnames[1][0] != '\0') { |
5913 | nat->nat_ifps[1] = GETIFP(nat->nat_ifnames[1], |
5914 | nat->nat_v[1]); |
5915 | } else { |
5916 | nat->nat_ifps[1] = nat->nat_ifps[0]; |
5917 | } |
5918 | if ((nat->nat_ifps[1] != NULL) && |
5919 | (nat->nat_ifps[1] != (void *)-1)) { |
5920 | nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); |
5921 | } |
5922 | ifp2 = nat->nat_ifps[0]; |
5923 | if (ifp2 == NULL) |
5924 | continue; |
5925 | |
5926 | /* |
5927 | * Change the map-to address to be the same as the |
5928 | * new one. |
5929 | */ |
5930 | sum1 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6); |
5931 | if (ipf_ifpaddr(softc, nat->nat_v[0], FRI_NORMAL, ifp2, |
5932 | &in, NULL) != -1) { |
5933 | if (nat->nat_v[0] == 4) |
5934 | nat->nat_nsrcip = in.in4; |
5935 | } |
5936 | sum2 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6); |
5937 | |
5938 | if (sum1 == sum2) |
5939 | continue; |
5940 | /* |
5941 | * Readjust the checksum adjustment to take into |
5942 | * account the new IP#. |
5943 | */ |
5944 | CALC_SUMD(sum1, sum2, sumd); |
5945 | /* XXX - dont change for TCP when solaris does |
5946 | * hardware checksumming. |
5947 | */ |
5948 | sumd += nat->nat_sumd[0]; |
5949 | nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); |
5950 | nat->nat_sumd[1] = nat->nat_sumd[0]; |
5951 | } |
5952 | } |
5953 | |
5954 | for (n = softn->ipf_nat_list; (n != NULL); n = n->in_next) { |
5955 | char *base = n->in_names; |
5956 | |
5957 | if ((ifp == NULL) || (n->in_ifps[0] == ifp)) |
5958 | n->in_ifps[0] = ipf_resolvenic(softc, |
5959 | base + n->in_ifnames[0], |
5960 | n->in_v[0]); |
5961 | if ((ifp == NULL) || (n->in_ifps[1] == ifp)) |
5962 | n->in_ifps[1] = ipf_resolvenic(softc, |
5963 | base + n->in_ifnames[1], |
5964 | n->in_v[1]); |
5965 | |
5966 | if (n->in_redir & NAT_REDIRECT) |
5967 | idx = 1; |
5968 | else |
5969 | idx = 0; |
5970 | |
5971 | if (((ifp == NULL) || (n->in_ifps[idx] == ifp)) && |
5972 | (n->in_ifps[idx] != NULL && |
5973 | n->in_ifps[idx] != (void *)-1)) { |
5974 | |
5975 | ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, |
5976 | 0, n->in_ifps[idx]); |
5977 | ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, |
5978 | 0, n->in_ifps[idx]); |
5979 | ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, |
5980 | 0, n->in_ifps[idx]); |
5981 | ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, |
5982 | 0, n->in_ifps[idx]); |
5983 | } |
5984 | } |
5985 | RWLOCK_EXIT(&softc->ipf_nat); |
5986 | SPL_X(s); |
5987 | } |
5988 | |
5989 | |
5990 | /* ------------------------------------------------------------------------ */ |
5991 | /* Function: ipf_nat_icmpquerytype */ |
5992 | /* Returns: int - 1 == success, 0 == failure */ |
5993 | /* Parameters: icmptype(I) - ICMP type number */ |
5994 | /* */ |
5995 | /* Tests to see if the ICMP type number passed is a query/response type or */ |
5996 | /* not. */ |
5997 | /* ------------------------------------------------------------------------ */ |
5998 | static int |
5999 | ipf_nat_icmpquerytype(int icmptype) |
6000 | { |
6001 | |
6002 | /* |
6003 | * For the ICMP query NAT code, it is essential that both the query |
6004 | * and the reply match on the NAT rule. Because the NAT structure |
6005 | * does not keep track of the icmptype, and a single NAT structure |
6006 | * is used for all icmp types with the same src, dest and id, we |
6007 | * simply define the replies as queries as well. The funny thing is, |
6008 | * although it seems silly to call a reply a query, this is exactly |
6009 | * as it is defined in the IPv4 specification |
6010 | */ |
6011 | switch (icmptype) |
6012 | { |
6013 | case ICMP_ECHOREPLY: |
6014 | case ICMP_ECHO: |
6015 | /* route aedvertisement/solliciation is currently unsupported: */ |
6016 | /* it would require rewriting the ICMP data section */ |
6017 | case ICMP_TSTAMP: |
6018 | case ICMP_TSTAMPREPLY: |
6019 | case ICMP_IREQ: |
6020 | case ICMP_IREQREPLY: |
6021 | case ICMP_MASKREQ: |
6022 | case ICMP_MASKREPLY: |
6023 | return 1; |
6024 | default: |
6025 | return 0; |
6026 | } |
6027 | } |
6028 | |
6029 | |
6030 | /* ------------------------------------------------------------------------ */ |
6031 | /* Function: nat_log */ |
6032 | /* Returns: Nil */ |
6033 | /* Parameters: softc(I) - pointer to soft context main structure */ |
6034 | /* softn(I) - pointer to NAT context structure */ |
6035 | /* nat(I) - pointer to NAT structure */ |
6036 | /* action(I) - action related to NAT structure being performed */ |
6037 | /* */ |
6038 | /* Creates a NAT log entry. */ |
6039 | /* ------------------------------------------------------------------------ */ |
6040 | void |
6041 | ipf_nat_log(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, struct nat *nat, |
6042 | u_int action) |
6043 | { |
6044 | #ifdef IPFILTER_LOG |
6045 | # ifndef LARGE_NAT |
6046 | struct ipnat *np; |
6047 | int rulen; |
6048 | # endif |
6049 | struct natlog natl; |
6050 | void *items[1]; |
6051 | size_t sizes[1]; |
6052 | int types[1]; |
6053 | |
6054 | bcopy((char *)&nat->nat_osrc6, (char *)&natl.nl_osrcip, |
6055 | sizeof(natl.nl_osrcip)); |
6056 | bcopy((char *)&nat->nat_nsrc6, (char *)&natl.nl_nsrcip, |
6057 | sizeof(natl.nl_nsrcip)); |
6058 | bcopy((char *)&nat->nat_odst6, (char *)&natl.nl_odstip, |
6059 | sizeof(natl.nl_odstip)); |
6060 | bcopy((char *)&nat->nat_ndst6, (char *)&natl.nl_ndstip, |
6061 | sizeof(natl.nl_ndstip)); |
6062 | |
6063 | natl.nl_bytes[0] = nat->nat_bytes[0]; |
6064 | natl.nl_bytes[1] = nat->nat_bytes[1]; |
6065 | natl.nl_pkts[0] = nat->nat_pkts[0]; |
6066 | natl.nl_pkts[1] = nat->nat_pkts[1]; |
6067 | natl.nl_odstport = nat->nat_odport; |
6068 | natl.nl_osrcport = nat->nat_osport; |
6069 | natl.nl_nsrcport = nat->nat_nsport; |
6070 | natl.nl_ndstport = nat->nat_ndport; |
6071 | natl.nl_p[0] = nat->nat_pr[0]; |
6072 | natl.nl_p[1] = nat->nat_pr[1]; |
6073 | natl.nl_v[0] = nat->nat_v[0]; |
6074 | natl.nl_v[1] = nat->nat_v[1]; |
6075 | natl.nl_type = nat->nat_redir; |
6076 | natl.nl_action = action; |
6077 | natl.nl_rule = -1; |
6078 | |
6079 | bcopy(nat->nat_ifnames[0], natl.nl_ifnames[0], |
6080 | sizeof(nat->nat_ifnames[0])); |
6081 | bcopy(nat->nat_ifnames[1], natl.nl_ifnames[1], |
6082 | sizeof(nat->nat_ifnames[1])); |
6083 | |
6084 | # ifndef LARGE_NAT |
6085 | if (nat->nat_ptr != NULL) { |
6086 | for (rulen = 0, np = softn->ipf_nat_list; np != NULL; |
6087 | np = np->in_next, rulen++) |
6088 | if (np == nat->nat_ptr) { |
6089 | natl.nl_rule = rulen; |
6090 | break; |
6091 | } |
6092 | } |
6093 | # endif |
6094 | items[0] = &natl; |
6095 | sizes[0] = sizeof(natl); |
6096 | types[0] = 0; |
6097 | |
6098 | (void) ipf_log_items(softc, IPL_LOGNAT, NULL, items, sizes, types, 1); |
6099 | #endif |
6100 | } |
6101 | |
6102 | |
6103 | #if defined(__OpenBSD__) |
6104 | /* ------------------------------------------------------------------------ */ |
6105 | /* Function: ipf_nat_ifdetach */ |
6106 | /* Returns: Nil */ |
6107 | /* Parameters: ifp(I) - pointer to network interface */ |
6108 | /* */ |
6109 | /* Compatibility interface for OpenBSD to trigger the correct updating of */ |
6110 | /* interface references within IPFilter. */ |
6111 | /* ------------------------------------------------------------------------ */ |
6112 | void |
6113 | ipf_nat_ifdetach(ifp) |
6114 | void *ifp; |
6115 | { |
6116 | ipf_main_softc_t *softc; |
6117 | |
6118 | softc = ipf_get_softc(0); |
6119 | |
6120 | ipf_sync(ifp); |
6121 | return; |
6122 | } |
6123 | #endif |
6124 | |
6125 | |
6126 | /* ------------------------------------------------------------------------ */ |
6127 | /* Function: ipf_nat_rule_deref */ |
6128 | /* Returns: Nil */ |
6129 | /* Parameters: softc(I) - pointer to soft context main structure */ |
6130 | /* inp(I) - pointer to pointer to NAT rule */ |
6131 | /* Write Locks: ipf_nat */ |
6132 | /* */ |
6133 | /* Dropping the refernce count for a rule means that whatever held the */ |
6134 | /* pointer to this rule (*inp) is no longer interested in it and when the */ |
6135 | /* reference count drops to zero, any resources allocated for the rule can */ |
6136 | /* be released and the rule itself free'd. */ |
6137 | /* ------------------------------------------------------------------------ */ |
6138 | void |
6139 | ipf_nat_rule_deref(ipf_main_softc_t *softc, ipnat_t **inp) |
6140 | { |
6141 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
6142 | ipnat_t *n; |
6143 | |
6144 | n = *inp; |
6145 | *inp = NULL; |
6146 | n->in_use--; |
6147 | if (n->in_use > 0) |
6148 | return; |
6149 | |
6150 | if (n->in_apr != NULL) |
6151 | ipf_proxy_deref(n->in_apr); |
6152 | |
6153 | ipf_nat_rule_fini(softc, n); |
6154 | |
6155 | if (n->in_redir & NAT_REDIRECT) { |
6156 | if ((n->in_flags & IPN_PROXYRULE) == 0) { |
6157 | ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_rdr); |
6158 | } |
6159 | } |
6160 | if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { |
6161 | if ((n->in_flags & IPN_PROXYRULE) == 0) { |
6162 | ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_map); |
6163 | } |
6164 | } |
6165 | |
6166 | if (n->in_tqehead[0] != NULL) { |
6167 | if (ipf_deletetimeoutqueue(n->in_tqehead[0]) == 0) { |
6168 | ipf_freetimeoutqueue(softc, n->in_tqehead[1]); |
6169 | } |
6170 | } |
6171 | |
6172 | if (n->in_tqehead[1] != NULL) { |
6173 | if (ipf_deletetimeoutqueue(n->in_tqehead[1]) == 0) { |
6174 | ipf_freetimeoutqueue(softc, n->in_tqehead[1]); |
6175 | } |
6176 | } |
6177 | |
6178 | if ((n->in_flags & IPN_PROXYRULE) == 0) { |
6179 | ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules); |
6180 | } |
6181 | |
6182 | MUTEX_DESTROY(&n->in_lock); |
6183 | |
6184 | KFREES(n, n->in_size); |
6185 | |
6186 | #if SOLARIS && !defined(INSTANCES) |
6187 | if (softn->ipf_nat_stats.ns_rules == 0) |
6188 | pfil_delayed_copy = 1; |
6189 | #endif |
6190 | } |
6191 | |
6192 | |
6193 | /* ------------------------------------------------------------------------ */ |
6194 | /* Function: ipf_nat_deref */ |
6195 | /* Returns: Nil */ |
6196 | /* Parameters: softc(I) - pointer to soft context main structure */ |
6197 | /* natp(I) - pointer to pointer to NAT table entry */ |
6198 | /* */ |
6199 | /* Decrement the reference counter for this NAT table entry and free it if */ |
6200 | /* there are no more things using it. */ |
6201 | /* */ |
6202 | /* IF nat_ref == 1 when this function is called, then we have an orphan nat */ |
6203 | /* structure *because* it only gets called on paths _after_ nat_ref has been*/ |
6204 | /* incremented. If nat_ref == 1 then we shouldn't decrement it here */ |
6205 | /* because nat_delete() will do that and send nat_ref to -1. */ |
6206 | /* */ |
6207 | /* Holding the lock on nat_lock is required to serialise nat_delete() being */ |
6208 | /* called from a NAT flush ioctl with a deref happening because of a packet.*/ |
6209 | /* ------------------------------------------------------------------------ */ |
6210 | void |
6211 | ipf_nat_deref(ipf_main_softc_t *softc, nat_t **natp) |
6212 | { |
6213 | nat_t *nat; |
6214 | |
6215 | nat = *natp; |
6216 | *natp = NULL; |
6217 | |
6218 | MUTEX_ENTER(&nat->nat_lock); |
6219 | if (nat->nat_ref > 1) { |
6220 | nat->nat_ref--; |
6221 | ASSERT(nat->nat_ref >= 0); |
6222 | MUTEX_EXIT(&nat->nat_lock); |
6223 | return; |
6224 | } |
6225 | MUTEX_EXIT(&nat->nat_lock); |
6226 | |
6227 | WRITE_ENTER(&softc->ipf_nat); |
6228 | ipf_nat_delete(softc, nat, NL_EXPIRE); |
6229 | RWLOCK_EXIT(&softc->ipf_nat); |
6230 | } |
6231 | |
6232 | |
6233 | /* ------------------------------------------------------------------------ */ |
6234 | /* Function: ipf_nat_clone */ |
6235 | /* Returns: ipstate_t* - NULL == cloning failed, */ |
6236 | /* else pointer to new state structure */ |
6237 | /* Parameters: fin(I) - pointer to packet information */ |
6238 | /* is(I) - pointer to master state structure */ |
6239 | /* Write Lock: ipf_nat */ |
6240 | /* */ |
6241 | /* Create a "duplcate" state table entry from the master. */ |
6242 | /* ------------------------------------------------------------------------ */ |
6243 | nat_t * |
6244 | ipf_nat_clone(fr_info_t *fin, nat_t *nat) |
6245 | { |
6246 | ipf_main_softc_t *softc = fin->fin_main_soft; |
6247 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
6248 | frentry_t *fr; |
6249 | nat_t *clone; |
6250 | ipnat_t *np; |
6251 | |
6252 | KMALLOC(clone, nat_t *); |
6253 | if (clone == NULL) { |
6254 | NBUMPSIDED(fin->fin_out, ns_clone_nomem); |
6255 | return NULL; |
6256 | } |
6257 | bcopy((char *)nat, (char *)clone, sizeof(*clone)); |
6258 | |
6259 | MUTEX_NUKE(&clone->nat_lock); |
6260 | |
6261 | clone->nat_rev = fin->fin_rev; |
6262 | clone->nat_aps = NULL; |
6263 | /* |
6264 | * Initialize all these so that ipf_nat_delete() doesn't cause a crash. |
6265 | */ |
6266 | clone->nat_tqe.tqe_pnext = NULL; |
6267 | clone->nat_tqe.tqe_next = NULL; |
6268 | clone->nat_tqe.tqe_ifq = NULL; |
6269 | clone->nat_tqe.tqe_parent = clone; |
6270 | |
6271 | clone->nat_flags &= ~SI_CLONE; |
6272 | clone->nat_flags |= SI_CLONED; |
6273 | |
6274 | if (clone->nat_hm) |
6275 | clone->nat_hm->hm_ref++; |
6276 | |
6277 | if (ipf_nat_insert(softc, softn, clone) == -1) { |
6278 | KFREE(clone); |
6279 | NBUMPSIDED(fin->fin_out, ns_insert_fail); |
6280 | return NULL; |
6281 | } |
6282 | |
6283 | np = clone->nat_ptr; |
6284 | if (np != NULL) { |
6285 | if (softn->ipf_nat_logging) |
6286 | ipf_nat_log(softc, softn, clone, NL_CLONE); |
6287 | np->in_use++; |
6288 | } |
6289 | fr = clone->nat_fr; |
6290 | if (fr != NULL) { |
6291 | MUTEX_ENTER(&fr->fr_lock); |
6292 | fr->fr_ref++; |
6293 | MUTEX_EXIT(&fr->fr_lock); |
6294 | } |
6295 | |
6296 | |
6297 | /* |
6298 | * Because the clone is created outside the normal loop of things and |
6299 | * TCP has special needs in terms of state, initialise the timeout |
6300 | * state of the new NAT from here. |
6301 | */ |
6302 | if (clone->nat_pr[0] == IPPROTO_TCP) { |
6303 | (void) ipf_tcp_age(&clone->nat_tqe, fin, softn->ipf_nat_tcptq, |
6304 | clone->nat_flags, 2); |
6305 | } |
6306 | clone->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, clone); |
6307 | if (softn->ipf_nat_logging) |
6308 | ipf_nat_log(softc, softn, clone, NL_CLONE); |
6309 | return clone; |
6310 | } |
6311 | |
6312 | |
6313 | /* ------------------------------------------------------------------------ */ |
6314 | /* Function: ipf_nat_wildok */ |
6315 | /* Returns: int - 1 == packet's ports match wildcards */ |
6316 | /* 0 == packet's ports don't match wildcards */ |
6317 | /* Parameters: nat(I) - NAT entry */ |
6318 | /* sport(I) - source port */ |
6319 | /* dport(I) - destination port */ |
6320 | /* flags(I) - wildcard flags */ |
6321 | /* dir(I) - packet direction */ |
6322 | /* */ |
6323 | /* Use NAT entry and packet direction to determine which combination of */ |
6324 | /* wildcard flags should be used. */ |
6325 | /* ------------------------------------------------------------------------ */ |
6326 | int |
6327 | ipf_nat_wildok(nat_t *nat, int sport, int dport, int flags, int dir) |
6328 | { |
6329 | /* |
6330 | * When called by dir is set to |
6331 | * nat_inlookup NAT_INBOUND (0) |
6332 | * nat_outlookup NAT_OUTBOUND (1) |
6333 | * |
6334 | * We simply combine the packet's direction in dir with the original |
6335 | * "intended" direction of that NAT entry in nat->nat_dir to decide |
6336 | * which combination of wildcard flags to allow. |
6337 | */ |
6338 | switch ((dir << 1) | (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND))) |
6339 | { |
6340 | case 3: /* outbound packet / outbound entry */ |
6341 | if (((nat->nat_osport == sport) || |
6342 | (flags & SI_W_SPORT)) && |
6343 | ((nat->nat_odport == dport) || |
6344 | (flags & SI_W_DPORT))) |
6345 | return 1; |
6346 | break; |
6347 | case 2: /* outbound packet / inbound entry */ |
6348 | if (((nat->nat_osport == dport) || |
6349 | (flags & SI_W_SPORT)) && |
6350 | ((nat->nat_odport == sport) || |
6351 | (flags & SI_W_DPORT))) |
6352 | return 1; |
6353 | break; |
6354 | case 1: /* inbound packet / outbound entry */ |
6355 | if (((nat->nat_osport == dport) || |
6356 | (flags & SI_W_SPORT)) && |
6357 | ((nat->nat_odport == sport) || |
6358 | (flags & SI_W_DPORT))) |
6359 | return 1; |
6360 | break; |
6361 | case 0: /* inbound packet / inbound entry */ |
6362 | if (((nat->nat_osport == sport) || |
6363 | (flags & SI_W_SPORT)) && |
6364 | ((nat->nat_odport == dport) || |
6365 | (flags & SI_W_DPORT))) |
6366 | return 1; |
6367 | break; |
6368 | default: |
6369 | break; |
6370 | } |
6371 | |
6372 | return(0); |
6373 | } |
6374 | |
6375 | |
6376 | /* ------------------------------------------------------------------------ */ |
6377 | /* Function: nat_mssclamp */ |
6378 | /* Returns: Nil */ |
6379 | /* Parameters: tcp(I) - pointer to TCP header */ |
6380 | /* maxmss(I) - value to clamp the TCP MSS to */ |
6381 | /* fin(I) - pointer to packet information */ |
6382 | /* csump(I) - pointer to TCP checksum */ |
6383 | /* */ |
6384 | /* Check for MSS option and clamp it if necessary. If found and changed, */ |
6385 | /* then the TCP header checksum will be updated to reflect the change in */ |
6386 | /* the MSS. */ |
6387 | /* ------------------------------------------------------------------------ */ |
6388 | static void |
6389 | ipf_nat_mssclamp(tcphdr_t *tcp, u_32_t maxmss, fr_info_t *fin, u_short *csump) |
6390 | { |
6391 | u_char *cp, *ep, opt; |
6392 | int hlen, advance; |
6393 | u_32_t mss, sumd; |
6394 | |
6395 | hlen = TCP_OFF(tcp) << 2; |
6396 | if (hlen > sizeof(*tcp)) { |
6397 | cp = (u_char *)tcp + sizeof(*tcp); |
6398 | ep = (u_char *)tcp + hlen; |
6399 | |
6400 | while (cp < ep) { |
6401 | opt = cp[0]; |
6402 | if (opt == TCPOPT_EOL) |
6403 | break; |
6404 | else if (opt == TCPOPT_NOP) { |
6405 | cp++; |
6406 | continue; |
6407 | } |
6408 | |
6409 | if (cp + 1 >= ep) |
6410 | break; |
6411 | advance = cp[1]; |
6412 | if ((cp + advance > ep) || (advance <= 0)) |
6413 | break; |
6414 | switch (opt) |
6415 | { |
6416 | case TCPOPT_MAXSEG: |
6417 | if (advance != 4) |
6418 | break; |
6419 | mss = cp[2] * 256 + cp[3]; |
6420 | if (mss > maxmss) { |
6421 | cp[2] = maxmss / 256; |
6422 | cp[3] = maxmss & 0xff; |
6423 | CALC_SUMD(mss, maxmss, sumd); |
6424 | ipf_fix_outcksum(0, csump, sumd, 0); |
6425 | } |
6426 | break; |
6427 | default: |
6428 | /* ignore unknown options */ |
6429 | break; |
6430 | } |
6431 | |
6432 | cp += advance; |
6433 | } |
6434 | } |
6435 | } |
6436 | |
6437 | |
6438 | /* ------------------------------------------------------------------------ */ |
6439 | /* Function: ipf_nat_setqueue */ |
6440 | /* Returns: Nil */ |
6441 | /* Parameters: softc(I) - pointer to soft context main structure */ |
6442 | /* softn(I) - pointer to NAT context structure */ |
6443 | /* nat(I)- pointer to NAT structure */ |
6444 | /* Locks: ipf_nat (read or write) */ |
6445 | /* */ |
6446 | /* Put the NAT entry on its default queue entry, using rev as a helped in */ |
6447 | /* determining which queue it should be placed on. */ |
6448 | /* ------------------------------------------------------------------------ */ |
6449 | void |
6450 | ipf_nat_setqueue(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, nat_t *nat) |
6451 | { |
6452 | ipftq_t *oifq, *nifq; |
6453 | int rev = nat->nat_rev; |
6454 | |
6455 | if (nat->nat_ptr != NULL) |
6456 | nifq = nat->nat_ptr->in_tqehead[rev]; |
6457 | else |
6458 | nifq = NULL; |
6459 | |
6460 | if (nifq == NULL) { |
6461 | switch (nat->nat_pr[0]) |
6462 | { |
6463 | case IPPROTO_UDP : |
6464 | nifq = &softn->ipf_nat_udptq; |
6465 | break; |
6466 | case IPPROTO_ICMP : |
6467 | nifq = &softn->ipf_nat_icmptq; |
6468 | break; |
6469 | case IPPROTO_TCP : |
6470 | nifq = softn->ipf_nat_tcptq + |
6471 | nat->nat_tqe.tqe_state[rev]; |
6472 | break; |
6473 | default : |
6474 | nifq = &softn->ipf_nat_iptq; |
6475 | break; |
6476 | } |
6477 | } |
6478 | |
6479 | oifq = nat->nat_tqe.tqe_ifq; |
6480 | /* |
6481 | * If it's currently on a timeout queue, move it from one queue to |
6482 | * another, else put it on the end of the newly determined queue. |
6483 | */ |
6484 | if (oifq != NULL) |
6485 | ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, nifq); |
6486 | else |
6487 | ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, nifq, nat); |
6488 | return; |
6489 | } |
6490 | |
6491 | |
6492 | /* ------------------------------------------------------------------------ */ |
6493 | /* Function: nat_getnext */ |
6494 | /* Returns: int - 0 == ok, else error */ |
6495 | /* Parameters: softc(I) - pointer to soft context main structure */ |
6496 | /* t(I) - pointer to ipftoken structure */ |
6497 | /* itp(I) - pointer to ipfgeniter_t structure */ |
6498 | /* */ |
6499 | /* Fetch the next nat/ipnat structure pointer from the linked list and */ |
6500 | /* copy it out to the storage space pointed to by itp_data. The next item */ |
6501 | /* in the list to look at is put back in the ipftoken struture. */ |
6502 | /* ------------------------------------------------------------------------ */ |
6503 | static int |
6504 | ipf_nat_getnext(ipf_main_softc_t *softc, ipftoken_t *t, ipfgeniter_t *itp, |
6505 | ipfobj_t *objp) |
6506 | { |
6507 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
6508 | hostmap_t *hm, *nexthm = NULL, zerohm; |
6509 | ipnat_t *ipn, *nextipnat = NULL, zeroipn; |
6510 | nat_t *nat, *nextnat = NULL, zeronat; |
6511 | int error = 0; |
6512 | void *nnext; |
6513 | |
6514 | if (itp->igi_nitems != 1) { |
6515 | IPFERROR(60075); |
6516 | return ENOSPC; |
6517 | } |
6518 | |
6519 | READ_ENTER(&softc->ipf_nat); |
6520 | |
6521 | switch (itp->igi_type) |
6522 | { |
6523 | case IPFGENITER_HOSTMAP : |
6524 | hm = t->ipt_data; |
6525 | if (hm == NULL) { |
6526 | nexthm = softn->ipf_hm_maplist; |
6527 | } else { |
6528 | nexthm = hm->hm_next; |
6529 | } |
6530 | if (nexthm != NULL) { |
6531 | ATOMIC_INC32(nexthm->hm_ref); |
6532 | t->ipt_data = nexthm; |
6533 | } else { |
6534 | bzero(&zerohm, sizeof(zerohm)); |
6535 | nexthm = &zerohm; |
6536 | t->ipt_data = NULL; |
6537 | } |
6538 | nnext = nexthm->hm_next; |
6539 | break; |
6540 | |
6541 | case IPFGENITER_IPNAT : |
6542 | ipn = t->ipt_data; |
6543 | if (ipn == NULL) { |
6544 | nextipnat = softn->ipf_nat_list; |
6545 | } else { |
6546 | nextipnat = ipn->in_next; |
6547 | } |
6548 | if (nextipnat != NULL) { |
6549 | ATOMIC_INC32(nextipnat->in_use); |
6550 | t->ipt_data = nextipnat; |
6551 | } else { |
6552 | bzero(&zeroipn, sizeof(zeroipn)); |
6553 | nextipnat = &zeroipn; |
6554 | t->ipt_data = NULL; |
6555 | } |
6556 | nnext = nextipnat->in_next; |
6557 | break; |
6558 | |
6559 | case IPFGENITER_NAT : |
6560 | nat = t->ipt_data; |
6561 | if (nat == NULL) { |
6562 | nextnat = softn->ipf_nat_instances; |
6563 | } else { |
6564 | nextnat = nat->nat_next; |
6565 | } |
6566 | if (nextnat != NULL) { |
6567 | MUTEX_ENTER(&nextnat->nat_lock); |
6568 | nextnat->nat_ref++; |
6569 | MUTEX_EXIT(&nextnat->nat_lock); |
6570 | t->ipt_data = nextnat; |
6571 | } else { |
6572 | bzero(&zeronat, sizeof(zeronat)); |
6573 | nextnat = &zeronat; |
6574 | t->ipt_data = NULL; |
6575 | } |
6576 | nnext = nextnat->nat_next; |
6577 | break; |
6578 | |
6579 | default : |
6580 | RWLOCK_EXIT(&softc->ipf_nat); |
6581 | IPFERROR(60055); |
6582 | return EINVAL; |
6583 | } |
6584 | |
6585 | RWLOCK_EXIT(&softc->ipf_nat); |
6586 | |
6587 | objp->ipfo_ptr = itp->igi_data; |
6588 | |
6589 | switch (itp->igi_type) |
6590 | { |
6591 | case IPFGENITER_HOSTMAP : |
6592 | error = COPYOUT(nexthm, objp->ipfo_ptr, sizeof(*nexthm)); |
6593 | if (error != 0) { |
6594 | IPFERROR(60049); |
6595 | error = EFAULT; |
6596 | } |
6597 | if (hm != NULL) { |
6598 | WRITE_ENTER(&softc->ipf_nat); |
6599 | ipf_nat_hostmapdel(softc, &hm); |
6600 | RWLOCK_EXIT(&softc->ipf_nat); |
6601 | } |
6602 | break; |
6603 | |
6604 | case IPFGENITER_IPNAT : |
6605 | objp->ipfo_size = nextipnat->in_size; |
6606 | objp->ipfo_type = IPFOBJ_IPNAT; |
6607 | error = ipf_outobjk(softc, objp, nextipnat); |
6608 | if (ipn != NULL) { |
6609 | WRITE_ENTER(&softc->ipf_nat); |
6610 | ipf_nat_rule_deref(softc, &ipn); |
6611 | RWLOCK_EXIT(&softc->ipf_nat); |
6612 | } |
6613 | break; |
6614 | |
6615 | case IPFGENITER_NAT : |
6616 | objp->ipfo_size = sizeof(nat_t); |
6617 | objp->ipfo_type = IPFOBJ_NAT; |
6618 | error = ipf_outobjk(softc, objp, nextnat); |
6619 | if (nat != NULL) |
6620 | ipf_nat_deref(softc, &nat); |
6621 | |
6622 | break; |
6623 | } |
6624 | |
6625 | if (nnext == NULL) |
6626 | ipf_token_mark_complete(t); |
6627 | |
6628 | return error; |
6629 | } |
6630 | |
6631 | |
6632 | /* ------------------------------------------------------------------------ */ |
6633 | /* Function: nat_extraflush */ |
6634 | /* Returns: int - 0 == success, -1 == failure */ |
6635 | /* Parameters: softc(I) - pointer to soft context main structure */ |
6636 | /* softn(I) - pointer to NAT context structure */ |
6637 | /* which(I) - how to flush the active NAT table */ |
6638 | /* Write Locks: ipf_nat */ |
6639 | /* */ |
6640 | /* Flush nat tables. Three actions currently defined: */ |
6641 | /* which == 0 : flush all nat table entries */ |
6642 | /* which == 1 : flush TCP connections which have started to close but are */ |
6643 | /* stuck for some reason. */ |
6644 | /* which == 2 : flush TCP connections which have been idle for a long time, */ |
6645 | /* starting at > 4 days idle and working back in successive half-*/ |
6646 | /* days to at most 12 hours old. If this fails to free enough */ |
6647 | /* slots then work backwards in half hour slots to 30 minutes. */ |
6648 | /* If that too fails, then work backwards in 30 second intervals */ |
6649 | /* for the last 30 minutes to at worst 30 seconds idle. */ |
6650 | /* ------------------------------------------------------------------------ */ |
6651 | static int |
6652 | (ipf_main_softc_t *softc, ipf_nat_softc_t *softn, int which) |
6653 | { |
6654 | nat_t *nat, **natp; |
6655 | ipftqent_t *tqn; |
6656 | ipftq_t *ifq; |
6657 | int removed; |
6658 | SPL_INT(s); |
6659 | |
6660 | removed = 0; |
6661 | |
6662 | SPL_NET(s); |
6663 | switch (which) |
6664 | { |
6665 | case 0 : |
6666 | softn->ipf_nat_stats.ns_flush_all++; |
6667 | /* |
6668 | * Style 0 flush removes everything... |
6669 | */ |
6670 | for (natp = &softn->ipf_nat_instances; |
6671 | ((nat = *natp) != NULL); ) { |
6672 | ipf_nat_delete(softc, nat, NL_FLUSH); |
6673 | removed++; |
6674 | } |
6675 | break; |
6676 | |
6677 | case 1 : |
6678 | softn->ipf_nat_stats.ns_flush_closing++; |
6679 | /* |
6680 | * Since we're only interested in things that are closing, |
6681 | * we can start with the appropriate timeout queue. |
6682 | */ |
6683 | for (ifq = softn->ipf_nat_tcptq + IPF_TCPS_CLOSE_WAIT; |
6684 | ifq != NULL; ifq = ifq->ifq_next) { |
6685 | |
6686 | for (tqn = ifq->ifq_head; tqn != NULL; ) { |
6687 | nat = tqn->tqe_parent; |
6688 | tqn = tqn->tqe_next; |
6689 | if (nat->nat_pr[0] != IPPROTO_TCP || |
6690 | nat->nat_pr[1] != IPPROTO_TCP) |
6691 | break; |
6692 | ipf_nat_delete(softc, nat, NL_EXPIRE); |
6693 | removed++; |
6694 | } |
6695 | } |
6696 | |
6697 | /* |
6698 | * Also need to look through the user defined queues. |
6699 | */ |
6700 | for (ifq = softn->ipf_nat_utqe; ifq != NULL; |
6701 | ifq = ifq->ifq_next) { |
6702 | for (tqn = ifq->ifq_head; tqn != NULL; ) { |
6703 | nat = tqn->tqe_parent; |
6704 | tqn = tqn->tqe_next; |
6705 | if (nat->nat_pr[0] != IPPROTO_TCP || |
6706 | nat->nat_pr[1] != IPPROTO_TCP) |
6707 | continue; |
6708 | |
6709 | if ((nat->nat_tcpstate[0] > |
6710 | IPF_TCPS_ESTABLISHED) && |
6711 | (nat->nat_tcpstate[1] > |
6712 | IPF_TCPS_ESTABLISHED)) { |
6713 | ipf_nat_delete(softc, nat, NL_EXPIRE); |
6714 | removed++; |
6715 | } |
6716 | } |
6717 | } |
6718 | break; |
6719 | |
6720 | /* |
6721 | * Args 5-11 correspond to flushing those particular states |
6722 | * for TCP connections. |
6723 | */ |
6724 | case IPF_TCPS_CLOSE_WAIT : |
6725 | case IPF_TCPS_FIN_WAIT_1 : |
6726 | case IPF_TCPS_CLOSING : |
6727 | case IPF_TCPS_LAST_ACK : |
6728 | case IPF_TCPS_FIN_WAIT_2 : |
6729 | case IPF_TCPS_TIME_WAIT : |
6730 | case IPF_TCPS_CLOSED : |
6731 | softn->ipf_nat_stats.ns_flush_state++; |
6732 | tqn = softn->ipf_nat_tcptq[which].ifq_head; |
6733 | while (tqn != NULL) { |
6734 | nat = tqn->tqe_parent; |
6735 | tqn = tqn->tqe_next; |
6736 | ipf_nat_delete(softc, nat, NL_FLUSH); |
6737 | removed++; |
6738 | } |
6739 | break; |
6740 | |
6741 | default : |
6742 | if (which < 30) |
6743 | break; |
6744 | |
6745 | softn->ipf_nat_stats.ns_flush_timeout++; |
6746 | /* |
6747 | * Take a large arbitrary number to mean the number of seconds |
6748 | * for which which consider to be the maximum value we'll allow |
6749 | * the expiration to be. |
6750 | */ |
6751 | which = IPF_TTLVAL(which); |
6752 | for (natp = &softn->ipf_nat_instances; |
6753 | ((nat = *natp) != NULL); ) { |
6754 | if (softc->ipf_ticks - nat->nat_touched > which) { |
6755 | ipf_nat_delete(softc, nat, NL_FLUSH); |
6756 | removed++; |
6757 | } else |
6758 | natp = &nat->nat_next; |
6759 | } |
6760 | break; |
6761 | } |
6762 | |
6763 | if (which != 2) { |
6764 | SPL_X(s); |
6765 | return removed; |
6766 | } |
6767 | |
6768 | softn->ipf_nat_stats.ns_flush_queue++; |
6769 | |
6770 | /* |
6771 | * Asked to remove inactive entries because the table is full, try |
6772 | * again, 3 times, if first attempt failed with a different criteria |
6773 | * each time. The order tried in must be in decreasing age. |
6774 | * Another alternative is to implement random drop and drop N entries |
6775 | * at random until N have been freed up. |
6776 | */ |
6777 | if (softc->ipf_ticks - softn->ipf_nat_last_force_flush > |
6778 | IPF_TTLVAL(5)) { |
6779 | softn->ipf_nat_last_force_flush = softc->ipf_ticks; |
6780 | |
6781 | removed = ipf_queueflush(softc, ipf_nat_flush_entry, |
6782 | softn->ipf_nat_tcptq, |
6783 | softn->ipf_nat_utqe, |
6784 | &softn->ipf_nat_stats.ns_active, |
6785 | softn->ipf_nat_table_sz, |
6786 | softn->ipf_nat_table_wm_low); |
6787 | } |
6788 | |
6789 | SPL_X(s); |
6790 | return removed; |
6791 | } |
6792 | |
6793 | |
6794 | /* ------------------------------------------------------------------------ */ |
6795 | /* Function: ipf_nat_flush_entry */ |
6796 | /* Returns: 0 - always succeeds */ |
6797 | /* Parameters: softc(I) - pointer to soft context main structure */ |
6798 | /* entry(I) - pointer to NAT entry */ |
6799 | /* Write Locks: ipf_nat */ |
6800 | /* */ |
6801 | /* This function is a stepping stone between ipf_queueflush() and */ |
6802 | /* nat_dlete(). It is used so we can provide a uniform interface via the */ |
6803 | /* ipf_queueflush() function. Since the nat_delete() function returns void */ |
6804 | /* we translate that to mean it always succeeds in deleting something. */ |
6805 | /* ------------------------------------------------------------------------ */ |
6806 | static int |
6807 | ipf_nat_flush_entry(ipf_main_softc_t *softc, void *entry) |
6808 | { |
6809 | ipf_nat_delete(softc, entry, NL_FLUSH); |
6810 | return 0; |
6811 | } |
6812 | |
6813 | |
6814 | /* ------------------------------------------------------------------------ */ |
6815 | /* Function: ipf_nat_iterator */ |
6816 | /* Returns: int - 0 == ok, else error */ |
6817 | /* Parameters: softc(I) - pointer to soft context main structure */ |
6818 | /* token(I) - pointer to ipftoken structure */ |
6819 | /* itp(I) - pointer to ipfgeniter_t structure */ |
6820 | /* obj(I) - pointer to data description structure */ |
6821 | /* */ |
6822 | /* This function acts as a handler for the SIOCGENITER ioctls that use a */ |
6823 | /* generic structure to iterate through a list. There are three different */ |
6824 | /* linked lists of NAT related information to go through: NAT rules, active */ |
6825 | /* NAT mappings and the NAT fragment cache. */ |
6826 | /* ------------------------------------------------------------------------ */ |
6827 | static int |
6828 | ipf_nat_iterator(ipf_main_softc_t *softc, ipftoken_t *token, ipfgeniter_t *itp, |
6829 | ipfobj_t *obj) |
6830 | { |
6831 | int error; |
6832 | |
6833 | if (itp->igi_data == NULL) { |
6834 | IPFERROR(60052); |
6835 | return EFAULT; |
6836 | } |
6837 | |
6838 | switch (itp->igi_type) |
6839 | { |
6840 | case IPFGENITER_HOSTMAP : |
6841 | case IPFGENITER_IPNAT : |
6842 | case IPFGENITER_NAT : |
6843 | error = ipf_nat_getnext(softc, token, itp, obj); |
6844 | break; |
6845 | |
6846 | case IPFGENITER_NATFRAG : |
6847 | error = ipf_frag_nat_next(softc, token, itp); |
6848 | break; |
6849 | default : |
6850 | IPFERROR(60053); |
6851 | error = EINVAL; |
6852 | break; |
6853 | } |
6854 | |
6855 | return error; |
6856 | } |
6857 | |
6858 | |
6859 | /* ------------------------------------------------------------------------ */ |
6860 | /* Function: ipf_nat_setpending */ |
6861 | /* Returns: Nil */ |
6862 | /* Parameters: softc(I) - pointer to soft context main structure */ |
6863 | /* nat(I) - pointer to NAT structure */ |
6864 | /* Locks: ipf_nat (read or write) */ |
6865 | /* */ |
6866 | /* Put the NAT entry on to the pending queue - this queue has a very short */ |
6867 | /* lifetime where items are put that can't be deleted straight away because */ |
6868 | /* of locking issues but we want to delete them ASAP, anyway. In calling */ |
6869 | /* this function, it is assumed that the owner (if there is one, as shown */ |
6870 | /* by nat_me) is no longer interested in it. */ |
6871 | /* ------------------------------------------------------------------------ */ |
6872 | void |
6873 | ipf_nat_setpending(ipf_main_softc_t *softc, nat_t *nat) |
6874 | { |
6875 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
6876 | ipftq_t *oifq; |
6877 | |
6878 | oifq = nat->nat_tqe.tqe_ifq; |
6879 | if (oifq != NULL) |
6880 | ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, |
6881 | &softn->ipf_nat_pending); |
6882 | else |
6883 | ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, |
6884 | &softn->ipf_nat_pending, nat); |
6885 | |
6886 | if (nat->nat_me != NULL) { |
6887 | *nat->nat_me = NULL; |
6888 | nat->nat_me = NULL; |
6889 | nat->nat_ref--; |
6890 | ASSERT(nat->nat_ref >= 0); |
6891 | } |
6892 | } |
6893 | |
6894 | |
6895 | /* ------------------------------------------------------------------------ */ |
6896 | /* Function: nat_newrewrite */ |
6897 | /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ |
6898 | /* allow rule to be moved if IPN_ROUNDR is set. */ |
6899 | /* Parameters: fin(I) - pointer to packet information */ |
6900 | /* nat(I) - pointer to NAT entry */ |
6901 | /* ni(I) - pointer to structure with misc. information needed */ |
6902 | /* to create new NAT entry. */ |
6903 | /* Write Lock: ipf_nat */ |
6904 | /* */ |
6905 | /* This function is responsible for setting up an active NAT session where */ |
6906 | /* we are changing both the source and destination parameters at the same */ |
6907 | /* time. The loop in here works differently to elsewhere - each iteration */ |
6908 | /* is responsible for changing a single parameter that can be incremented. */ |
6909 | /* So one pass may increase the source IP#, next source port, next dest. IP#*/ |
6910 | /* and the last destination port for a total of 4 iterations to try each. */ |
6911 | /* This is done to try and exhaustively use the translation space available.*/ |
6912 | /* ------------------------------------------------------------------------ */ |
6913 | static int |
6914 | ipf_nat_newrewrite(fr_info_t *fin, nat_t *nat, natinfo_t *nai) |
6915 | { |
6916 | int src_search = 1; |
6917 | int dst_search = 1; |
6918 | fr_info_t frnat; |
6919 | u_32_t flags; |
6920 | u_short swap; |
6921 | ipnat_t *np; |
6922 | nat_t *natl; |
6923 | int l = 0; |
6924 | int changed; |
6925 | |
6926 | natl = NULL; |
6927 | changed = -1; |
6928 | np = nai->nai_np; |
6929 | flags = nat->nat_flags; |
6930 | bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); |
6931 | |
6932 | nat->nat_hm = NULL; |
6933 | |
6934 | do { |
6935 | changed = -1; |
6936 | /* TRACE (l, src_search, dst_search, np) */ |
6937 | |
6938 | if ((src_search == 0) && (np->in_spnext == 0) && |
6939 | (dst_search == 0) && (np->in_dpnext == 0)) { |
6940 | if (l > 0) |
6941 | return -1; |
6942 | } |
6943 | |
6944 | /* |
6945 | * Find a new source address |
6946 | */ |
6947 | if (ipf_nat_nextaddr(fin, &np->in_nsrc, &frnat.fin_saddr, |
6948 | &frnat.fin_saddr) == -1) { |
6949 | return -1; |
6950 | } |
6951 | |
6952 | if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0xffffffff)) { |
6953 | src_search = 0; |
6954 | if (np->in_stepnext == 0) |
6955 | np->in_stepnext = 1; |
6956 | |
6957 | } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) { |
6958 | src_search = 0; |
6959 | if (np->in_stepnext == 0) |
6960 | np->in_stepnext = 1; |
6961 | |
6962 | } else if (np->in_nsrcmsk == 0xffffffff) { |
6963 | src_search = 0; |
6964 | if (np->in_stepnext == 0) |
6965 | np->in_stepnext = 1; |
6966 | |
6967 | } else if (np->in_nsrcmsk != 0xffffffff) { |
6968 | if (np->in_stepnext == 0 && changed == -1) { |
6969 | np->in_snip++; |
6970 | np->in_stepnext++; |
6971 | changed = 0; |
6972 | } |
6973 | } |
6974 | |
6975 | if ((flags & IPN_TCPUDPICMP) != 0) { |
6976 | if (np->in_spnext != 0) |
6977 | frnat.fin_data[0] = np->in_spnext; |
6978 | |
6979 | /* |
6980 | * Standard port translation. Select next port. |
6981 | */ |
6982 | if ((flags & IPN_FIXEDSPORT) != 0) { |
6983 | np->in_stepnext = 2; |
6984 | } else if ((np->in_stepnext == 1) && |
6985 | (changed == -1) && (natl != NULL)) { |
6986 | np->in_spnext++; |
6987 | np->in_stepnext++; |
6988 | changed = 1; |
6989 | if (np->in_spnext > np->in_spmax) |
6990 | np->in_spnext = np->in_spmin; |
6991 | } |
6992 | } else { |
6993 | np->in_stepnext = 2; |
6994 | } |
6995 | np->in_stepnext &= 0x3; |
6996 | |
6997 | /* |
6998 | * Find a new destination address |
6999 | */ |
7000 | /* TRACE (fin, np, l, frnat) */ |
7001 | |
7002 | if (ipf_nat_nextaddr(fin, &np->in_ndst, &frnat.fin_daddr, |
7003 | &frnat.fin_daddr) == -1) |
7004 | return -1; |
7005 | if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) { |
7006 | dst_search = 0; |
7007 | if (np->in_stepnext == 2) |
7008 | np->in_stepnext = 3; |
7009 | |
7010 | } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0)) { |
7011 | dst_search = 0; |
7012 | if (np->in_stepnext == 2) |
7013 | np->in_stepnext = 3; |
7014 | |
7015 | } else if (np->in_ndstmsk == 0xffffffff) { |
7016 | dst_search = 0; |
7017 | if (np->in_stepnext == 2) |
7018 | np->in_stepnext = 3; |
7019 | |
7020 | } else if (np->in_ndstmsk != 0xffffffff) { |
7021 | if ((np->in_stepnext == 2) && (changed == -1) && |
7022 | (natl != NULL)) { |
7023 | changed = 2; |
7024 | np->in_stepnext++; |
7025 | np->in_dnip++; |
7026 | } |
7027 | } |
7028 | |
7029 | if ((flags & IPN_TCPUDPICMP) != 0) { |
7030 | if (np->in_dpnext != 0) |
7031 | frnat.fin_data[1] = np->in_dpnext; |
7032 | |
7033 | /* |
7034 | * Standard port translation. Select next port. |
7035 | */ |
7036 | if ((flags & IPN_FIXEDDPORT) != 0) { |
7037 | np->in_stepnext = 0; |
7038 | } else if (np->in_stepnext == 3 && changed == -1) { |
7039 | np->in_dpnext++; |
7040 | np->in_stepnext++; |
7041 | changed = 3; |
7042 | if (np->in_dpnext > np->in_dpmax) |
7043 | np->in_dpnext = np->in_dpmin; |
7044 | } |
7045 | } else { |
7046 | if (np->in_stepnext == 3) |
7047 | np->in_stepnext = 0; |
7048 | } |
7049 | |
7050 | /* TRACE (frnat) */ |
7051 | |
7052 | /* |
7053 | * Here we do a lookup of the connection as seen from |
7054 | * the outside. If an IP# pair already exists, try |
7055 | * again. So if you have A->B becomes C->B, you can |
7056 | * also have D->E become C->E but not D->B causing |
7057 | * another C->B. Also take protocol and ports into |
7058 | * account when determining whether a pre-existing |
7059 | * NAT setup will cause an external conflict where |
7060 | * this is appropriate. |
7061 | * |
7062 | * fin_data[] is swapped around because we are doing a |
7063 | * lookup of the packet is if it were moving in the opposite |
7064 | * direction of the one we are working with now. |
7065 | */ |
7066 | if (flags & IPN_TCPUDP) { |
7067 | swap = frnat.fin_data[0]; |
7068 | frnat.fin_data[0] = frnat.fin_data[1]; |
7069 | frnat.fin_data[1] = swap; |
7070 | } |
7071 | if (fin->fin_out == 1) { |
7072 | natl = ipf_nat_inlookup(&frnat, |
7073 | flags & ~(SI_WILDP|NAT_SEARCH), |
7074 | (u_int)frnat.fin_p, |
7075 | frnat.fin_dst, frnat.fin_src); |
7076 | |
7077 | } else { |
7078 | natl = ipf_nat_outlookup(&frnat, |
7079 | flags & ~(SI_WILDP|NAT_SEARCH), |
7080 | (u_int)frnat.fin_p, |
7081 | frnat.fin_dst, frnat.fin_src); |
7082 | } |
7083 | if (flags & IPN_TCPUDP) { |
7084 | swap = frnat.fin_data[0]; |
7085 | frnat.fin_data[0] = frnat.fin_data[1]; |
7086 | frnat.fin_data[1] = swap; |
7087 | } |
7088 | |
7089 | /* TRACE natl, in_stepnext, l */ |
7090 | |
7091 | if ((natl != NULL) && (l > 8)) /* XXX 8 is arbitrary */ |
7092 | return -1; |
7093 | |
7094 | np->in_stepnext &= 0x3; |
7095 | |
7096 | l++; |
7097 | changed = -1; |
7098 | } while (natl != NULL); |
7099 | |
7100 | nat->nat_osrcip = fin->fin_src; |
7101 | nat->nat_odstip = fin->fin_dst; |
7102 | nat->nat_nsrcip = frnat.fin_src; |
7103 | nat->nat_ndstip = frnat.fin_dst; |
7104 | |
7105 | if ((flags & IPN_TCPUDP) != 0) { |
7106 | nat->nat_osport = htons(fin->fin_data[0]); |
7107 | nat->nat_odport = htons(fin->fin_data[1]); |
7108 | nat->nat_nsport = htons(frnat.fin_data[0]); |
7109 | nat->nat_ndport = htons(frnat.fin_data[1]); |
7110 | } else if ((flags & IPN_ICMPQUERY) != 0) { |
7111 | nat->nat_oicmpid = fin->fin_data[1]; |
7112 | nat->nat_nicmpid = frnat.fin_data[1]; |
7113 | } |
7114 | |
7115 | return 0; |
7116 | } |
7117 | |
7118 | |
7119 | /* ------------------------------------------------------------------------ */ |
7120 | /* Function: nat_newdivert */ |
7121 | /* Returns: int - -1 == error, 0 == success */ |
7122 | /* Parameters: fin(I) - pointer to packet information */ |
7123 | /* nat(I) - pointer to NAT entry */ |
7124 | /* ni(I) - pointer to structure with misc. information needed */ |
7125 | /* to create new NAT entry. */ |
7126 | /* Write Lock: ipf_nat */ |
7127 | /* */ |
7128 | /* Create a new NAT divert session as defined by the NAT rule. This is */ |
7129 | /* somewhat different to other NAT session creation routines because we */ |
7130 | /* do not iterate through either port numbers or IP addresses, searching */ |
7131 | /* for a unique mapping, however, a complimentary duplicate check is made. */ |
7132 | /* ------------------------------------------------------------------------ */ |
7133 | static int |
7134 | ipf_nat_newdivert(fr_info_t *fin, nat_t *nat, natinfo_t *nai) |
7135 | { |
7136 | ipf_main_softc_t *softc = fin->fin_main_soft; |
7137 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
7138 | fr_info_t frnat; |
7139 | ipnat_t *np; |
7140 | nat_t *natl; |
7141 | int p; |
7142 | |
7143 | np = nai->nai_np; |
7144 | bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); |
7145 | |
7146 | nat->nat_pr[0] = 0; |
7147 | nat->nat_osrcaddr = fin->fin_saddr; |
7148 | nat->nat_odstaddr = fin->fin_daddr; |
7149 | frnat.fin_saddr = htonl(np->in_snip); |
7150 | frnat.fin_daddr = htonl(np->in_dnip); |
7151 | if ((nat->nat_flags & IPN_TCPUDP) != 0) { |
7152 | nat->nat_osport = htons(fin->fin_data[0]); |
7153 | nat->nat_odport = htons(fin->fin_data[1]); |
7154 | } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { |
7155 | nat->nat_oicmpid = fin->fin_data[1]; |
7156 | } |
7157 | |
7158 | if (np->in_redir & NAT_DIVERTUDP) { |
7159 | frnat.fin_data[0] = np->in_spnext; |
7160 | frnat.fin_data[1] = np->in_dpnext; |
7161 | frnat.fin_flx |= FI_TCPUDP; |
7162 | p = IPPROTO_UDP; |
7163 | } else { |
7164 | frnat.fin_flx &= ~FI_TCPUDP; |
7165 | p = IPPROTO_IPIP; |
7166 | } |
7167 | |
7168 | if (fin->fin_out == 1) { |
7169 | natl = ipf_nat_inlookup(&frnat, 0, p, |
7170 | frnat.fin_dst, frnat.fin_src); |
7171 | |
7172 | } else { |
7173 | natl = ipf_nat_outlookup(&frnat, 0, p, |
7174 | frnat.fin_dst, frnat.fin_src); |
7175 | } |
7176 | |
7177 | if (natl != NULL) { |
7178 | NBUMPSIDED(fin->fin_out, ns_divert_exist); |
7179 | return -1; |
7180 | } |
7181 | |
7182 | nat->nat_nsrcaddr = frnat.fin_saddr; |
7183 | nat->nat_ndstaddr = frnat.fin_daddr; |
7184 | if ((nat->nat_flags & IPN_TCPUDP) != 0) { |
7185 | nat->nat_nsport = htons(frnat.fin_data[0]); |
7186 | nat->nat_ndport = htons(frnat.fin_data[1]); |
7187 | } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { |
7188 | nat->nat_nicmpid = frnat.fin_data[1]; |
7189 | } |
7190 | |
7191 | nat->nat_pr[fin->fin_out] = fin->fin_p; |
7192 | nat->nat_pr[1 - fin->fin_out] = p; |
7193 | |
7194 | if (np->in_redir & NAT_REDIRECT) |
7195 | nat->nat_dir = NAT_DIVERTIN; |
7196 | else |
7197 | nat->nat_dir = NAT_DIVERTOUT; |
7198 | |
7199 | return 0; |
7200 | } |
7201 | |
7202 | |
7203 | /* ------------------------------------------------------------------------ */ |
7204 | /* Function: nat_builddivertmp */ |
7205 | /* Returns: int - -1 == error, 0 == success */ |
7206 | /* Parameters: softn(I) - pointer to NAT context structure */ |
7207 | /* np(I) - pointer to a NAT rule */ |
7208 | /* */ |
7209 | /* For divert rules, a skeleton packet representing what will be prepended */ |
7210 | /* to the real packet is created. Even though we don't have the full */ |
7211 | /* packet here, a checksum is calculated that we update later when we */ |
7212 | /* fill in the final details. At present a 0 checksum for UDP is being set */ |
7213 | /* here because it is expected that divert will be used for localhost. */ |
7214 | /* ------------------------------------------------------------------------ */ |
7215 | static int |
7216 | ipf_nat_builddivertmp(ipf_nat_softc_t *softn, ipnat_t *np) |
7217 | { |
7218 | udphdr_t *uh; |
7219 | size_t len; |
7220 | ip_t *ip; |
7221 | |
7222 | if ((np->in_redir & NAT_DIVERTUDP) != 0) |
7223 | len = sizeof(ip_t) + sizeof(udphdr_t); |
7224 | else |
7225 | len = sizeof(ip_t); |
7226 | |
7227 | ALLOC_MB_T(np->in_divmp, len); |
7228 | if (np->in_divmp == NULL) { |
7229 | NBUMPD(ipf_nat_stats, ns_divert_build); |
7230 | return -1; |
7231 | } |
7232 | |
7233 | /* |
7234 | * First, the header to get the packet diverted to the new destination |
7235 | */ |
7236 | ip = MTOD(np->in_divmp, ip_t *); |
7237 | IP_V_A(ip, 4); |
7238 | IP_HL_A(ip, 5); |
7239 | ip->ip_tos = 0; |
7240 | if ((np->in_redir & NAT_DIVERTUDP) != 0) |
7241 | ip->ip_p = IPPROTO_UDP; |
7242 | else |
7243 | ip->ip_p = IPPROTO_IPIP; |
7244 | ip->ip_ttl = 255; |
7245 | ip->ip_off = 0; |
7246 | ip->ip_sum = 0; |
7247 | ip->ip_len = htons(len); |
7248 | ip->ip_id = 0; |
7249 | ip->ip_src.s_addr = htonl(np->in_snip); |
7250 | ip->ip_dst.s_addr = htonl(np->in_dnip); |
7251 | ip->ip_sum = ipf_cksum((u_short *)ip, sizeof(*ip)); |
7252 | |
7253 | if (np->in_redir & NAT_DIVERTUDP) { |
7254 | uh = (udphdr_t *)(ip + 1); |
7255 | uh->uh_sum = 0; |
7256 | uh->uh_ulen = 8; |
7257 | uh->uh_sport = htons(np->in_spnext); |
7258 | uh->uh_dport = htons(np->in_dpnext); |
7259 | } |
7260 | |
7261 | return 0; |
7262 | } |
7263 | |
7264 | |
7265 | #define MINDECAP (sizeof(ip_t) + sizeof(udphdr_t) + sizeof(ip_t)) |
7266 | |
7267 | /* ------------------------------------------------------------------------ */ |
7268 | /* Function: nat_decap */ |
7269 | /* Returns: int - -1 == error, 0 == success */ |
7270 | /* Parameters: fin(I) - pointer to packet information */ |
7271 | /* nat(I) - pointer to current NAT session */ |
7272 | /* */ |
7273 | /* This function is responsible for undoing a packet's encapsulation in the */ |
7274 | /* reverse of an encap/divert rule. After removing the outer encapsulation */ |
7275 | /* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/ |
7276 | /* match the "new" packet as it may still be used by IPFilter elsewhere. */ |
7277 | /* We use "dir" here as the basis for some of the expectations about the */ |
7278 | /* outer header. If we return an error, the goal is to leave the original */ |
7279 | /* packet information undisturbed - this falls short at the end where we'd */ |
7280 | /* need to back a backup copy of "fin" - expensive. */ |
7281 | /* ------------------------------------------------------------------------ */ |
7282 | static int |
7283 | ipf_nat_decap(fr_info_t *fin, nat_t *nat) |
7284 | { |
7285 | ipf_main_softc_t *softc = fin->fin_main_soft; |
7286 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
7287 | char *hdr; |
7288 | int hlen; |
7289 | int skip; |
7290 | mb_t *m; |
7291 | |
7292 | if ((fin->fin_flx & FI_ICMPERR) != 0) { |
7293 | /* |
7294 | * ICMP packets don't get decapsulated, instead what we need |
7295 | * to do is change the ICMP reply from including (in the data |
7296 | * portion for errors) the encapsulated packet that we sent |
7297 | * out to something that resembles the original packet prior |
7298 | * to encapsulation. This isn't done here - all we're doing |
7299 | * here is changing the outer address to ensure that it gets |
7300 | * targetted back to the correct system. |
7301 | */ |
7302 | |
7303 | if (nat->nat_dir & NAT_OUTBOUND) { |
7304 | u_32_t sum1, sum2, sumd; |
7305 | |
7306 | sum1 = ntohl(fin->fin_daddr); |
7307 | sum2 = ntohl(nat->nat_osrcaddr); |
7308 | CALC_SUMD(sum1, sum2, sumd); |
7309 | fin->fin_ip->ip_dst = nat->nat_osrcip; |
7310 | fin->fin_daddr = nat->nat_osrcaddr; |
7311 | #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ |
7312 | defined(__osf__) || defined(linux) |
7313 | ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, sumd, 0); |
7314 | #endif |
7315 | } |
7316 | return 0; |
7317 | } |
7318 | |
7319 | m = fin->fin_m; |
7320 | skip = fin->fin_hlen; |
7321 | |
7322 | switch (nat->nat_dir) |
7323 | { |
7324 | case NAT_DIVERTIN : |
7325 | case NAT_DIVERTOUT : |
7326 | if (fin->fin_plen < MINDECAP) |
7327 | return -1; |
7328 | skip += sizeof(udphdr_t); |
7329 | break; |
7330 | |
7331 | case NAT_ENCAPIN : |
7332 | case NAT_ENCAPOUT : |
7333 | if (fin->fin_plen < (skip + sizeof(ip_t))) |
7334 | return -1; |
7335 | break; |
7336 | default : |
7337 | return -1; |
7338 | /* NOTREACHED */ |
7339 | } |
7340 | |
7341 | /* |
7342 | * The aim here is to keep the original packet details in "fin" for |
7343 | * as long as possible so that returning with an error is for the |
7344 | * original packet and there is little undoing work to do. |
7345 | */ |
7346 | if (M_LEN(m) < skip + sizeof(ip_t)) { |
7347 | if (ipf_pr_pullup(fin, skip + sizeof(ip_t)) == -1) |
7348 | return -1; |
7349 | } |
7350 | |
7351 | hdr = MTOD(fin->fin_m, char *); |
7352 | fin->fin_ip = (ip_t *)(hdr + skip); |
7353 | hlen = IP_HL(fin->fin_ip) << 2; |
7354 | |
7355 | if (ipf_pr_pullup(fin, skip + hlen) == -1) { |
7356 | NBUMPSIDED(fin->fin_out, ns_decap_pullup); |
7357 | return -1; |
7358 | } |
7359 | |
7360 | fin->fin_hlen = hlen; |
7361 | fin->fin_dlen -= skip; |
7362 | fin->fin_plen -= skip; |
7363 | fin->fin_ipoff += skip; |
7364 | |
7365 | if (ipf_makefrip(hlen, (ip_t *)hdr, fin) == -1) { |
7366 | NBUMPSIDED(fin->fin_out, ns_decap_bad); |
7367 | return -1; |
7368 | } |
7369 | |
7370 | return skip; |
7371 | } |
7372 | |
7373 | |
7374 | /* ------------------------------------------------------------------------ */ |
7375 | /* Function: nat_nextaddr */ |
7376 | /* Returns: int - -1 == bad input (no new address), */ |
7377 | /* 0 == success and dst has new address */ |
7378 | /* Parameters: fin(I) - pointer to packet information */ |
7379 | /* na(I) - how to generate new address */ |
7380 | /* old(I) - original address being replaced */ |
7381 | /* dst(O) - where to put the new address */ |
7382 | /* Write Lock: ipf_nat */ |
7383 | /* */ |
7384 | /* This function uses the contents of the "na" structure, in combination */ |
7385 | /* with "old" to produce a new address to store in "dst". Not all of the */ |
7386 | /* possible uses of "na" will result in a new address. */ |
7387 | /* ------------------------------------------------------------------------ */ |
7388 | static int |
7389 | ipf_nat_nextaddr(fr_info_t *fin, nat_addr_t *na, u_32_t *old, u_32_t *dst) |
7390 | { |
7391 | ipf_main_softc_t *softc = fin->fin_main_soft; |
7392 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
7393 | u_32_t amin, amax, new; |
7394 | i6addr_t newip; |
7395 | int error; |
7396 | |
7397 | new = 0; |
7398 | amin = na->na_addr[0].in4.s_addr; |
7399 | |
7400 | switch (na->na_atype) |
7401 | { |
7402 | case FRI_RANGE : |
7403 | amax = na->na_addr[1].in4.s_addr; |
7404 | break; |
7405 | |
7406 | case FRI_NETMASKED : |
7407 | case FRI_DYNAMIC : |
7408 | case FRI_NORMAL : |
7409 | /* |
7410 | * Compute the maximum address by adding the inverse of the |
7411 | * netmask to the minimum address. |
7412 | */ |
7413 | amax = ~na->na_addr[1].in4.s_addr; |
7414 | amax |= amin; |
7415 | break; |
7416 | |
7417 | case FRI_LOOKUP : |
7418 | break; |
7419 | |
7420 | case FRI_BROADCAST : |
7421 | case FRI_PEERADDR : |
7422 | case FRI_NETWORK : |
7423 | default : |
7424 | return -1; |
7425 | } |
7426 | |
7427 | error = -1; |
7428 | |
7429 | if (na->na_atype == FRI_LOOKUP) { |
7430 | if (na->na_type == IPLT_DSTLIST) { |
7431 | error = ipf_dstlist_select_node(fin, na->na_ptr, dst, |
7432 | NULL); |
7433 | } else { |
7434 | NBUMPSIDE(fin->fin_out, ns_badnextaddr); |
7435 | } |
7436 | |
7437 | } else if (na->na_atype == IPLT_NONE) { |
7438 | /* |
7439 | * 0/0 as the new address means leave it alone. |
7440 | */ |
7441 | if (na->na_addr[0].in4.s_addr == 0 && |
7442 | na->na_addr[1].in4.s_addr == 0) { |
7443 | new = *old; |
7444 | |
7445 | /* |
7446 | * 0/32 means get the interface's address |
7447 | */ |
7448 | } else if (na->na_addr[0].in4.s_addr == 0 && |
7449 | na->na_addr[1].in4.s_addr == 0xffffffff) { |
7450 | if (ipf_ifpaddr(softc, 4, na->na_atype, |
7451 | fin->fin_ifp, &newip, NULL) == -1) { |
7452 | NBUMPSIDED(fin->fin_out, ns_ifpaddrfail); |
7453 | return -1; |
7454 | } |
7455 | new = newip.in4.s_addr; |
7456 | } else { |
7457 | new = htonl(na->na_nextip); |
7458 | } |
7459 | *dst = new; |
7460 | error = 0; |
7461 | |
7462 | } else { |
7463 | NBUMPSIDE(fin->fin_out, ns_badnextaddr); |
7464 | } |
7465 | |
7466 | return error; |
7467 | } |
7468 | |
7469 | |
7470 | /* ------------------------------------------------------------------------ */ |
7471 | /* Function: nat_nextaddrinit */ |
7472 | /* Returns: int - 0 == success, else error number */ |
7473 | /* Parameters: softc(I) - pointer to soft context main structure */ |
7474 | /* na(I) - NAT address information for generating new addr*/ |
7475 | /* initial(I) - flag indicating if it is the first call for */ |
7476 | /* this "na" structure. */ |
7477 | /* ifp(I) - network interface to derive address */ |
7478 | /* information from. */ |
7479 | /* */ |
7480 | /* This function is expected to be called in two scenarious: when a new NAT */ |
7481 | /* rule is loaded into the kernel and when the list of NAT rules is sync'd */ |
7482 | /* up with the valid network interfaces (possibly due to them changing.) */ |
7483 | /* To distinguish between these, the "initial" parameter is used. If it is */ |
7484 | /* 1 then this indicates the rule has just been reloaded and 0 for when we */ |
7485 | /* are updating information. This difference is important because in */ |
7486 | /* instances where we are not updating address information associated with */ |
7487 | /* a network interface, we don't want to disturb what the "next" address to */ |
7488 | /* come out of ipf_nat_nextaddr() will be. */ |
7489 | /* ------------------------------------------------------------------------ */ |
7490 | static int |
7491 | ipf_nat_nextaddrinit(ipf_main_softc_t *softc, char *base, nat_addr_t *na, |
7492 | int initial, void *ifp) |
7493 | { |
7494 | |
7495 | switch (na->na_atype) |
7496 | { |
7497 | case FRI_LOOKUP : |
7498 | if (na->na_subtype == 0) { |
7499 | na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT, |
7500 | na->na_type, |
7501 | na->na_num, |
7502 | &na->na_func); |
7503 | } else if (na->na_subtype == 1) { |
7504 | na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT, |
7505 | na->na_type, |
7506 | base + na->na_num, |
7507 | &na->na_func); |
7508 | } |
7509 | if (na->na_func == NULL) { |
7510 | IPFERROR(60060); |
7511 | return ESRCH; |
7512 | } |
7513 | if (na->na_ptr == NULL) { |
7514 | IPFERROR(60056); |
7515 | return ESRCH; |
7516 | } |
7517 | break; |
7518 | |
7519 | case FRI_DYNAMIC : |
7520 | case FRI_BROADCAST : |
7521 | case FRI_NETWORK : |
7522 | case FRI_NETMASKED : |
7523 | case FRI_PEERADDR : |
7524 | if (ifp != NULL) |
7525 | (void )ipf_ifpaddr(softc, 4, na->na_atype, ifp, |
7526 | &na->na_addr[0], &na->na_addr[1]); |
7527 | break; |
7528 | |
7529 | case FRI_SPLIT : |
7530 | case FRI_RANGE : |
7531 | if (initial) |
7532 | na->na_nextip = ntohl(na->na_addr[0].in4.s_addr); |
7533 | break; |
7534 | |
7535 | case FRI_NONE : |
7536 | na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr; |
7537 | return 0; |
7538 | |
7539 | case FRI_NORMAL : |
7540 | na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr; |
7541 | break; |
7542 | |
7543 | default : |
7544 | IPFERROR(60054); |
7545 | return EINVAL; |
7546 | } |
7547 | |
7548 | if (initial && (na->na_atype == FRI_NORMAL)) { |
7549 | if (na->na_addr[0].in4.s_addr == 0) { |
7550 | if ((na->na_addr[1].in4.s_addr == 0xffffffff) || |
7551 | (na->na_addr[1].in4.s_addr == 0)) { |
7552 | return 0; |
7553 | } |
7554 | } |
7555 | |
7556 | if (na->na_addr[1].in4.s_addr == 0xffffffff) { |
7557 | na->na_nextip = ntohl(na->na_addr[0].in4.s_addr); |
7558 | } else { |
7559 | na->na_nextip = ntohl(na->na_addr[0].in4.s_addr) + 1; |
7560 | } |
7561 | } |
7562 | |
7563 | return 0; |
7564 | } |
7565 | |
7566 | |
7567 | /* ------------------------------------------------------------------------ */ |
7568 | /* Function: ipf_nat_matchflush */ |
7569 | /* Returns: int - -1 == error, 0 == success */ |
7570 | /* Parameters: softc(I) - pointer to soft context main structure */ |
7571 | /* softn(I) - pointer to NAT context structure */ |
7572 | /* nat(I) - pointer to current NAT session */ |
7573 | /* */ |
7574 | /* ------------------------------------------------------------------------ */ |
7575 | static int |
7576 | ipf_nat_matchflush(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, void *data) |
7577 | { |
7578 | int *array, flushed, error; |
7579 | nat_t *nat, *natnext; |
7580 | ipfobj_t obj; |
7581 | |
7582 | error = ipf_matcharray_load(softc, data, &obj, &array); |
7583 | if (error != 0) |
7584 | return error; |
7585 | |
7586 | flushed = 0; |
7587 | |
7588 | for (nat = softn->ipf_nat_instances; nat != NULL; nat = natnext) { |
7589 | natnext = nat->nat_next; |
7590 | if (ipf_nat_matcharray(nat, array, softc->ipf_ticks) == 0) { |
7591 | ipf_nat_delete(softc, nat, NL_FLUSH); |
7592 | flushed++; |
7593 | } |
7594 | } |
7595 | |
7596 | obj.ipfo_retval = flushed; |
7597 | error = BCOPYOUT(&obj, data, sizeof(obj)); |
7598 | |
7599 | KFREES(array, array[0] * sizeof(*array)); |
7600 | |
7601 | return error; |
7602 | } |
7603 | |
7604 | |
7605 | /* ------------------------------------------------------------------------ */ |
7606 | /* Function: ipf_nat_matcharray */ |
7607 | /* Returns: int - -1 == error, 0 == success */ |
7608 | /* Parameters: fin(I) - pointer to packet information */ |
7609 | /* nat(I) - pointer to current NAT session */ |
7610 | /* */ |
7611 | /* ------------------------------------------------------------------------ */ |
7612 | static int |
7613 | ipf_nat_matcharray(nat_t *nat, int *array, u_long ticks) |
7614 | { |
7615 | int i, n, *x, e, p; |
7616 | |
7617 | e = 0; |
7618 | n = array[0]; |
7619 | x = array + 1; |
7620 | |
7621 | for (; n > 0; x += 3 + x[2]) { |
7622 | if (x[0] == IPF_EXP_END) |
7623 | break; |
7624 | e = 0; |
7625 | |
7626 | n -= x[2] + 3; |
7627 | if (n < 0) |
7628 | break; |
7629 | |
7630 | p = x[0] >> 16; |
7631 | if (p != 0 && p != nat->nat_pr[1]) |
7632 | break; |
7633 | |
7634 | switch (x[0]) |
7635 | { |
7636 | case IPF_EXP_IP_PR : |
7637 | for (i = 0; !e && i < x[2]; i++) { |
7638 | e |= (nat->nat_pr[1] == x[i + 3]); |
7639 | } |
7640 | break; |
7641 | |
7642 | case IPF_EXP_IP_SRCADDR : |
7643 | if (nat->nat_v[0] == 4) { |
7644 | for (i = 0; !e && i < x[2]; i++) { |
7645 | e |= ((nat->nat_osrcaddr & x[i + 4]) == |
7646 | x[i + 3]); |
7647 | } |
7648 | } |
7649 | if (nat->nat_v[1] == 4) { |
7650 | for (i = 0; !e && i < x[2]; i++) { |
7651 | e |= ((nat->nat_nsrcaddr & x[i + 4]) == |
7652 | x[i + 3]); |
7653 | } |
7654 | } |
7655 | break; |
7656 | |
7657 | case IPF_EXP_IP_DSTADDR : |
7658 | if (nat->nat_v[0] == 4) { |
7659 | for (i = 0; !e && i < x[2]; i++) { |
7660 | e |= ((nat->nat_odstaddr & x[i + 4]) == |
7661 | x[i + 3]); |
7662 | } |
7663 | } |
7664 | if (nat->nat_v[1] == 4) { |
7665 | for (i = 0; !e && i < x[2]; i++) { |
7666 | e |= ((nat->nat_ndstaddr & x[i + 4]) == |
7667 | x[i + 3]); |
7668 | } |
7669 | } |
7670 | break; |
7671 | |
7672 | case IPF_EXP_IP_ADDR : |
7673 | for (i = 0; !e && i < x[2]; i++) { |
7674 | if (nat->nat_v[0] == 4) { |
7675 | e |= ((nat->nat_osrcaddr & x[i + 4]) == |
7676 | x[i + 3]); |
7677 | } |
7678 | if (nat->nat_v[1] == 4) { |
7679 | e |= ((nat->nat_nsrcaddr & x[i + 4]) == |
7680 | x[i + 3]); |
7681 | } |
7682 | if (nat->nat_v[0] == 4) { |
7683 | e |= ((nat->nat_odstaddr & x[i + 4]) == |
7684 | x[i + 3]); |
7685 | } |
7686 | if (nat->nat_v[1] == 4) { |
7687 | e |= ((nat->nat_ndstaddr & x[i + 4]) == |
7688 | x[i + 3]); |
7689 | } |
7690 | } |
7691 | break; |
7692 | |
7693 | #ifdef USE_INET6 |
7694 | case IPF_EXP_IP6_SRCADDR : |
7695 | if (nat->nat_v[0] == 6) { |
7696 | for (i = 0; !e && i < x[3]; i++) { |
7697 | e |= IP6_MASKEQ(&nat->nat_osrc6, |
7698 | x + i + 7, x + i + 3); |
7699 | } |
7700 | } |
7701 | if (nat->nat_v[1] == 6) { |
7702 | for (i = 0; !e && i < x[3]; i++) { |
7703 | e |= IP6_MASKEQ(&nat->nat_nsrc6, |
7704 | x + i + 7, x + i + 3); |
7705 | } |
7706 | } |
7707 | break; |
7708 | |
7709 | case IPF_EXP_IP6_DSTADDR : |
7710 | if (nat->nat_v[0] == 6) { |
7711 | for (i = 0; !e && i < x[3]; i++) { |
7712 | e |= IP6_MASKEQ(&nat->nat_odst6, |
7713 | x + i + 7, |
7714 | x + i + 3); |
7715 | } |
7716 | } |
7717 | if (nat->nat_v[1] == 6) { |
7718 | for (i = 0; !e && i < x[3]; i++) { |
7719 | e |= IP6_MASKEQ(&nat->nat_ndst6, |
7720 | x + i + 7, |
7721 | x + i + 3); |
7722 | } |
7723 | } |
7724 | break; |
7725 | |
7726 | case IPF_EXP_IP6_ADDR : |
7727 | for (i = 0; !e && i < x[3]; i++) { |
7728 | if (nat->nat_v[0] == 6) { |
7729 | e |= IP6_MASKEQ(&nat->nat_osrc6, |
7730 | x + i + 7, |
7731 | x + i + 3); |
7732 | } |
7733 | if (nat->nat_v[0] == 6) { |
7734 | e |= IP6_MASKEQ(&nat->nat_odst6, |
7735 | x + i + 7, |
7736 | x + i + 3); |
7737 | } |
7738 | if (nat->nat_v[1] == 6) { |
7739 | e |= IP6_MASKEQ(&nat->nat_nsrc6, |
7740 | x + i + 7, |
7741 | x + i + 3); |
7742 | } |
7743 | if (nat->nat_v[1] == 6) { |
7744 | e |= IP6_MASKEQ(&nat->nat_ndst6, |
7745 | x + i + 7, |
7746 | x + i + 3); |
7747 | } |
7748 | } |
7749 | break; |
7750 | #endif |
7751 | |
7752 | case IPF_EXP_UDP_PORT : |
7753 | case IPF_EXP_TCP_PORT : |
7754 | for (i = 0; !e && i < x[2]; i++) { |
7755 | e |= (nat->nat_nsport == x[i + 3]) || |
7756 | (nat->nat_ndport == x[i + 3]); |
7757 | } |
7758 | break; |
7759 | |
7760 | case IPF_EXP_UDP_SPORT : |
7761 | case IPF_EXP_TCP_SPORT : |
7762 | for (i = 0; !e && i < x[2]; i++) { |
7763 | e |= (nat->nat_nsport == x[i + 3]); |
7764 | } |
7765 | break; |
7766 | |
7767 | case IPF_EXP_UDP_DPORT : |
7768 | case IPF_EXP_TCP_DPORT : |
7769 | for (i = 0; !e && i < x[2]; i++) { |
7770 | e |= (nat->nat_ndport == x[i + 3]); |
7771 | } |
7772 | break; |
7773 | |
7774 | case IPF_EXP_TCP_STATE : |
7775 | for (i = 0; !e && i < x[2]; i++) { |
7776 | e |= (nat->nat_tcpstate[0] == x[i + 3]) || |
7777 | (nat->nat_tcpstate[1] == x[i + 3]); |
7778 | } |
7779 | break; |
7780 | |
7781 | case IPF_EXP_IDLE_GT : |
7782 | e |= (ticks - nat->nat_touched > x[3]); |
7783 | break; |
7784 | } |
7785 | e ^= x[1]; |
7786 | |
7787 | if (!e) |
7788 | break; |
7789 | } |
7790 | |
7791 | return e; |
7792 | } |
7793 | |
7794 | |
7795 | /* ------------------------------------------------------------------------ */ |
7796 | /* Function: ipf_nat_gettable */ |
7797 | /* Returns: int - 0 = success, else error */ |
7798 | /* Parameters: softc(I) - pointer to soft context main structure */ |
7799 | /* softn(I) - pointer to NAT context structure */ |
7800 | /* data(I) - pointer to ioctl data */ |
7801 | /* */ |
7802 | /* This function handles ioctl requests for tables of nat information. */ |
7803 | /* At present the only table it deals with is the hash bucket statistics. */ |
7804 | /* ------------------------------------------------------------------------ */ |
7805 | static int |
7806 | ipf_nat_gettable(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, char *data) |
7807 | { |
7808 | ipftable_t table; |
7809 | int error; |
7810 | |
7811 | error = ipf_inobj(softc, data, NULL, &table, IPFOBJ_GTABLE); |
7812 | if (error != 0) |
7813 | return error; |
7814 | |
7815 | switch (table.ita_type) |
7816 | { |
7817 | case IPFTABLE_BUCKETS_NATIN : |
7818 | error = COPYOUT(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, |
7819 | table.ita_table, |
7820 | softn->ipf_nat_table_sz * sizeof(u_int)); |
7821 | break; |
7822 | |
7823 | case IPFTABLE_BUCKETS_NATOUT : |
7824 | error = COPYOUT(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, |
7825 | table.ita_table, |
7826 | softn->ipf_nat_table_sz * sizeof(u_int)); |
7827 | break; |
7828 | |
7829 | default : |
7830 | IPFERROR(60058); |
7831 | return EINVAL; |
7832 | } |
7833 | |
7834 | if (error != 0) { |
7835 | IPFERROR(60059); |
7836 | error = EFAULT; |
7837 | } |
7838 | return error; |
7839 | } |
7840 | |
7841 | |
7842 | /* ------------------------------------------------------------------------ */ |
7843 | /* Function: ipf_nat_settimeout */ |
7844 | /* Returns: int - 0 = success, else failure */ |
7845 | /* Parameters: softc(I) - pointer to soft context main structure */ |
7846 | /* t(I) - pointer to tunable */ |
7847 | /* p(I) - pointer to new tuning data */ |
7848 | /* */ |
7849 | /* Apply the timeout change to the NAT timeout queues. */ |
7850 | /* ------------------------------------------------------------------------ */ |
7851 | int |
7852 | ipf_nat_settimeout(struct ipf_main_softc_s *softc, ipftuneable_t *t, |
7853 | ipftuneval_t *p) |
7854 | { |
7855 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
7856 | |
7857 | if (!strncmp(t->ipft_name, "tcp_" , 4)) |
7858 | return ipf_settimeout_tcp(t, p, softn->ipf_nat_tcptq); |
7859 | |
7860 | if (!strcmp(t->ipft_name, "udp_timeout" )) { |
7861 | ipf_apply_timeout(&softn->ipf_nat_udptq, p->ipftu_int); |
7862 | } else if (!strcmp(t->ipft_name, "udp_ack_timeout" )) { |
7863 | ipf_apply_timeout(&softn->ipf_nat_udpacktq, p->ipftu_int); |
7864 | } else if (!strcmp(t->ipft_name, "icmp_timeout" )) { |
7865 | ipf_apply_timeout(&softn->ipf_nat_icmptq, p->ipftu_int); |
7866 | } else if (!strcmp(t->ipft_name, "icmp_ack_timeout" )) { |
7867 | ipf_apply_timeout(&softn->ipf_nat_icmpacktq, p->ipftu_int); |
7868 | } else if (!strcmp(t->ipft_name, "ip_timeout" )) { |
7869 | ipf_apply_timeout(&softn->ipf_nat_iptq, p->ipftu_int); |
7870 | } else { |
7871 | IPFERROR(60062); |
7872 | return ESRCH; |
7873 | } |
7874 | return 0; |
7875 | } |
7876 | |
7877 | |
7878 | /* ------------------------------------------------------------------------ */ |
7879 | /* Function: ipf_nat_rehash */ |
7880 | /* Returns: int - 0 = success, else failure */ |
7881 | /* Parameters: softc(I) - pointer to soft context main structure */ |
7882 | /* t(I) - pointer to tunable */ |
7883 | /* p(I) - pointer to new tuning data */ |
7884 | /* */ |
7885 | /* To change the size of the basic NAT table, we need to first allocate the */ |
7886 | /* new tables (lest it fails and we've got nowhere to store all of the NAT */ |
7887 | /* sessions currently active) and then walk through the entire list and */ |
7888 | /* insert them into the table. There are two tables here: an inbound one */ |
7889 | /* and an outbound one. Each NAT entry goes into each table once. */ |
7890 | /* ------------------------------------------------------------------------ */ |
7891 | int |
7892 | ipf_nat_rehash(ipf_main_softc_t *softc, ipftuneable_t *t, ipftuneval_t *p) |
7893 | { |
7894 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
7895 | nat_t **newtab[2], *nat, **natp; |
7896 | u_int *bucketlens[2]; |
7897 | u_int maxbucket; |
7898 | u_int newsize; |
7899 | int error; |
7900 | u_int hv; |
7901 | int i; |
7902 | |
7903 | newsize = p->ipftu_int; |
7904 | /* |
7905 | * In case there is nothing to do... |
7906 | */ |
7907 | if (newsize == softn->ipf_nat_table_sz) |
7908 | return 0; |
7909 | |
7910 | newtab[0] = NULL; |
7911 | newtab[1] = NULL; |
7912 | bucketlens[0] = NULL; |
7913 | bucketlens[1] = NULL; |
7914 | /* |
7915 | * 4 tables depend on the NAT table size: the inbound looking table, |
7916 | * the outbound lookup table and the hash chain length for each. |
7917 | */ |
7918 | KMALLOCS(newtab[0], nat_t **, newsize * sizeof(nat_t *)); |
7919 | if (newtab[0] == NULL) { |
7920 | error = 60063; |
7921 | goto badrehash; |
7922 | } |
7923 | |
7924 | KMALLOCS(newtab[1], nat_t **, newsize * sizeof(nat_t *)); |
7925 | if (newtab[1] == NULL) { |
7926 | error = 60064; |
7927 | goto badrehash; |
7928 | } |
7929 | |
7930 | KMALLOCS(bucketlens[0], u_int *, newsize * sizeof(u_int)); |
7931 | if (bucketlens[0] == NULL) { |
7932 | error = 60065; |
7933 | goto badrehash; |
7934 | } |
7935 | |
7936 | KMALLOCS(bucketlens[1], u_int *, newsize * sizeof(u_int)); |
7937 | if (bucketlens[1] == NULL) { |
7938 | error = 60066; |
7939 | goto badrehash; |
7940 | } |
7941 | |
7942 | /* |
7943 | * Recalculate the maximum length based on the new size. |
7944 | */ |
7945 | for (maxbucket = 0, i = newsize; i > 0; i >>= 1) |
7946 | maxbucket++; |
7947 | maxbucket *= 2; |
7948 | |
7949 | bzero((char *)newtab[0], newsize * sizeof(nat_t *)); |
7950 | bzero((char *)newtab[1], newsize * sizeof(nat_t *)); |
7951 | bzero((char *)bucketlens[0], newsize * sizeof(u_int)); |
7952 | bzero((char *)bucketlens[1], newsize * sizeof(u_int)); |
7953 | |
7954 | WRITE_ENTER(&softc->ipf_nat); |
7955 | |
7956 | if (softn->ipf_nat_table[0] != NULL) { |
7957 | KFREES(softn->ipf_nat_table[0], |
7958 | softn->ipf_nat_table_sz * |
7959 | sizeof(*softn->ipf_nat_table[0])); |
7960 | } |
7961 | softn->ipf_nat_table[0] = newtab[0]; |
7962 | |
7963 | if (softn->ipf_nat_table[1] != NULL) { |
7964 | KFREES(softn->ipf_nat_table[1], |
7965 | softn->ipf_nat_table_sz * |
7966 | sizeof(*softn->ipf_nat_table[1])); |
7967 | } |
7968 | softn->ipf_nat_table[1] = newtab[1]; |
7969 | |
7970 | if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) { |
7971 | KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, |
7972 | softn->ipf_nat_table_sz * sizeof(u_int)); |
7973 | } |
7974 | softn->ipf_nat_stats.ns_side[0].ns_bucketlen = bucketlens[0]; |
7975 | |
7976 | if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) { |
7977 | KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, |
7978 | softn->ipf_nat_table_sz * sizeof(u_int)); |
7979 | } |
7980 | softn->ipf_nat_stats.ns_side[1].ns_bucketlen = bucketlens[1]; |
7981 | |
7982 | softn->ipf_nat_maxbucket = maxbucket; |
7983 | softn->ipf_nat_table_sz = newsize; |
7984 | /* |
7985 | * Walk through the entire list of NAT table entries and put them |
7986 | * in the new NAT table, somewhere. Because we have a new table, |
7987 | * we need to restart the counter of how many chains are in use. |
7988 | */ |
7989 | softn->ipf_nat_stats.ns_side[0].ns_inuse = 0; |
7990 | softn->ipf_nat_stats.ns_side[1].ns_inuse = 0; |
7991 | |
7992 | for (nat = softn->ipf_nat_instances; nat != NULL; nat = nat->nat_next) { |
7993 | nat->nat_hnext[0] = NULL; |
7994 | nat->nat_phnext[0] = NULL; |
7995 | hv = nat->nat_hv[0] % softn->ipf_nat_table_sz; |
7996 | |
7997 | natp = &softn->ipf_nat_table[0][hv]; |
7998 | if (*natp) { |
7999 | (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; |
8000 | } else { |
8001 | NBUMPSIDE(0, ns_inuse); |
8002 | } |
8003 | nat->nat_phnext[0] = natp; |
8004 | nat->nat_hnext[0] = *natp; |
8005 | *natp = nat; |
8006 | NBUMPSIDE(0, ns_bucketlen[hv]); |
8007 | |
8008 | nat->nat_hnext[1] = NULL; |
8009 | nat->nat_phnext[1] = NULL; |
8010 | hv = nat->nat_hv[1] % softn->ipf_nat_table_sz; |
8011 | |
8012 | natp = &softn->ipf_nat_table[1][hv]; |
8013 | if (*natp) { |
8014 | (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; |
8015 | } else { |
8016 | NBUMPSIDE(1, ns_inuse); |
8017 | } |
8018 | nat->nat_phnext[1] = natp; |
8019 | nat->nat_hnext[1] = *natp; |
8020 | *natp = nat; |
8021 | NBUMPSIDE(1, ns_bucketlen[hv]); |
8022 | } |
8023 | RWLOCK_EXIT(&softc->ipf_nat); |
8024 | |
8025 | return 0; |
8026 | |
8027 | badrehash: |
8028 | if (bucketlens[1] != NULL) { |
8029 | KFREES(bucketlens[0], newsize * sizeof(u_int)); |
8030 | } |
8031 | if (bucketlens[0] != NULL) { |
8032 | KFREES(bucketlens[0], newsize * sizeof(u_int)); |
8033 | } |
8034 | if (newtab[0] != NULL) { |
8035 | KFREES(newtab[0], newsize * sizeof(nat_t *)); |
8036 | } |
8037 | if (newtab[1] != NULL) { |
8038 | KFREES(newtab[1], newsize * sizeof(nat_t *)); |
8039 | } |
8040 | IPFERROR(error); |
8041 | return ENOMEM; |
8042 | } |
8043 | |
8044 | |
8045 | /* ------------------------------------------------------------------------ */ |
8046 | /* Function: ipf_nat_rehash_rules */ |
8047 | /* Returns: int - 0 = success, else failure */ |
8048 | /* Parameters: softc(I) - pointer to soft context main structure */ |
8049 | /* t(I) - pointer to tunable */ |
8050 | /* p(I) - pointer to new tuning data */ |
8051 | /* */ |
8052 | /* All of the NAT rules hang off of a hash table that is searched with a */ |
8053 | /* hash on address after the netmask is applied. There is a different table*/ |
8054 | /* for both inbound rules (rdr) and outbound (map.) The resizing will only */ |
8055 | /* affect one of these two tables. */ |
8056 | /* ------------------------------------------------------------------------ */ |
8057 | int |
8058 | ipf_nat_rehash_rules(ipf_main_softc_t *softc, ipftuneable_t *t, ipftuneval_t *p) |
8059 | { |
8060 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
8061 | ipnat_t **newtab, *np, ***old, **npp; |
8062 | u_int newsize; |
8063 | u_int mask; |
8064 | u_int hv; |
8065 | |
8066 | newsize = p->ipftu_int; |
8067 | /* |
8068 | * In case there is nothing to do... |
8069 | */ |
8070 | if (newsize == *t->ipft_pint) |
8071 | return 0; |
8072 | |
8073 | /* |
8074 | * All inbound rules have the NAT_REDIRECT bit set in in_redir and |
8075 | * all outbound rules have either NAT_MAP or MAT_MAPBLK set. |
8076 | * This if statement allows for some more generic code to be below, |
8077 | * rather than two huge gobs of code that almost do the same thing. |
8078 | */ |
8079 | if (t->ipft_pint == &softn->ipf_nat_rdrrules_sz) { |
8080 | old = &softn->ipf_nat_rdr_rules; |
8081 | mask = NAT_REDIRECT; |
8082 | } else { |
8083 | old = &softn->ipf_nat_map_rules; |
8084 | mask = NAT_MAP|NAT_MAPBLK; |
8085 | } |
8086 | |
8087 | KMALLOCS(newtab, ipnat_t **, newsize * sizeof(ipnat_t *)); |
8088 | if (newtab == NULL) { |
8089 | IPFERROR(60067); |
8090 | return ENOMEM; |
8091 | } |
8092 | |
8093 | bzero((char *)newtab, newsize * sizeof(ipnat_t *)); |
8094 | |
8095 | WRITE_ENTER(&softc->ipf_nat); |
8096 | |
8097 | if (*old != NULL) { |
8098 | KFREES(*old, *t->ipft_pint * sizeof(ipnat_t **)); |
8099 | } |
8100 | *old = newtab; |
8101 | *t->ipft_pint = newsize; |
8102 | |
8103 | for (np = softn->ipf_nat_list; np != NULL; np = np->in_next) { |
8104 | if ((np->in_redir & mask) == 0) |
8105 | continue; |
8106 | |
8107 | if (np->in_redir & NAT_REDIRECT) { |
8108 | np->in_rnext = NULL; |
8109 | hv = np->in_hv[0] % newsize; |
8110 | for (npp = newtab + hv; *npp != NULL; ) |
8111 | npp = &(*npp)->in_rnext; |
8112 | np->in_prnext = npp; |
8113 | *npp = np; |
8114 | } |
8115 | if (np->in_redir & NAT_MAP) { |
8116 | np->in_mnext = NULL; |
8117 | hv = np->in_hv[1] % newsize; |
8118 | for (npp = newtab + hv; *npp != NULL; ) |
8119 | npp = &(*npp)->in_mnext; |
8120 | np->in_pmnext = npp; |
8121 | *npp = np; |
8122 | } |
8123 | |
8124 | } |
8125 | RWLOCK_EXIT(&softc->ipf_nat); |
8126 | |
8127 | return 0; |
8128 | } |
8129 | |
8130 | |
8131 | /* ------------------------------------------------------------------------ */ |
8132 | /* Function: ipf_nat_hostmap_rehash */ |
8133 | /* Returns: int - 0 = success, else failure */ |
8134 | /* Parameters: softc(I) - pointer to soft context main structure */ |
8135 | /* t(I) - pointer to tunable */ |
8136 | /* p(I) - pointer to new tuning data */ |
8137 | /* */ |
8138 | /* Allocate and populate a new hash table that will contain a reference to */ |
8139 | /* all of the active IP# translations currently in place. */ |
8140 | /* ------------------------------------------------------------------------ */ |
8141 | int |
8142 | ipf_nat_hostmap_rehash(ipf_main_softc_t *softc, ipftuneable_t *t, |
8143 | ipftuneval_t *p) |
8144 | { |
8145 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
8146 | hostmap_t *hm, **newtab; |
8147 | u_int newsize; |
8148 | u_int hv; |
8149 | |
8150 | newsize = p->ipftu_int; |
8151 | /* |
8152 | * In case there is nothing to do... |
8153 | */ |
8154 | if (newsize == *t->ipft_pint) |
8155 | return 0; |
8156 | |
8157 | KMALLOCS(newtab, hostmap_t **, newsize * sizeof(hostmap_t *)); |
8158 | if (newtab == NULL) { |
8159 | IPFERROR(60068); |
8160 | return ENOMEM; |
8161 | } |
8162 | |
8163 | bzero((char *)newtab, newsize * sizeof(hostmap_t *)); |
8164 | |
8165 | WRITE_ENTER(&softc->ipf_nat); |
8166 | if (softn->ipf_hm_maptable != NULL) { |
8167 | KFREES(softn->ipf_hm_maptable, |
8168 | softn->ipf_nat_hostmap_sz * sizeof(hostmap_t *)); |
8169 | } |
8170 | softn->ipf_hm_maptable = newtab; |
8171 | softn->ipf_nat_hostmap_sz = newsize; |
8172 | |
8173 | for (hm = softn->ipf_hm_maplist; hm != NULL; hm = hm->hm_next) { |
8174 | hv = hm->hm_hv % softn->ipf_nat_hostmap_sz; |
8175 | hm->hm_hnext = softn->ipf_hm_maptable[hv]; |
8176 | hm->hm_phnext = softn->ipf_hm_maptable + hv; |
8177 | if (softn->ipf_hm_maptable[hv] != NULL) |
8178 | softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; |
8179 | softn->ipf_hm_maptable[hv] = hm; |
8180 | } |
8181 | RWLOCK_EXIT(&softc->ipf_nat); |
8182 | |
8183 | return 0; |
8184 | } |
8185 | |
8186 | |
8187 | /* ------------------------------------------------------------------------ */ |
8188 | /* Function: ipf_nat_add_tq */ |
8189 | /* Parameters: softc(I) - pointer to soft context main structure */ |
8190 | /* */ |
8191 | /* ------------------------------------------------------------------------ */ |
8192 | ipftq_t * |
8193 | ipf_nat_add_tq(ipf_main_softc_t *softc, int ttl) |
8194 | { |
8195 | ipf_nat_softc_t *softs = softc->ipf_nat_soft; |
8196 | |
8197 | return ipf_addtimeoutqueue(softc, &softs->ipf_nat_utqe, ttl); |
8198 | } |
8199 | |
8200 | /* ------------------------------------------------------------------------ */ |
8201 | /* Function: ipf_nat_uncreate */ |
8202 | /* Returns: Nil */ |
8203 | /* Parameters: fin(I) - pointer to packet information */ |
8204 | /* */ |
8205 | /* This function is used to remove a NAT entry from the NAT table when we */ |
8206 | /* decide that the create was actually in error. It is thus assumed that */ |
8207 | /* fin_flx will have both FI_NATED and FI_NATNEW set. Because we're dealing */ |
8208 | /* with the translated packet (not the original), we have to reverse the */ |
8209 | /* lookup. Although doing the lookup is expensive (relatively speaking), it */ |
8210 | /* is not anticipated that this will be a frequent occurance for normal */ |
8211 | /* traffic patterns. */ |
8212 | /* ------------------------------------------------------------------------ */ |
8213 | void |
8214 | ipf_nat_uncreate(fr_info_t *fin) |
8215 | { |
8216 | ipf_main_softc_t *softc = fin->fin_main_soft; |
8217 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
8218 | int nflags; |
8219 | nat_t *nat; |
8220 | |
8221 | switch (fin->fin_p) |
8222 | { |
8223 | case IPPROTO_TCP : |
8224 | nflags = IPN_TCP; |
8225 | break; |
8226 | case IPPROTO_UDP : |
8227 | nflags = IPN_UDP; |
8228 | break; |
8229 | default : |
8230 | nflags = 0; |
8231 | break; |
8232 | } |
8233 | |
8234 | WRITE_ENTER(&softc->ipf_nat); |
8235 | |
8236 | if (fin->fin_out == 0) { |
8237 | nat = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p, |
8238 | fin->fin_dst, fin->fin_src); |
8239 | } else { |
8240 | nat = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p, |
8241 | fin->fin_src, fin->fin_dst); |
8242 | } |
8243 | |
8244 | if (nat != NULL) { |
8245 | NBUMPSIDE(fin->fin_out, ns_uncreate[0]); |
8246 | ipf_nat_delete(softc, nat, NL_DESTROY); |
8247 | } else { |
8248 | NBUMPSIDE(fin->fin_out, ns_uncreate[1]); |
8249 | } |
8250 | |
8251 | RWLOCK_EXIT(&softc->ipf_nat); |
8252 | } |
8253 | |
8254 | |
8255 | /* ------------------------------------------------------------------------ */ |
8256 | /* Function: ipf_nat_cmp_rules */ |
8257 | /* Returns: int - 0 == success, else rules do not match. */ |
8258 | /* Parameters: n1(I) - first rule to compare */ |
8259 | /* n2(I) - first rule to compare */ |
8260 | /* */ |
8261 | /* Compare two rules using pointers to each rule. A straight bcmp will not */ |
8262 | /* work as some fields (such as in_dst, in_pkts) actually do change once */ |
8263 | /* the rule has been loaded into the kernel. Whilst this function returns */ |
8264 | /* various non-zero returns, they're strictly to aid in debugging. Use of */ |
8265 | /* this function should simply care if the result is zero or not. */ |
8266 | /* ------------------------------------------------------------------------ */ |
8267 | static int |
8268 | ipf_nat_cmp_rules(ipnat_t *n1, ipnat_t *n2) |
8269 | { |
8270 | if (n1->in_size != n2->in_size) |
8271 | return 1; |
8272 | |
8273 | if (bcmp((char *)&n1->in_v, (char *)&n2->in_v, |
8274 | offsetof(ipnat_t, in_ndst) - offsetof(ipnat_t, in_v)) != 0) |
8275 | return 2; |
8276 | |
8277 | if (bcmp((char *)&n1->in_tuc, (char *)&n2->in_tuc, |
8278 | n1->in_size - offsetof(ipnat_t, in_tuc)) != 0) |
8279 | return 3; |
8280 | if (n1->in_ndst.na_atype != n2->in_ndst.na_atype) |
8281 | return 5; |
8282 | if (n1->in_ndst.na_function != n2->in_ndst.na_function) |
8283 | return 6; |
8284 | if (bcmp((char *)&n1->in_ndst.na_addr, (char *)&n2->in_ndst.na_addr, |
8285 | sizeof(n1->in_ndst.na_addr))) |
8286 | return 7; |
8287 | if (n1->in_nsrc.na_atype != n2->in_nsrc.na_atype) |
8288 | return 8; |
8289 | if (n1->in_nsrc.na_function != n2->in_nsrc.na_function) |
8290 | return 9; |
8291 | if (bcmp((char *)&n1->in_nsrc.na_addr, (char *)&n2->in_nsrc.na_addr, |
8292 | sizeof(n1->in_nsrc.na_addr))) |
8293 | return 10; |
8294 | if (n1->in_odst.na_atype != n2->in_odst.na_atype) |
8295 | return 11; |
8296 | if (n1->in_odst.na_function != n2->in_odst.na_function) |
8297 | return 12; |
8298 | if (bcmp((char *)&n1->in_odst.na_addr, (char *)&n2->in_odst.na_addr, |
8299 | sizeof(n1->in_odst.na_addr))) |
8300 | return 13; |
8301 | if (n1->in_osrc.na_atype != n2->in_osrc.na_atype) |
8302 | return 14; |
8303 | if (n1->in_osrc.na_function != n2->in_osrc.na_function) |
8304 | return 15; |
8305 | if (bcmp((char *)&n1->in_osrc.na_addr, (char *)&n2->in_osrc.na_addr, |
8306 | sizeof(n1->in_osrc.na_addr))) |
8307 | return 16; |
8308 | return 0; |
8309 | } |
8310 | |
8311 | |
8312 | /* ------------------------------------------------------------------------ */ |
8313 | /* Function: ipf_nat_rule_init */ |
8314 | /* Returns: int - 0 == success, else rules do not match. */ |
8315 | /* Parameters: softc(I) - pointer to soft context main structure */ |
8316 | /* softn(I) - pointer to NAT context structure */ |
8317 | /* n(I) - first rule to compare */ |
8318 | /* */ |
8319 | /* ------------------------------------------------------------------------ */ |
8320 | static int |
8321 | ipf_nat_rule_init(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, ipnat_t *n) |
8322 | { |
8323 | int error = 0; |
8324 | |
8325 | if ((n->in_flags & IPN_SIPRANGE) != 0) |
8326 | n->in_nsrcatype = FRI_RANGE; |
8327 | |
8328 | if ((n->in_flags & IPN_DIPRANGE) != 0) |
8329 | n->in_ndstatype = FRI_RANGE; |
8330 | |
8331 | if ((n->in_flags & IPN_SPLIT) != 0) |
8332 | n->in_ndstatype = FRI_SPLIT; |
8333 | |
8334 | if ((n->in_redir & (NAT_MAP|NAT_REWRITE|NAT_DIVERTUDP)) != 0) |
8335 | n->in_spnext = n->in_spmin; |
8336 | |
8337 | if ((n->in_redir & (NAT_REWRITE|NAT_DIVERTUDP)) != 0) { |
8338 | n->in_dpnext = n->in_dpmin; |
8339 | } else if (n->in_redir == NAT_REDIRECT) { |
8340 | n->in_dpnext = n->in_dpmin; |
8341 | } |
8342 | |
8343 | n->in_stepnext = 0; |
8344 | |
8345 | switch (n->in_v[0]) |
8346 | { |
8347 | case 4 : |
8348 | error = ipf_nat_ruleaddrinit(softc, softn, n); |
8349 | if (error != 0) |
8350 | return error; |
8351 | break; |
8352 | #ifdef USE_INET6 |
8353 | case 6 : |
8354 | error = ipf_nat6_ruleaddrinit(softc, softn, n); |
8355 | if (error != 0) |
8356 | return error; |
8357 | break; |
8358 | #endif |
8359 | default : |
8360 | break; |
8361 | } |
8362 | |
8363 | if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) { |
8364 | /* |
8365 | * Prerecord whether or not the destination of the divert |
8366 | * is local or not to the interface the packet is going |
8367 | * to be sent out. |
8368 | */ |
8369 | n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1], |
8370 | n->in_ifps[1], &n->in_ndstip6); |
8371 | } |
8372 | |
8373 | return error; |
8374 | } |
8375 | |
8376 | |
8377 | /* ------------------------------------------------------------------------ */ |
8378 | /* Function: ipf_nat_rule_fini */ |
8379 | /* Returns: int - 0 == success, else rules do not match. */ |
8380 | /* Parameters: softc(I) - pointer to soft context main structure */ |
8381 | /* n(I) - rule to work on */ |
8382 | /* */ |
8383 | /* This function is used to release any objects that were referenced during */ |
8384 | /* the rule initialisation. This is useful both when free'ing the rule and */ |
8385 | /* when handling ioctls that need to initialise these fields but not */ |
8386 | /* actually use them after the ioctl processing has finished. */ |
8387 | /* ------------------------------------------------------------------------ */ |
8388 | static void |
8389 | ipf_nat_rule_fini(ipf_main_softc_t *softc, ipnat_t *n) |
8390 | { |
8391 | if (n->in_odst.na_atype == FRI_LOOKUP && n->in_odst.na_ptr != NULL) |
8392 | ipf_lookup_deref(softc, n->in_odst.na_type, n->in_odst.na_ptr); |
8393 | |
8394 | if (n->in_osrc.na_atype == FRI_LOOKUP && n->in_osrc.na_ptr != NULL) |
8395 | ipf_lookup_deref(softc, n->in_osrc.na_type, n->in_osrc.na_ptr); |
8396 | |
8397 | if (n->in_ndst.na_atype == FRI_LOOKUP && n->in_ndst.na_ptr != NULL) |
8398 | ipf_lookup_deref(softc, n->in_ndst.na_type, n->in_ndst.na_ptr); |
8399 | |
8400 | if (n->in_nsrc.na_atype == FRI_LOOKUP && n->in_nsrc.na_ptr != NULL) |
8401 | ipf_lookup_deref(softc, n->in_nsrc.na_type, n->in_nsrc.na_ptr); |
8402 | |
8403 | if (n->in_divmp != NULL) |
8404 | FREE_MB_T(n->in_divmp); |
8405 | } |
8406 | |