1 | /* $NetBSD: ip_nat6.c,v 1.10 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 ipf_nat6_KERNEL |
11 | # define KERNEL 1 |
12 | # define ipf_nat6_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) && defined(__NetBSD_Version__) && \ |
20 | (__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 ipf_nat6_KERNEL |
28 | # ifdef ipf_nat6__OpenBSD__ |
29 | struct file; |
30 | # endif |
31 | # include <sys/uio.h> |
32 | # undef ipf_nat6_KERNEL |
33 | #endif |
34 | #if defined(_KERNEL) && (__FreeBSD_version >= 220000) |
35 | # include <sys/filio.h> |
36 | # include <sys/fcntl.h> |
37 | #else |
38 | # include <sys/ioctl.h> |
39 | #endif |
40 | #if !defined(AIX) |
41 | # include <sys/fcntl.h> |
42 | #endif |
43 | #if !defined(linux) |
44 | # include <sys/protosw.h> |
45 | #endif |
46 | #include <sys/socket.h> |
47 | #if defined(_KERNEL) |
48 | # include <sys/systm.h> |
49 | # if !defined(__SVR4) && !defined(__svr4__) |
50 | # include <sys/mbuf.h> |
51 | # endif |
52 | #endif |
53 | #if defined(__SVR4) || defined(__svr4__) |
54 | # include <sys/filio.h> |
55 | # include <sys/byteorder.h> |
56 | # ifdef ipf_nat6_KERNEL |
57 | # include <sys/dditypes.h> |
58 | # endif |
59 | # include <sys/stream.h> |
60 | # include <sys/kmem.h> |
61 | #endif |
62 | #if ipf_nat6__FreeBSD_version >= 300000 |
63 | # include <sys/queue.h> |
64 | #endif |
65 | #include <net/if.h> |
66 | #if ipf_nat6__FreeBSD_version >= 300000 |
67 | # include <net/if_var.h> |
68 | #endif |
69 | #ifdef sun |
70 | # include <net/af.h> |
71 | #endif |
72 | #include <net/route.h> |
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/ip_fil.h" |
92 | #include "netinet/ip_nat.h" |
93 | #include "netinet/ip_frag.h" |
94 | #include "netinet/ip_state.h" |
95 | #include "netinet/ip_proxy.h" |
96 | #include "netinet/ip_lookup.h" |
97 | #include "netinet/ip_dstlist.h" |
98 | #include "netinet/ip_sync.h" |
99 | #if (__FreeBSD_version >= 300000) |
100 | # include <sys/malloc.h> |
101 | #endif |
102 | #ifdef HAS_SYS_MD5_H |
103 | # include <sys/md5.h> |
104 | #else |
105 | # include "md5.h" |
106 | #endif |
107 | /* END OF INCLUDES */ |
108 | |
109 | #undef SOCKADDR_IN |
110 | #define SOCKADDR_IN struct sockaddr_in |
111 | |
112 | __KERNEL_RCSID(0, "Id: ip_nat6.c,v 1.1.1.2 2012/07/22 13:45:29 darrenr Exp" ); |
113 | |
114 | #ifdef USE_INET6 |
115 | static struct hostmap *ipf_nat6_hostmap(ipf_nat_softc_t *, ipnat_t *, |
116 | i6addr_t *, i6addr_t *, |
117 | i6addr_t *, u_32_t); |
118 | static int ipf_nat6_match(fr_info_t *, ipnat_t *); |
119 | static void ipf_nat6_tabmove(ipf_nat_softc_t *, nat_t *); |
120 | static int ipf_nat6_decap(fr_info_t *, nat_t *); |
121 | static int ipf_nat6_nextaddr(fr_info_t *, nat_addr_t *, i6addr_t *, |
122 | i6addr_t *); |
123 | static int ipf_nat6_icmpquerytype(int); |
124 | static int ipf_nat6_out(fr_info_t *, nat_t *, int, u_32_t); |
125 | static int ipf_nat6_in(fr_info_t *, nat_t *, int, u_32_t); |
126 | static int ipf_nat6_builddivertmp(ipf_nat_softc_t *, ipnat_t *); |
127 | static int ipf_nat6_nextaddrinit(ipf_main_softc_t *, char *, |
128 | nat_addr_t *, int, void *); |
129 | static int ipf_nat6_insert(ipf_main_softc_t *, ipf_nat_softc_t *, |
130 | nat_t *); |
131 | |
132 | |
133 | #define NINCLSIDE6(y,x) ATOMIC_INCL(softn->ipf_nat_stats.ns_side6[y].x) |
134 | #define NBUMPSIDE(y,x) softn->ipf_nat_stats.ns_side[y].x++ |
135 | #define NBUMPSIDE6(y,x) softn->ipf_nat_stats.ns_side6[y].x++ |
136 | #define NBUMPSIDE6D(y,x) \ |
137 | do { \ |
138 | softn->ipf_nat_stats.ns_side6[y].x++; \ |
139 | DT(x); \ |
140 | } while (0) |
141 | #define NBUMPSIDE6DX(y,x,z) \ |
142 | do { \ |
143 | softn->ipf_nat_stats.ns_side6[y].x++; \ |
144 | DT(z); \ |
145 | } while (0) |
146 | |
147 | |
148 | /* ------------------------------------------------------------------------ */ |
149 | /* Function: ipf_nat6_ruleaddrinit */ |
150 | /* Returns: int - 0 == success, else failure */ |
151 | /* Parameters: in(I) - NAT rule that requires address fields to be init'd */ |
152 | /* */ |
153 | /* For each of the source/destination address fields in a NAT rule, call */ |
154 | /* ipf_nat6_nextaddrinit() to prepare the structure for active duty. Other */ |
155 | /* IPv6 specific actions can also be taken care of here. */ |
156 | /* ------------------------------------------------------------------------ */ |
157 | int |
158 | ipf_nat6_ruleaddrinit(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, |
159 | ipnat_t *n) |
160 | { |
161 | int idx, error; |
162 | |
163 | if (n->in_redir == NAT_BIMAP) { |
164 | n->in_ndstip6 = n->in_osrcip6; |
165 | n->in_ndstmsk6 = n->in_osrcmsk6; |
166 | n->in_odstip6 = n->in_nsrcip6; |
167 | n->in_odstmsk6 = n->in_nsrcmsk6; |
168 | |
169 | } |
170 | |
171 | if (n->in_redir & NAT_REDIRECT) |
172 | idx = 1; |
173 | else |
174 | idx = 0; |
175 | /* |
176 | * Initialise all of the address fields. |
177 | */ |
178 | error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_osrc, 1, |
179 | n->in_ifps[idx]); |
180 | if (error != 0) |
181 | return error; |
182 | |
183 | error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_odst, 1, |
184 | n->in_ifps[idx]); |
185 | if (error != 0) |
186 | return error; |
187 | |
188 | error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_nsrc, 1, |
189 | n->in_ifps[idx]); |
190 | if (error != 0) |
191 | return error; |
192 | |
193 | error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_ndst, 1, |
194 | n->in_ifps[idx]); |
195 | if (error != 0) |
196 | return error; |
197 | |
198 | if (n->in_redir & NAT_DIVERTUDP) |
199 | ipf_nat6_builddivertmp(softn, n); |
200 | return 0; |
201 | } |
202 | |
203 | |
204 | /* ------------------------------------------------------------------------ */ |
205 | /* Function: ipf_nat6_addrdr */ |
206 | /* Returns: Nil */ |
207 | /* Parameters: n(I) - pointer to NAT rule to add */ |
208 | /* */ |
209 | /* Adds a redirect rule to the hash table of redirect rules and the list of */ |
210 | /* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ |
211 | /* use by redirect rules. */ |
212 | /* ------------------------------------------------------------------------ */ |
213 | void |
214 | ipf_nat6_addrdr(ipf_nat_softc_t *softn, ipnat_t *n) |
215 | { |
216 | i6addr_t *mask; |
217 | ipnat_t **np; |
218 | i6addr_t j; |
219 | u_int hv; |
220 | int k; |
221 | |
222 | if ((n->in_redir & NAT_BIMAP) == NAT_BIMAP) { |
223 | k = count6bits(n->in_nsrcmsk6.i6); |
224 | mask = &n->in_nsrcmsk6; |
225 | IP6_AND(&n->in_odstip6, &n->in_odstmsk6, &j); |
226 | hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_rdrrules_sz); |
227 | |
228 | } else if (n->in_odstatype == FRI_NORMAL) { |
229 | k = count6bits(n->in_odstmsk6.i6); |
230 | mask = &n->in_odstmsk6; |
231 | IP6_AND(&n->in_odstip6, &n->in_odstmsk6, &j); |
232 | hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_rdrrules_sz); |
233 | } else { |
234 | k = 0; |
235 | hv = 0; |
236 | mask = NULL; |
237 | } |
238 | ipf_inet6_mask_add(k, mask, &softn->ipf_nat6_rdr_mask); |
239 | |
240 | np = softn->ipf_nat_rdr_rules + hv; |
241 | while (*np != NULL) |
242 | np = &(*np)->in_rnext; |
243 | n->in_rnext = NULL; |
244 | n->in_prnext = np; |
245 | n->in_hv[0] = hv; |
246 | n->in_use++; |
247 | *np = n; |
248 | } |
249 | |
250 | |
251 | /* ------------------------------------------------------------------------ */ |
252 | /* Function: ipf_nat6_addmap */ |
253 | /* Returns: Nil */ |
254 | /* Parameters: n(I) - pointer to NAT rule to add */ |
255 | /* */ |
256 | /* Adds a NAT map rule to the hash table of rules and the list of loaded */ |
257 | /* NAT rules. Updates the bitmask indicating which netmasks are in use by */ |
258 | /* redirect rules. */ |
259 | /* ------------------------------------------------------------------------ */ |
260 | void |
261 | ipf_nat6_addmap(ipf_nat_softc_t *softn, ipnat_t *n) |
262 | { |
263 | i6addr_t *mask; |
264 | ipnat_t **np; |
265 | i6addr_t j; |
266 | u_int hv; |
267 | int k; |
268 | |
269 | if (n->in_osrcatype == FRI_NORMAL) { |
270 | k = count6bits(n->in_osrcmsk6.i6); |
271 | mask = &n->in_osrcmsk6; |
272 | IP6_AND(&n->in_osrcip6, &n->in_osrcmsk6, &j); |
273 | hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_maprules_sz); |
274 | } else { |
275 | k = 0; |
276 | hv = 0; |
277 | mask = NULL; |
278 | } |
279 | ipf_inet6_mask_add(k, mask, &softn->ipf_nat6_map_mask); |
280 | |
281 | np = softn->ipf_nat_map_rules + hv; |
282 | while (*np != NULL) |
283 | np = &(*np)->in_mnext; |
284 | n->in_mnext = NULL; |
285 | n->in_pmnext = np; |
286 | n->in_hv[1] = hv; |
287 | n->in_use++; |
288 | *np = n; |
289 | } |
290 | |
291 | |
292 | /* ------------------------------------------------------------------------ */ |
293 | /* Function: ipf_nat6_del_rdr */ |
294 | /* Returns: Nil */ |
295 | /* Parameters: n(I) - pointer to NAT rule to delete */ |
296 | /* */ |
297 | /* Removes a NAT rdr rule from the hash table of NAT rdr rules. */ |
298 | /* ------------------------------------------------------------------------ */ |
299 | void |
300 | ipf_nat6_delrdr(ipf_nat_softc_t *softn, ipnat_t *n) |
301 | { |
302 | i6addr_t *mask; |
303 | int k; |
304 | |
305 | if ((n->in_redir & NAT_BIMAP) == NAT_BIMAP) { |
306 | k = count6bits(n->in_nsrcmsk6.i6); |
307 | mask = &n->in_nsrcmsk6; |
308 | } else if (n->in_odstatype == FRI_NORMAL) { |
309 | k = count6bits(n->in_odstmsk6.i6); |
310 | mask = &n->in_odstmsk6; |
311 | } else { |
312 | k = 0; |
313 | mask = NULL; |
314 | } |
315 | ipf_inet6_mask_del(k, mask, &softn->ipf_nat6_rdr_mask); |
316 | |
317 | if (n->in_rnext != NULL) |
318 | n->in_rnext->in_prnext = n->in_prnext; |
319 | *n->in_prnext = n->in_rnext; |
320 | n->in_use--; |
321 | } |
322 | |
323 | |
324 | /* ------------------------------------------------------------------------ */ |
325 | /* Function: ipf_nat6_delmap */ |
326 | /* Returns: Nil */ |
327 | /* Parameters: n(I) - pointer to NAT rule to delete */ |
328 | /* */ |
329 | /* Removes a NAT map rule from the hash table of NAT map rules. */ |
330 | /* ------------------------------------------------------------------------ */ |
331 | void |
332 | ipf_nat6_delmap(ipf_nat_softc_t *softn, ipnat_t *n) |
333 | { |
334 | i6addr_t *mask; |
335 | int k; |
336 | |
337 | if (n->in_osrcatype == FRI_NORMAL) { |
338 | k = count6bits(n->in_osrcmsk6.i6); |
339 | mask = &n->in_osrcmsk6; |
340 | } else { |
341 | k = 0; |
342 | mask = NULL; |
343 | } |
344 | ipf_inet6_mask_del(k, mask, &softn->ipf_nat6_map_mask); |
345 | |
346 | if (n->in_mnext != NULL) |
347 | n->in_mnext->in_pmnext = n->in_pmnext; |
348 | *n->in_pmnext = n->in_mnext; |
349 | n->in_use--; |
350 | } |
351 | |
352 | |
353 | /* ------------------------------------------------------------------------ */ |
354 | /* Function: ipf_nat6_hostmap */ |
355 | /* Returns: struct hostmap* - NULL if no hostmap could be created, */ |
356 | /* else a pointer to the hostmapping to use */ |
357 | /* Parameters: np(I) - pointer to NAT rule */ |
358 | /* real(I) - real IP address */ |
359 | /* map(I) - mapped IP address */ |
360 | /* port(I) - destination port number */ |
361 | /* Write Locks: ipf_nat */ |
362 | /* */ |
363 | /* Check if an ip address has already been allocated for a given mapping */ |
364 | /* that is not doing port based translation. If is not yet allocated, then */ |
365 | /* create a new entry if a non-NULL NAT rule pointer has been supplied. */ |
366 | /* ------------------------------------------------------------------------ */ |
367 | static struct hostmap * |
368 | ipf_nat6_hostmap(ipf_nat_softc_t *softn, ipnat_t *np, i6addr_t *src, |
369 | i6addr_t *dst, i6addr_t *map, u_32_t port) |
370 | { |
371 | hostmap_t *hm; |
372 | u_int hv; |
373 | |
374 | hv = (src->i6[3] ^ dst->i6[3]); |
375 | hv += (src->i6[2] ^ dst->i6[2]); |
376 | hv += (src->i6[1] ^ dst->i6[1]); |
377 | hv += (src->i6[0] ^ dst->i6[0]); |
378 | hv += src->i6[3]; |
379 | hv += src->i6[2]; |
380 | hv += src->i6[1]; |
381 | hv += src->i6[0]; |
382 | hv += dst->i6[3]; |
383 | hv += dst->i6[2]; |
384 | hv += dst->i6[1]; |
385 | hv += dst->i6[0]; |
386 | hv %= HOSTMAP_SIZE; |
387 | for (hm = softn->ipf_hm_maptable[hv]; hm; hm = hm->hm_next) |
388 | if (IP6_EQ(&hm->hm_osrc6, src) && |
389 | IP6_EQ(&hm->hm_odst6, dst) && |
390 | ((np == NULL) || (np == hm->hm_ipnat)) && |
391 | ((port == 0) || (port == hm->hm_port))) { |
392 | softn->ipf_nat_stats.ns_hm_addref++; |
393 | hm->hm_ref++; |
394 | return hm; |
395 | } |
396 | |
397 | if (np == NULL) { |
398 | softn->ipf_nat_stats.ns_hm_nullnp++; |
399 | return NULL; |
400 | } |
401 | |
402 | KMALLOC(hm, hostmap_t *); |
403 | if (hm) { |
404 | hm->hm_next = softn->ipf_hm_maplist; |
405 | hm->hm_pnext = &softn->ipf_hm_maplist; |
406 | if (softn->ipf_hm_maplist != NULL) |
407 | softn->ipf_hm_maplist->hm_pnext = &hm->hm_next; |
408 | softn->ipf_hm_maplist = hm; |
409 | hm->hm_hnext = softn->ipf_hm_maptable[hv]; |
410 | hm->hm_phnext = softn->ipf_hm_maptable + hv; |
411 | if (softn->ipf_hm_maptable[hv] != NULL) |
412 | softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; |
413 | softn->ipf_hm_maptable[hv] = hm; |
414 | hm->hm_ipnat = np; |
415 | np->in_use++; |
416 | hm->hm_osrcip6 = *src; |
417 | hm->hm_odstip6 = *dst; |
418 | hm->hm_nsrcip6 = *map; |
419 | hm->hm_ndstip6.i6[0] = 0; |
420 | hm->hm_ndstip6.i6[1] = 0; |
421 | hm->hm_ndstip6.i6[2] = 0; |
422 | hm->hm_ndstip6.i6[3] = 0; |
423 | hm->hm_ref = 1; |
424 | hm->hm_port = port; |
425 | hm->hm_hv = hv; |
426 | hm->hm_v = 6; |
427 | softn->ipf_nat_stats.ns_hm_new++; |
428 | } else { |
429 | softn->ipf_nat_stats.ns_hm_newfail++; |
430 | } |
431 | return hm; |
432 | } |
433 | |
434 | |
435 | /* ------------------------------------------------------------------------ */ |
436 | /* Function: ipf_nat6_newmap */ |
437 | /* Returns: int - -1 == error, 0 == success */ |
438 | /* Parameters: fin(I) - pointer to packet information */ |
439 | /* nat(I) - pointer to NAT entry */ |
440 | /* ni(I) - pointer to structure with misc. information needed */ |
441 | /* to create new NAT entry. */ |
442 | /* */ |
443 | /* Given an empty NAT structure, populate it with new information about a */ |
444 | /* new NAT session, as defined by the matching NAT rule. */ |
445 | /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ |
446 | /* to the new IP address for the translation. */ |
447 | /* ------------------------------------------------------------------------ */ |
448 | int |
449 | ipf_nat6_newmap(fr_info_t *fin, nat_t *nat, natinfo_t *ni) |
450 | { |
451 | ipf_main_softc_t *softc = fin->fin_main_soft; |
452 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
453 | u_short st_port, dport, sport, port, sp, dp; |
454 | i6addr_t in, st_ip; |
455 | hostmap_t *hm; |
456 | u_32_t flags; |
457 | ipnat_t *np; |
458 | nat_t *natl; |
459 | int l; |
460 | |
461 | /* |
462 | * If it's an outbound packet which doesn't match any existing |
463 | * record, then create a new port |
464 | */ |
465 | l = 0; |
466 | hm = NULL; |
467 | np = ni->nai_np; |
468 | st_ip = np->in_snip6; |
469 | st_port = np->in_spnext; |
470 | flags = nat->nat_flags; |
471 | |
472 | if (flags & IPN_ICMPQUERY) { |
473 | sport = fin->fin_data[1]; |
474 | dport = 0; |
475 | } else { |
476 | sport = htons(fin->fin_data[0]); |
477 | dport = htons(fin->fin_data[1]); |
478 | } |
479 | |
480 | /* |
481 | * Do a loop until we either run out of entries to try or we find |
482 | * a NAT mapping that isn't currently being used. This is done |
483 | * because the change to the source is not (usually) being fixed. |
484 | */ |
485 | do { |
486 | port = 0; |
487 | in = np->in_nsrc.na_nextaddr; |
488 | if (l == 0) { |
489 | /* |
490 | * Check to see if there is an existing NAT |
491 | * setup for this IP address pair. |
492 | */ |
493 | hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6, |
494 | &fin->fin_dst6, &in, 0); |
495 | if (hm != NULL) |
496 | in = hm->hm_nsrcip6; |
497 | } else if ((l == 1) && (hm != NULL)) { |
498 | ipf_nat_hostmapdel(softc, &hm); |
499 | } |
500 | |
501 | nat->nat_hm = hm; |
502 | |
503 | if (IP6_ISONES(&np->in_nsrcmsk6) && (np->in_spnext == 0)) { |
504 | if (l > 0) { |
505 | NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_1); |
506 | return -1; |
507 | } |
508 | } |
509 | |
510 | if ((np->in_redir == NAT_BIMAP) && |
511 | IP6_EQ(&np->in_osrcmsk6, &np->in_nsrcmsk6)) { |
512 | i6addr_t temp; |
513 | /* |
514 | * map the address block in a 1:1 fashion |
515 | */ |
516 | temp.i6[0] = fin->fin_src6.i6[0] & |
517 | ~np->in_osrcmsk6.i6[0]; |
518 | temp.i6[1] = fin->fin_src6.i6[1] & |
519 | ~np->in_osrcmsk6.i6[1]; |
520 | temp.i6[2] = fin->fin_src6.i6[2] & |
521 | ~np->in_osrcmsk6.i6[0]; |
522 | temp.i6[3] = fin->fin_src6.i6[3] & |
523 | ~np->in_osrcmsk6.i6[3]; |
524 | in = np->in_nsrcip6; |
525 | IP6_MERGE(&in, &temp, &np->in_osrc); |
526 | |
527 | #ifdef NEED_128BIT_MATH |
528 | } else if (np->in_redir & NAT_MAPBLK) { |
529 | if ((l >= np->in_ppip) || ((l > 0) && |
530 | !(flags & IPN_TCPUDP))) { |
531 | NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_2); |
532 | return -1; |
533 | } |
534 | /* |
535 | * map-block - Calculate destination address. |
536 | */ |
537 | IP6_MASK(&in, &fin->fin_src6, &np->in_osrcmsk6); |
538 | in = ntohl(in); |
539 | inb = in; |
540 | in.s_addr /= np->in_ippip; |
541 | in.s_addr &= ntohl(~np->in_nsrcmsk6); |
542 | in.s_addr += ntohl(np->in_nsrcaddr6); |
543 | /* |
544 | * Calculate destination port. |
545 | */ |
546 | if ((flags & IPN_TCPUDP) && |
547 | (np->in_ppip != 0)) { |
548 | port = ntohs(sport) + l; |
549 | port %= np->in_ppip; |
550 | port += np->in_ppip * |
551 | (inb.s_addr % np->in_ippip); |
552 | port += MAPBLK_MINPORT; |
553 | port = htons(port); |
554 | } |
555 | #endif |
556 | |
557 | } else if (IP6_ISZERO(&np->in_nsrcaddr) && |
558 | IP6_ISONES(&np->in_nsrcmsk)) { |
559 | /* |
560 | * 0/32 - use the interface's IP address. |
561 | */ |
562 | if ((l > 0) || |
563 | ipf_ifpaddr(softc, 6, FRI_NORMAL, fin->fin_ifp, |
564 | &in, NULL) == -1) { |
565 | NBUMPSIDE6DX(1, ns_new_ifpaddr, |
566 | ns_new_ifpaddr_1); |
567 | return -1; |
568 | } |
569 | |
570 | } else if (IP6_ISZERO(&np->in_nsrcip6) && |
571 | IP6_ISZERO(&np->in_nsrcmsk6)) { |
572 | /* |
573 | * 0/0 - use the original source address/port. |
574 | */ |
575 | if (l > 0) { |
576 | NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_3); |
577 | return -1; |
578 | } |
579 | in = fin->fin_src6; |
580 | |
581 | } else if (!IP6_ISONES(&np->in_nsrcmsk6) && |
582 | (np->in_spnext == 0) && ((l > 0) || (hm == NULL))) { |
583 | IP6_INC(&np->in_snip6); |
584 | } |
585 | |
586 | natl = NULL; |
587 | |
588 | if ((flags & IPN_TCPUDP) && |
589 | ((np->in_redir & NAT_MAPBLK) == 0) && |
590 | (np->in_flags & IPN_AUTOPORTMAP)) { |
591 | #ifdef NEED_128BIT_MATH |
592 | /* |
593 | * "ports auto" (without map-block) |
594 | */ |
595 | if ((l > 0) && (l % np->in_ppip == 0)) { |
596 | if ((l > np->in_ppip) && |
597 | !IP6_ISONES(&np->in_nsrcmsk)) { |
598 | IP6_INC(&np->in_snip6) |
599 | } |
600 | } |
601 | if (np->in_ppip != 0) { |
602 | port = ntohs(sport); |
603 | port += (l % np->in_ppip); |
604 | port %= np->in_ppip; |
605 | port += np->in_ppip * |
606 | (ntohl(fin->fin_src6) % |
607 | np->in_ippip); |
608 | port += MAPBLK_MINPORT; |
609 | port = htons(port); |
610 | } |
611 | #endif |
612 | |
613 | } else if (((np->in_redir & NAT_MAPBLK) == 0) && |
614 | (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) { |
615 | /* |
616 | * Standard port translation. Select next port. |
617 | */ |
618 | if (np->in_flags & IPN_SEQUENTIAL) { |
619 | port = np->in_spnext; |
620 | } else { |
621 | port = ipf_random() % (np->in_spmax - |
622 | np->in_spmin + 1); |
623 | port += np->in_spmin; |
624 | } |
625 | port = htons(port); |
626 | np->in_spnext++; |
627 | |
628 | if (np->in_spnext > np->in_spmax) { |
629 | np->in_spnext = np->in_spmin; |
630 | if (!IP6_ISONES(&np->in_nsrcmsk6)) { |
631 | IP6_INC(&np->in_snip6); |
632 | } |
633 | } |
634 | } |
635 | |
636 | if (np->in_flags & IPN_SIPRANGE) { |
637 | if (IP6_GT(&np->in_snip, &np->in_nsrcmsk)) |
638 | np->in_snip6 = np->in_nsrcip6; |
639 | } else { |
640 | i6addr_t a1, a2; |
641 | |
642 | a1 = np->in_snip6; |
643 | IP6_INC(&a1); |
644 | IP6_AND(&a1, &np->in_nsrcmsk6, &a2); |
645 | |
646 | if (!IP6_ISONES(&np->in_nsrcmsk6) && |
647 | IP6_GT(&a2, &np->in_nsrcip6)) { |
648 | IP6_ADD(&np->in_nsrcip6, 1, &np->in_snip6); |
649 | } |
650 | } |
651 | |
652 | if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) |
653 | port = sport; |
654 | |
655 | /* |
656 | * Here we do a lookup of the connection as seen from |
657 | * the outside. If an IP# pair already exists, try |
658 | * again. So if you have A->B becomes C->B, you can |
659 | * also have D->E become C->E but not D->B causing |
660 | * another C->B. Also take protocol and ports into |
661 | * account when determining whether a pre-existing |
662 | * NAT setup will cause an external conflict where |
663 | * this is appropriate. |
664 | */ |
665 | sp = fin->fin_data[0]; |
666 | dp = fin->fin_data[1]; |
667 | fin->fin_data[0] = fin->fin_data[1]; |
668 | fin->fin_data[1] = ntohs(port); |
669 | natl = ipf_nat6_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), |
670 | (u_int)fin->fin_p, &fin->fin_dst6.in6, |
671 | &in.in6); |
672 | fin->fin_data[0] = sp; |
673 | fin->fin_data[1] = dp; |
674 | |
675 | /* |
676 | * Has the search wrapped around and come back to the |
677 | * start ? |
678 | */ |
679 | if ((natl != NULL) && |
680 | (np->in_spnext != 0) && (st_port == np->in_spnext) && |
681 | (!IP6_ISZERO(&np->in_snip6) && |
682 | IP6_EQ(&st_ip, &np->in_snip6))) { |
683 | NBUMPSIDE6D(1, ns_wrap); |
684 | return -1; |
685 | } |
686 | l++; |
687 | } while (natl != NULL); |
688 | |
689 | /* Setup the NAT table */ |
690 | nat->nat_osrc6 = fin->fin_src6; |
691 | nat->nat_nsrc6 = in; |
692 | nat->nat_odst6 = fin->fin_dst6; |
693 | nat->nat_ndst6 = fin->fin_dst6; |
694 | if (nat->nat_hm == NULL) |
695 | nat->nat_hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6, |
696 | &fin->fin_dst6, |
697 | &nat->nat_nsrc6, 0); |
698 | |
699 | if (flags & IPN_TCPUDP) { |
700 | nat->nat_osport = sport; |
701 | nat->nat_nsport = port; /* sport */ |
702 | nat->nat_odport = dport; |
703 | nat->nat_ndport = dport; |
704 | ((tcphdr_t *)fin->fin_dp)->th_sport = port; |
705 | } else if (flags & IPN_ICMPQUERY) { |
706 | nat->nat_oicmpid = fin->fin_data[1]; |
707 | ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = port; |
708 | nat->nat_nicmpid = port; |
709 | } |
710 | return 0; |
711 | } |
712 | |
713 | |
714 | /* ------------------------------------------------------------------------ */ |
715 | /* Function: ipf_nat6_newrdr */ |
716 | /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ |
717 | /* allow rule to be moved if IPN_ROUNDR is set. */ |
718 | /* Parameters: fin(I) - pointer to packet information */ |
719 | /* nat(I) - pointer to NAT entry */ |
720 | /* ni(I) - pointer to structure with misc. information needed */ |
721 | /* to create new NAT entry. */ |
722 | /* */ |
723 | /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ |
724 | /* to the new IP address for the translation. */ |
725 | /* ------------------------------------------------------------------------ */ |
726 | int |
727 | ipf_nat6_newrdr(fr_info_t *fin, nat_t *nat, natinfo_t *ni) |
728 | { |
729 | ipf_main_softc_t *softc = fin->fin_main_soft; |
730 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
731 | u_short nport, dport, sport; |
732 | u_short sp, dp; |
733 | hostmap_t *hm; |
734 | u_32_t flags; |
735 | i6addr_t in; |
736 | ipnat_t *np; |
737 | nat_t *natl; |
738 | int move; |
739 | |
740 | move = 1; |
741 | hm = NULL; |
742 | in.i6[0] = 0; |
743 | in.i6[1] = 0; |
744 | in.i6[2] = 0; |
745 | in.i6[3] = 0; |
746 | np = ni->nai_np; |
747 | flags = nat->nat_flags; |
748 | |
749 | if (flags & IPN_ICMPQUERY) { |
750 | dport = fin->fin_data[1]; |
751 | sport = 0; |
752 | } else { |
753 | sport = htons(fin->fin_data[0]); |
754 | dport = htons(fin->fin_data[1]); |
755 | } |
756 | |
757 | /* TRACE sport, dport */ |
758 | |
759 | |
760 | /* |
761 | * If the matching rule has IPN_STICKY set, then we want to have the |
762 | * same rule kick in as before. Why would this happen? If you have |
763 | * a collection of rdr rules with "round-robin sticky", the current |
764 | * packet might match a different one to the previous connection but |
765 | * we want the same destination to be used. |
766 | */ |
767 | if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) && |
768 | ((np->in_flags & IPN_STICKY) != 0)) { |
769 | hm = ipf_nat6_hostmap(softn, NULL, &fin->fin_src6, |
770 | &fin->fin_dst6, &in, (u_32_t)dport); |
771 | if (hm != NULL) { |
772 | in = hm->hm_ndstip6; |
773 | np = hm->hm_ipnat; |
774 | ni->nai_np = np; |
775 | move = 0; |
776 | } |
777 | } |
778 | |
779 | /* |
780 | * Otherwise, it's an inbound packet. Most likely, we don't |
781 | * want to rewrite source ports and source addresses. Instead, |
782 | * we want to rewrite to a fixed internal address and fixed |
783 | * internal port. |
784 | */ |
785 | if (np->in_flags & IPN_SPLIT) { |
786 | in = np->in_dnip6; |
787 | |
788 | if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { |
789 | hm = ipf_nat6_hostmap(softn, NULL, &fin->fin_src6, |
790 | &fin->fin_dst6, &in, |
791 | (u_32_t)dport); |
792 | if (hm != NULL) { |
793 | in = hm->hm_ndstip6; |
794 | move = 0; |
795 | } |
796 | } |
797 | |
798 | if (hm == NULL || hm->hm_ref == 1) { |
799 | if (IP6_EQ(&np->in_ndstip6, &in)) { |
800 | np->in_dnip6 = np->in_ndstmsk6; |
801 | move = 0; |
802 | } else { |
803 | np->in_dnip6 = np->in_ndstip6; |
804 | } |
805 | } |
806 | |
807 | } else if (IP6_ISZERO(&np->in_ndstaddr) && |
808 | IP6_ISONES(&np->in_ndstmsk)) { |
809 | /* |
810 | * 0/32 - use the interface's IP address. |
811 | */ |
812 | if (ipf_ifpaddr(softc, 6, FRI_NORMAL, fin->fin_ifp, |
813 | &in, NULL) == -1) { |
814 | NBUMPSIDE6DX(0, ns_new_ifpaddr, ns_new_ifpaddr_2); |
815 | return -1; |
816 | } |
817 | |
818 | } else if (IP6_ISZERO(&np->in_ndstip6) && |
819 | IP6_ISZERO(&np->in_ndstmsk6)) { |
820 | /* |
821 | * 0/0 - use the original destination address/port. |
822 | */ |
823 | in = fin->fin_dst6; |
824 | |
825 | } else if (np->in_redir == NAT_BIMAP && |
826 | IP6_EQ(&np->in_ndstmsk6, &np->in_odstmsk6)) { |
827 | i6addr_t temp; |
828 | /* |
829 | * map the address block in a 1:1 fashion |
830 | */ |
831 | temp.i6[0] = fin->fin_dst6.i6[0] & ~np->in_osrcmsk6.i6[0]; |
832 | temp.i6[1] = fin->fin_dst6.i6[1] & ~np->in_osrcmsk6.i6[1]; |
833 | temp.i6[2] = fin->fin_dst6.i6[2] & ~np->in_osrcmsk6.i6[0]; |
834 | temp.i6[3] = fin->fin_dst6.i6[3] & ~np->in_osrcmsk6.i6[3]; |
835 | in = np->in_ndstip6; |
836 | IP6_MERGE(&in, &temp, &np->in_ndstmsk6); |
837 | } else { |
838 | in = np->in_ndstip6; |
839 | } |
840 | |
841 | if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) |
842 | nport = dport; |
843 | else { |
844 | /* |
845 | * Whilst not optimized for the case where |
846 | * pmin == pmax, the gain is not significant. |
847 | */ |
848 | if (((np->in_flags & IPN_FIXEDDPORT) == 0) && |
849 | (np->in_odport != np->in_dtop)) { |
850 | nport = ntohs(dport) - np->in_odport + np->in_dpmax; |
851 | nport = htons(nport); |
852 | } else { |
853 | nport = htons(np->in_dpnext); |
854 | np->in_dpnext++; |
855 | if (np->in_dpnext > np->in_dpmax) |
856 | np->in_dpnext = np->in_dpmin; |
857 | } |
858 | } |
859 | |
860 | /* |
861 | * When the redirect-to address is set to 0.0.0.0, just |
862 | * assume a blank `forwarding' of the packet. We don't |
863 | * setup any translation for this either. |
864 | */ |
865 | if (IP6_ISZERO(&in)) { |
866 | if (nport == dport) { |
867 | NBUMPSIDE6D(0, ns_xlate_null); |
868 | return -1; |
869 | } |
870 | in = fin->fin_dst6; |
871 | } |
872 | |
873 | /* |
874 | * Check to see if this redirect mapping already exists and if |
875 | * it does, return "failure" (allowing it to be created will just |
876 | * cause one or both of these "connections" to stop working.) |
877 | */ |
878 | sp = fin->fin_data[0]; |
879 | dp = fin->fin_data[1]; |
880 | fin->fin_data[1] = fin->fin_data[0]; |
881 | fin->fin_data[0] = ntohs(nport); |
882 | natl = ipf_nat6_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), |
883 | (u_int)fin->fin_p, &in.in6, |
884 | &fin->fin_src6.in6); |
885 | fin->fin_data[0] = sp; |
886 | fin->fin_data[1] = dp; |
887 | if (natl != NULL) { |
888 | NBUMPSIDE6D(0, ns_xlate_exists); |
889 | return -1; |
890 | } |
891 | |
892 | nat->nat_ndst6 = in; |
893 | nat->nat_odst6 = fin->fin_dst6; |
894 | nat->nat_nsrc6 = fin->fin_src6; |
895 | nat->nat_osrc6 = fin->fin_src6; |
896 | if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0)) |
897 | nat->nat_hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6, |
898 | &fin->fin_dst6, &in, |
899 | (u_32_t)dport); |
900 | |
901 | if (flags & IPN_TCPUDP) { |
902 | nat->nat_odport = dport; |
903 | nat->nat_ndport = nport; |
904 | nat->nat_osport = sport; |
905 | nat->nat_nsport = sport; |
906 | ((tcphdr_t *)fin->fin_dp)->th_dport = nport; |
907 | } else if (flags & IPN_ICMPQUERY) { |
908 | nat->nat_oicmpid = fin->fin_data[1]; |
909 | ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = nport; |
910 | nat->nat_nicmpid = nport; |
911 | } |
912 | |
913 | return move; |
914 | } |
915 | |
916 | /* ------------------------------------------------------------------------ */ |
917 | /* Function: ipf_nat6_add */ |
918 | /* Returns: nat6_t* - NULL == failure to create new NAT structure, */ |
919 | /* else pointer to new NAT structure */ |
920 | /* Parameters: fin(I) - pointer to packet information */ |
921 | /* np(I) - pointer to NAT rule */ |
922 | /* natsave(I) - pointer to where to store NAT struct pointer */ |
923 | /* flags(I) - flags describing the current packet */ |
924 | /* direction(I) - direction of packet (in/out) */ |
925 | /* Write Lock: ipf_nat */ |
926 | /* */ |
927 | /* Attempts to create a new NAT entry. Does not actually change the packet */ |
928 | /* in any way. */ |
929 | /* */ |
930 | /* This fucntion is in three main parts: (1) deal with creating a new NAT */ |
931 | /* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */ |
932 | /* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */ |
933 | /* and (3) building that structure and putting it into the NAT table(s). */ |
934 | /* */ |
935 | /* NOTE: natsave should NOT be used top point back to an ipstate_t struct */ |
936 | /* as it can result in memory being corrupted. */ |
937 | /* ------------------------------------------------------------------------ */ |
938 | nat_t * |
939 | ipf_nat6_add(fr_info_t *fin, ipnat_t *np, nat_t **natsave, u_int flags, |
940 | int direction) |
941 | { |
942 | ipf_main_softc_t *softc = fin->fin_main_soft; |
943 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
944 | hostmap_t *hm = NULL; |
945 | nat_t *nat, *natl; |
946 | natstat_t *nsp; |
947 | u_int nflags; |
948 | natinfo_t ni; |
949 | int move; |
950 | #if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC) |
951 | qpktinfo_t *qpi = fin->fin_qpi; |
952 | #endif |
953 | |
954 | nsp = &softn->ipf_nat_stats; |
955 | |
956 | if ((nsp->ns_active * 100 / softn->ipf_nat_table_max) > |
957 | softn->ipf_nat_table_wm_high) { |
958 | softn->ipf_nat_doflush = 1; |
959 | } |
960 | |
961 | if (nsp->ns_active >= softn->ipf_nat_table_max) { |
962 | NBUMPSIDE6(fin->fin_out, ns_table_max); |
963 | return NULL; |
964 | } |
965 | |
966 | move = 1; |
967 | nflags = np->in_flags & flags; |
968 | nflags &= NAT_FROMRULE; |
969 | |
970 | ni.nai_np = np; |
971 | ni.nai_dport = 0; |
972 | ni.nai_sport = 0; |
973 | |
974 | /* Give me a new nat */ |
975 | KMALLOC(nat, nat_t *); |
976 | if (nat == NULL) { |
977 | NBUMPSIDE6(fin->fin_out, ns_memfail); |
978 | /* |
979 | * Try to automatically tune the max # of entries in the |
980 | * table allowed to be less than what will cause kmem_alloc() |
981 | * to fail and try to eliminate panics due to out of memory |
982 | * conditions arising. |
983 | */ |
984 | if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) && |
985 | (nsp->ns_active > 100)) { |
986 | softn->ipf_nat_table_max = nsp->ns_active - 100; |
987 | printf("table_max reduced to %d\n" , |
988 | softn->ipf_nat_table_max); |
989 | } |
990 | return NULL; |
991 | } |
992 | |
993 | if (flags & IPN_ICMPQUERY) { |
994 | /* |
995 | * In the ICMP query NAT code, we translate the ICMP id fields |
996 | * to make them unique. This is indepedent of the ICMP type |
997 | * (e.g. in the unlikely event that a host sends an echo and |
998 | * an tstamp request with the same id, both packets will have |
999 | * their ip address/id field changed in the same way). |
1000 | */ |
1001 | /* The icmp6_id field is used by the sender to identify the |
1002 | * process making the icmp request. (the receiver justs |
1003 | * copies it back in its response). So, it closely matches |
1004 | * the concept of source port. We overlay sport, so we can |
1005 | * maximally reuse the existing code. |
1006 | */ |
1007 | ni.nai_sport = fin->fin_data[1]; |
1008 | ni.nai_dport = 0; |
1009 | } |
1010 | |
1011 | bzero((char *)nat, sizeof(*nat)); |
1012 | nat->nat_flags = flags; |
1013 | nat->nat_redir = np->in_redir; |
1014 | nat->nat_dir = direction; |
1015 | nat->nat_pr[0] = fin->fin_p; |
1016 | nat->nat_pr[1] = fin->fin_p; |
1017 | |
1018 | /* |
1019 | * Search the current table for a match and create a new mapping |
1020 | * if there is none found. |
1021 | */ |
1022 | if (np->in_redir & NAT_DIVERTUDP) { |
1023 | move = ipf_nat6_newdivert(fin, nat, &ni); |
1024 | |
1025 | } else if (np->in_redir & NAT_REWRITE) { |
1026 | move = ipf_nat6_newrewrite(fin, nat, &ni); |
1027 | |
1028 | } else if (direction == NAT_OUTBOUND) { |
1029 | /* |
1030 | * We can now arrange to call this for the same connection |
1031 | * because ipf_nat6_new doesn't protect the code path into |
1032 | * this function. |
1033 | */ |
1034 | natl = ipf_nat6_outlookup(fin, nflags, (u_int)fin->fin_p, |
1035 | &fin->fin_src6.in6, |
1036 | &fin->fin_dst6.in6); |
1037 | if (natl != NULL) { |
1038 | KFREE(nat); |
1039 | nat = natl; |
1040 | goto done; |
1041 | } |
1042 | |
1043 | move = ipf_nat6_newmap(fin, nat, &ni); |
1044 | } else { |
1045 | /* |
1046 | * NAT_INBOUND is used for redirects rules |
1047 | */ |
1048 | natl = ipf_nat6_inlookup(fin, nflags, (u_int)fin->fin_p, |
1049 | &fin->fin_src6.in6, |
1050 | &fin->fin_dst6.in6); |
1051 | if (natl != NULL) { |
1052 | KFREE(nat); |
1053 | nat = natl; |
1054 | goto done; |
1055 | } |
1056 | |
1057 | move = ipf_nat6_newrdr(fin, nat, &ni); |
1058 | } |
1059 | if (move == -1) |
1060 | goto badnat; |
1061 | |
1062 | np = ni.nai_np; |
1063 | |
1064 | nat->nat_mssclamp = np->in_mssclamp; |
1065 | nat->nat_me = natsave; |
1066 | nat->nat_fr = fin->fin_fr; |
1067 | nat->nat_rev = fin->fin_rev; |
1068 | nat->nat_ptr = np; |
1069 | nat->nat_dlocal = np->in_dlocal; |
1070 | |
1071 | if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) { |
1072 | if (ipf_proxy_new(fin, nat) == -1) { |
1073 | NBUMPSIDE6D(fin->fin_out, ns_appr_fail); |
1074 | goto badnat; |
1075 | } |
1076 | } |
1077 | |
1078 | nat->nat_ifps[0] = np->in_ifps[0]; |
1079 | if (np->in_ifps[0] != NULL) { |
1080 | COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]); |
1081 | } |
1082 | |
1083 | nat->nat_ifps[1] = np->in_ifps[1]; |
1084 | if (np->in_ifps[1] != NULL) { |
1085 | COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]); |
1086 | } |
1087 | |
1088 | if (ipf_nat6_finalise(fin, nat) == -1) { |
1089 | goto badnat; |
1090 | } |
1091 | |
1092 | np->in_use++; |
1093 | |
1094 | if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { |
1095 | if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) { |
1096 | ipf_nat6_delrdr(softn, np); |
1097 | ipf_nat6_addrdr(softn, np); |
1098 | } else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) { |
1099 | ipf_nat6_delmap(softn, np); |
1100 | ipf_nat6_addmap(softn, np); |
1101 | } |
1102 | } |
1103 | |
1104 | if (flags & SI_WILDP) |
1105 | nsp->ns_wilds++; |
1106 | softn->ipf_nat_stats.ns_proto[nat->nat_pr[0]]++; |
1107 | |
1108 | goto done; |
1109 | badnat: |
1110 | NBUMPSIDE6(fin->fin_out, ns_badnatnew); |
1111 | if ((hm = nat->nat_hm) != NULL) |
1112 | ipf_nat_hostmapdel(softc, &hm); |
1113 | KFREE(nat); |
1114 | nat = NULL; |
1115 | done: |
1116 | if (nat != NULL && np != NULL) |
1117 | np->in_hits++; |
1118 | if (natsave != NULL) |
1119 | *natsave = nat; |
1120 | return nat; |
1121 | } |
1122 | |
1123 | |
1124 | /* ------------------------------------------------------------------------ */ |
1125 | /* Function: ipf_nat6_finalise */ |
1126 | /* Returns: int - 0 == sucess, -1 == failure */ |
1127 | /* Parameters: fin(I) - pointer to packet information */ |
1128 | /* nat(I) - pointer to NAT entry */ |
1129 | /* Write Lock: ipf_nat */ |
1130 | /* */ |
1131 | /* This is the tail end of constructing a new NAT entry and is the same */ |
1132 | /* for both IPv4 and IPv6. */ |
1133 | /* ------------------------------------------------------------------------ */ |
1134 | /*ARGSUSED*/ |
1135 | int |
1136 | ipf_nat6_finalise(fr_info_t *fin, nat_t *nat) |
1137 | { |
1138 | ipf_main_softc_t *softc = fin->fin_main_soft; |
1139 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
1140 | u_32_t sum1, sum2, sumd; |
1141 | frentry_t *fr; |
1142 | |
1143 | switch (fin->fin_p) |
1144 | { |
1145 | case IPPROTO_ICMPV6 : |
1146 | sum1 = LONG_SUM6(&nat->nat_osrc6); |
1147 | sum1 += ntohs(nat->nat_oicmpid); |
1148 | sum2 = LONG_SUM6(&nat->nat_nsrc6); |
1149 | sum2 += ntohs(nat->nat_nicmpid); |
1150 | CALC_SUMD(sum1, sum2, sumd); |
1151 | nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); |
1152 | |
1153 | sum1 = LONG_SUM6(&nat->nat_odst6); |
1154 | sum2 = LONG_SUM6(&nat->nat_ndst6); |
1155 | CALC_SUMD(sum1, sum2, sumd); |
1156 | nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); |
1157 | break; |
1158 | |
1159 | case IPPROTO_TCP : |
1160 | case IPPROTO_UDP : |
1161 | sum1 = LONG_SUM6(&nat->nat_osrc6); |
1162 | sum1 += ntohs(nat->nat_osport); |
1163 | sum2 = LONG_SUM6(&nat->nat_nsrc6); |
1164 | sum2 += ntohs(nat->nat_nsport); |
1165 | CALC_SUMD(sum1, sum2, sumd); |
1166 | nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); |
1167 | |
1168 | sum1 = LONG_SUM6(&nat->nat_odst6); |
1169 | sum1 += ntohs(nat->nat_odport); |
1170 | sum2 = LONG_SUM6(&nat->nat_ndst6); |
1171 | sum2 += ntohs(nat->nat_ndport); |
1172 | CALC_SUMD(sum1, sum2, sumd); |
1173 | nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); |
1174 | break; |
1175 | |
1176 | default : |
1177 | sum1 = LONG_SUM6(&nat->nat_osrc6); |
1178 | sum2 = LONG_SUM6(&nat->nat_nsrc6); |
1179 | CALC_SUMD(sum1, sum2, sumd); |
1180 | nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); |
1181 | |
1182 | sum1 = LONG_SUM6(&nat->nat_odst6); |
1183 | sum2 = LONG_SUM6(&nat->nat_ndst6); |
1184 | CALC_SUMD(sum1, sum2, sumd); |
1185 | nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); |
1186 | break; |
1187 | } |
1188 | |
1189 | /* |
1190 | * Compute the partial checksum, just in case. |
1191 | * This is only ever placed into outbound packets so care needs |
1192 | * to be taken over which pair of addresses are used. |
1193 | */ |
1194 | if (nat->nat_dir == NAT_OUTBOUND) { |
1195 | sum1 = LONG_SUM6(&nat->nat_nsrc6); |
1196 | sum1 += LONG_SUM6(&nat->nat_ndst6); |
1197 | } else { |
1198 | sum1 = LONG_SUM6(&nat->nat_osrc6); |
1199 | sum1 += LONG_SUM6(&nat->nat_odst6); |
1200 | } |
1201 | sum1 += nat->nat_pr[1]; |
1202 | nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16); |
1203 | |
1204 | if ((nat->nat_flags & SI_CLONE) == 0) |
1205 | nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat); |
1206 | |
1207 | if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { |
1208 | nat->nat_mtu[0] = GETIFMTU_6(nat->nat_ifps[0]); |
1209 | } |
1210 | |
1211 | if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { |
1212 | nat->nat_mtu[1] = GETIFMTU_6(nat->nat_ifps[1]); |
1213 | } |
1214 | |
1215 | nat->nat_v[0] = 6; |
1216 | nat->nat_v[1] = 6; |
1217 | |
1218 | if (ipf_nat6_insert(softc, softn, nat) == 0) { |
1219 | if (softn->ipf_nat_logging) |
1220 | ipf_nat_log(softc, softn, nat, NL_NEW); |
1221 | fr = nat->nat_fr; |
1222 | if (fr != NULL) { |
1223 | MUTEX_ENTER(&fr->fr_lock); |
1224 | fr->fr_ref++; |
1225 | MUTEX_EXIT(&fr->fr_lock); |
1226 | } |
1227 | return 0; |
1228 | } |
1229 | |
1230 | NBUMPSIDE6D(fin->fin_out, ns_unfinalised); |
1231 | /* |
1232 | * nat6_insert failed, so cleanup time... |
1233 | */ |
1234 | if (nat->nat_sync != NULL) |
1235 | ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); |
1236 | return -1; |
1237 | } |
1238 | |
1239 | |
1240 | /* ------------------------------------------------------------------------ */ |
1241 | /* Function: ipf_nat6_insert */ |
1242 | /* Returns: int - 0 == sucess, -1 == failure */ |
1243 | /* Parameters: softc(I) - pointer to soft context main structure */ |
1244 | /* softn(I) - pointer to NAT context structure */ |
1245 | /* nat(I) - pointer to NAT structure */ |
1246 | /* Write Lock: ipf_nat */ |
1247 | /* */ |
1248 | /* Insert a NAT entry into the hash tables for searching and add it to the */ |
1249 | /* list of active NAT entries. Adjust global counters when complete. */ |
1250 | /* ------------------------------------------------------------------------ */ |
1251 | static int |
1252 | ipf_nat6_insert(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, nat_t *nat) |
1253 | { |
1254 | u_int hv0, hv1; |
1255 | u_32_t sp, dp; |
1256 | ipnat_t *in; |
1257 | |
1258 | /* |
1259 | * Try and return an error as early as possible, so calculate the hash |
1260 | * entry numbers first and then proceed. |
1261 | */ |
1262 | if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { |
1263 | if ((nat->nat_flags & IPN_TCPUDP) != 0) { |
1264 | sp = nat->nat_osport; |
1265 | dp = nat->nat_odport; |
1266 | } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { |
1267 | sp = 0; |
1268 | dp = nat->nat_oicmpid; |
1269 | } else { |
1270 | sp = 0; |
1271 | dp = 0; |
1272 | } |
1273 | hv0 = NAT_HASH_FN6(&nat->nat_osrc6, sp, 0xffffffff); |
1274 | hv0 = NAT_HASH_FN6(&nat->nat_odst6, hv0 + dp, |
1275 | softn->ipf_nat_table_sz); |
1276 | |
1277 | /* |
1278 | * TRACE nat6_osrc6, nat6_osport, nat6_odst6, |
1279 | * nat6_odport, hv0 |
1280 | */ |
1281 | |
1282 | if ((nat->nat_flags & IPN_TCPUDP) != 0) { |
1283 | sp = nat->nat_nsport; |
1284 | dp = nat->nat_ndport; |
1285 | } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { |
1286 | sp = 0; |
1287 | dp = nat->nat_nicmpid; |
1288 | } else { |
1289 | sp = 0; |
1290 | dp = 0; |
1291 | } |
1292 | hv1 = NAT_HASH_FN6(&nat->nat_nsrc6, sp, 0xffffffff); |
1293 | hv1 = NAT_HASH_FN6(&nat->nat_ndst6, hv1 + dp, |
1294 | softn->ipf_nat_table_sz); |
1295 | /* |
1296 | * TRACE nat6_nsrcaddr, nat6_nsport, nat6_ndstaddr, |
1297 | * nat6_ndport, hv0 |
1298 | */ |
1299 | } else { |
1300 | hv0 = NAT_HASH_FN6(&nat->nat_osrc6, 0, 0xffffffff); |
1301 | hv0 = NAT_HASH_FN6(&nat->nat_odst6, hv0, |
1302 | softn->ipf_nat_table_sz); |
1303 | /* TRACE nat6_osrcip6, nat6_odstip6, hv0 */ |
1304 | |
1305 | hv1 = NAT_HASH_FN6(&nat->nat_nsrc6, 0, 0xffffffff); |
1306 | hv1 = NAT_HASH_FN6(&nat->nat_ndst6, hv1, |
1307 | softn->ipf_nat_table_sz); |
1308 | /* TRACE nat6_nsrcip6, nat6_ndstip6, hv1 */ |
1309 | } |
1310 | |
1311 | if ((nat->nat_dir & NAT_OUTBOUND) == NAT_OUTBOUND) { |
1312 | nat->nat_hv[0] = hv0; |
1313 | nat->nat_hv[1] = hv1; |
1314 | } else { |
1315 | nat->nat_hv[0] = hv1; |
1316 | nat->nat_hv[1] = hv0; |
1317 | } |
1318 | |
1319 | MUTEX_INIT(&nat->nat_lock, "nat entry lock" ); |
1320 | |
1321 | in = nat->nat_ptr; |
1322 | nat->nat_ref = nat->nat_me ? 2 : 1; |
1323 | |
1324 | nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; |
1325 | nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0], |
1326 | nat->nat_v[0]); |
1327 | |
1328 | if (nat->nat_ifnames[1][0] != '\0') { |
1329 | nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; |
1330 | nat->nat_ifps[1] = ipf_resolvenic(softc, nat->nat_ifnames[1], |
1331 | nat->nat_v[1]); |
1332 | } else if (in->in_ifnames[1] != -1) { |
1333 | char *name; |
1334 | |
1335 | name = in->in_names + in->in_ifnames[1]; |
1336 | if (name[1] != '\0' && name[0] != '-' && name[0] != '*') { |
1337 | (void) strncpy(nat->nat_ifnames[1], |
1338 | nat->nat_ifnames[0], LIFNAMSIZ); |
1339 | nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; |
1340 | nat->nat_ifps[1] = nat->nat_ifps[0]; |
1341 | } |
1342 | } |
1343 | if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { |
1344 | nat->nat_mtu[0] = GETIFMTU_6(nat->nat_ifps[0]); |
1345 | } |
1346 | if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { |
1347 | nat->nat_mtu[1] = GETIFMTU_6(nat->nat_ifps[1]); |
1348 | } |
1349 | |
1350 | return ipf_nat_hashtab_add(softc, softn, nat); |
1351 | } |
1352 | |
1353 | |
1354 | /* ------------------------------------------------------------------------ */ |
1355 | /* Function: ipf_nat6_icmperrorlookup */ |
1356 | /* Returns: nat6_t* - point to matching NAT structure */ |
1357 | /* Parameters: fin(I) - pointer to packet information */ |
1358 | /* dir(I) - direction of packet (in/out) */ |
1359 | /* */ |
1360 | /* Check if the ICMP error message is related to an existing TCP, UDP or */ |
1361 | /* ICMP query nat entry. It is assumed that the packet is already of the */ |
1362 | /* the required length. */ |
1363 | /* ------------------------------------------------------------------------ */ |
1364 | nat_t * |
1365 | ipf_nat6_icmperrorlookup(fr_info_t *fin, int dir) |
1366 | { |
1367 | ipf_main_softc_t *softc = fin->fin_main_soft; |
1368 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
1369 | struct icmp6_hdr *orgicmp; |
1370 | int flags = 0, minlen; |
1371 | nat_stat_side_t *nside; |
1372 | tcphdr_t *tcp = NULL; |
1373 | u_short data[2]; |
1374 | ip6_t *oip6; |
1375 | nat_t *nat; |
1376 | u_int p; |
1377 | |
1378 | minlen = 40; |
1379 | nside = &softn->ipf_nat_stats.ns_side6[fin->fin_out]; |
1380 | /* |
1381 | * Does it at least have the return (basic) IP header ? |
1382 | * Only a basic IP header (no options) should be with an ICMP error |
1383 | * header. Also, if it's not an error type, then return. |
1384 | */ |
1385 | if (!(fin->fin_flx & FI_ICMPERR)) { |
1386 | ATOMIC_INCL(nside->ns_icmp_basic); |
1387 | return NULL; |
1388 | } |
1389 | |
1390 | /* |
1391 | * Check packet size |
1392 | */ |
1393 | if (fin->fin_plen < ICMP6ERR_IPICMPHLEN) { |
1394 | ATOMIC_INCL(nside->ns_icmp_size); |
1395 | return NULL; |
1396 | } |
1397 | oip6 = (ip6_t *)((char *)fin->fin_dp + 8); |
1398 | |
1399 | /* |
1400 | * Is the buffer big enough for all of it ? It's the size of the IP |
1401 | * header claimed in the encapsulated part which is of concern. It |
1402 | * may be too big to be in this buffer but not so big that it's |
1403 | * outside the ICMP packet, leading to TCP deref's causing problems. |
1404 | * This is possible because we don't know how big oip_hl is when we |
1405 | * do the pullup early in ipf_check() and thus can't gaurantee it is |
1406 | * all here now. |
1407 | */ |
1408 | #ifdef ipf_nat6_KERNEL |
1409 | { |
1410 | mb_t *m; |
1411 | |
1412 | m = fin->fin_m; |
1413 | # if defined(MENTAT) |
1414 | if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN > |
1415 | (char *)m->b_wptr) { |
1416 | ATOMIC_INCL(nside->ns_icmp_mbuf); |
1417 | return NULL; |
1418 | } |
1419 | # else |
1420 | if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN > |
1421 | (char *)fin->fin_ip + M_LEN(m)) { |
1422 | ATOMIC_INCL(nside->ns_icmp_mbuf); |
1423 | return NULL; |
1424 | } |
1425 | # endif |
1426 | } |
1427 | #endif |
1428 | |
1429 | if (IP6_NEQ(&fin->fin_dst6, &oip6->ip6_src)) { |
1430 | ATOMIC_INCL(nside->ns_icmp_address); |
1431 | return NULL; |
1432 | } |
1433 | |
1434 | p = oip6->ip6_nxt; |
1435 | if (p == IPPROTO_TCP) |
1436 | flags = IPN_TCP; |
1437 | else if (p == IPPROTO_UDP) |
1438 | flags = IPN_UDP; |
1439 | else if (p == IPPROTO_ICMPV6) { |
1440 | orgicmp = (struct icmp6_hdr *)(oip6 + 1); |
1441 | |
1442 | /* see if this is related to an ICMP query */ |
1443 | if (ipf_nat6_icmpquerytype(orgicmp->icmp6_type)) { |
1444 | data[0] = fin->fin_data[0]; |
1445 | data[1] = fin->fin_data[1]; |
1446 | fin->fin_data[0] = 0; |
1447 | fin->fin_data[1] = orgicmp->icmp6_id; |
1448 | |
1449 | flags = IPN_ICMPERR|IPN_ICMPQUERY; |
1450 | /* |
1451 | * NOTE : dir refers to the direction of the original |
1452 | * ip packet. By definition the icmp error |
1453 | * message flows in the opposite direction. |
1454 | */ |
1455 | if (dir == NAT_INBOUND) |
1456 | nat = ipf_nat6_inlookup(fin, flags, p, |
1457 | &oip6->ip6_dst, |
1458 | &oip6->ip6_src); |
1459 | else |
1460 | nat = ipf_nat6_outlookup(fin, flags, p, |
1461 | &oip6->ip6_dst, |
1462 | &oip6->ip6_src); |
1463 | fin->fin_data[0] = data[0]; |
1464 | fin->fin_data[1] = data[1]; |
1465 | return nat; |
1466 | } |
1467 | } |
1468 | |
1469 | if (flags & IPN_TCPUDP) { |
1470 | minlen += 8; /* + 64bits of data to get ports */ |
1471 | /* TRACE (fin,minlen) */ |
1472 | if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) { |
1473 | ATOMIC_INCL(nside->ns_icmp_short); |
1474 | return NULL; |
1475 | } |
1476 | |
1477 | data[0] = fin->fin_data[0]; |
1478 | data[1] = fin->fin_data[1]; |
1479 | tcp = (tcphdr_t *)(oip6 + 1); |
1480 | fin->fin_data[0] = ntohs(tcp->th_dport); |
1481 | fin->fin_data[1] = ntohs(tcp->th_sport); |
1482 | |
1483 | if (dir == NAT_INBOUND) { |
1484 | nat = ipf_nat6_inlookup(fin, flags, p, &oip6->ip6_dst, |
1485 | &oip6->ip6_src); |
1486 | } else { |
1487 | nat = ipf_nat6_outlookup(fin, flags, p, &oip6->ip6_dst, |
1488 | &oip6->ip6_src); |
1489 | } |
1490 | fin->fin_data[0] = data[0]; |
1491 | fin->fin_data[1] = data[1]; |
1492 | return nat; |
1493 | } |
1494 | if (dir == NAT_INBOUND) |
1495 | nat = ipf_nat6_inlookup(fin, 0, p, &oip6->ip6_dst, |
1496 | &oip6->ip6_src); |
1497 | else |
1498 | nat = ipf_nat6_outlookup(fin, 0, p, &oip6->ip6_dst, |
1499 | &oip6->ip6_src); |
1500 | |
1501 | return nat; |
1502 | } |
1503 | |
1504 | |
1505 | /* result = ip1 - ip2 */ |
1506 | u_32_t |
1507 | ipf_nat6_ip6subtract(i6addr_t *ip1, i6addr_t *ip2) |
1508 | { |
1509 | i6addr_t l1, l2, d; |
1510 | u_short *s1, *s2, *ds; |
1511 | u_32_t r; |
1512 | int i; |
1513 | |
1514 | l1 = *ip1; |
1515 | l2 = *ip2; |
1516 | s1 = (u_short *)&l1; |
1517 | s2 = (u_short *)&l2; |
1518 | ds = (u_short *)&d; |
1519 | |
1520 | for (i = 7; i > 0; i--) { |
1521 | if (s1[i] > s2[i]) { |
1522 | ds[i] = s2[i] + 0x10000 - s1[i]; |
1523 | s2[i - 1] += 0x10000; |
1524 | } else { |
1525 | ds[i] = s2[i] - s1[i]; |
1526 | } |
1527 | } |
1528 | if (s2[0] > s1[0]) { |
1529 | ds[0] = s2[0] + 0x10000 - s1[0]; |
1530 | } else { |
1531 | ds[0] = s2[0] - s1[0]; |
1532 | } |
1533 | |
1534 | for (i = 0, r = 0; i < 8; i++) { |
1535 | r += ds[i]; |
1536 | } |
1537 | |
1538 | return r; |
1539 | } |
1540 | |
1541 | |
1542 | /* ------------------------------------------------------------------------ */ |
1543 | /* Function: ipf_nat6_icmperror */ |
1544 | /* Returns: nat6_t* - point to matching NAT structure */ |
1545 | /* Parameters: fin(I) - pointer to packet information */ |
1546 | /* nflags(I) - NAT flags for this packet */ |
1547 | /* dir(I) - direction of packet (in/out) */ |
1548 | /* */ |
1549 | /* Fix up an ICMP packet which is an error message for an existing NAT */ |
1550 | /* session. This will correct both packet header data and checksums. */ |
1551 | /* */ |
1552 | /* This should *ONLY* be used for incoming ICMP error packets to make sure */ |
1553 | /* a NAT'd ICMP packet gets correctly recognised. */ |
1554 | /* ------------------------------------------------------------------------ */ |
1555 | nat_t * |
1556 | ipf_nat6_icmperror(fr_info_t *fin, u_int *nflags, int dir) |
1557 | { |
1558 | ipf_main_softc_t *softc = fin->fin_main_soft; |
1559 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
1560 | u_32_t sum1, sum2, sumd, sumd2; |
1561 | i6addr_t a1, a2, a3, a4; |
1562 | struct icmp6_hdr *icmp6; |
1563 | int flags, dlen, odst; |
1564 | u_short *csump; |
1565 | tcphdr_t *tcp; |
1566 | ip6_t *oip6; |
1567 | nat_t *nat; |
1568 | void *dp; |
1569 | |
1570 | if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { |
1571 | NBUMPSIDE6D(fin->fin_out, ns_icmp_short); |
1572 | return NULL; |
1573 | } |
1574 | |
1575 | /* |
1576 | * ipf_nat6_icmperrorlookup() will return NULL for `defective' packets. |
1577 | */ |
1578 | if ((fin->fin_v != 6) || !(nat = ipf_nat6_icmperrorlookup(fin, dir))) { |
1579 | NBUMPSIDE6D(fin->fin_out, ns_icmp_notfound); |
1580 | return NULL; |
1581 | } |
1582 | |
1583 | tcp = NULL; |
1584 | csump = NULL; |
1585 | flags = 0; |
1586 | sumd2 = 0; |
1587 | *nflags = IPN_ICMPERR; |
1588 | icmp6 = fin->fin_dp; |
1589 | oip6 = (ip6_t *)((u_char *)icmp6 + sizeof(*icmp6)); |
1590 | dp = (u_char *)oip6 + sizeof(*oip6); |
1591 | if (oip6->ip6_nxt == IPPROTO_TCP) { |
1592 | tcp = (tcphdr_t *)dp; |
1593 | csump = (u_short *)&tcp->th_sum; |
1594 | flags = IPN_TCP; |
1595 | } else if (oip6->ip6_nxt == IPPROTO_UDP) { |
1596 | udphdr_t *udp; |
1597 | |
1598 | udp = (udphdr_t *)dp; |
1599 | tcp = (tcphdr_t *)dp; |
1600 | csump = (u_short *)&udp->uh_sum; |
1601 | flags = IPN_UDP; |
1602 | } else if (oip6->ip6_nxt == IPPROTO_ICMPV6) |
1603 | flags = IPN_ICMPQUERY; |
1604 | dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip); |
1605 | |
1606 | /* |
1607 | * Need to adjust ICMP header to include the real IP#'s and |
1608 | * port #'s. Only apply a checksum change relative to the |
1609 | * IP address change as it will be modified again in ipf_nat6_checkout |
1610 | * for both address and port. Two checksum changes are |
1611 | * necessary for the two header address changes. Be careful |
1612 | * to only modify the checksum once for the port # and twice |
1613 | * for the IP#. |
1614 | */ |
1615 | |
1616 | /* |
1617 | * Step 1 |
1618 | * Fix the IP addresses in the offending IP packet. You also need |
1619 | * to adjust the IP header checksum of that offending IP packet. |
1620 | * |
1621 | * Normally, you would expect that the ICMP checksum of the |
1622 | * ICMP error message needs to be adjusted as well for the |
1623 | * IP address change in oip. |
1624 | * However, this is a NOP, because the ICMP checksum is |
1625 | * calculated over the complete ICMP packet, which includes the |
1626 | * changed oip IP addresses and oip6->ip6_sum. However, these |
1627 | * two changes cancel each other out (if the delta for |
1628 | * the IP address is x, then the delta for ip_sum is minus x), |
1629 | * so no change in the icmp_cksum is necessary. |
1630 | * |
1631 | * Inbound ICMP |
1632 | * ------------ |
1633 | * MAP rule, SRC=a,DST=b -> SRC=c,DST=b |
1634 | * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b) |
1635 | * - OIP_SRC(c)=nat6_newsrcip, OIP_DST(b)=nat6_newdstip |
1636 | *=> OIP_SRC(c)=nat6_oldsrcip, OIP_DST(b)=nat6_olddstip |
1637 | * |
1638 | * RDR rule, SRC=a,DST=b -> SRC=a,DST=c |
1639 | * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a) |
1640 | * - OIP_SRC(b)=nat6_olddstip, OIP_DST(a)=nat6_oldsrcip |
1641 | *=> OIP_SRC(b)=nat6_newdstip, OIP_DST(a)=nat6_newsrcip |
1642 | * |
1643 | * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d |
1644 | * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d) |
1645 | * - OIP_SRC(c)=nat6_newsrcip, OIP_DST(d)=nat6_newdstip |
1646 | *=> OIP_SRC(c)=nat6_oldsrcip, OIP_DST(d)=nat6_olddstip |
1647 | * |
1648 | * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d |
1649 | * - response to outgoing packet (d,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) |
1650 | * - OIP_SRC(b)=nat6_olddstip, OIP_DST(a)=nat6_oldsrcip |
1651 | *=> OIP_SRC(b)=nat6_newdstip, OIP_DST(a)=nat6_newsrcip |
1652 | * |
1653 | * Outbound ICMP |
1654 | * ------------- |
1655 | * MAP rule, SRC=a,DST=b -> SRC=c,DST=b |
1656 | * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) |
1657 | * - OIP_SRC(b)=nat6_olddstip, OIP_DST(a)=nat6_oldsrcip |
1658 | *=> OIP_SRC(b)=nat6_newdstip, OIP_DST(a)=nat6_newsrcip |
1659 | * |
1660 | * RDR rule, SRC=a,DST=b -> SRC=a,DST=c |
1661 | * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c) |
1662 | * - OIP_SRC(a)=nat6_newsrcip, OIP_DST(c)=nat6_newdstip |
1663 | *=> OIP_SRC(a)=nat6_oldsrcip, OIP_DST(c)=nat6_olddstip |
1664 | * |
1665 | * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d |
1666 | * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d) |
1667 | * - OIP_SRC(c)=nat6_olddstip, OIP_DST(d)=nat6_oldsrcip |
1668 | *=> OIP_SRC(b)=nat6_newdstip, OIP_DST(a)=nat6_newsrcip |
1669 | * |
1670 | * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d |
1671 | * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a) |
1672 | * - OIP_SRC(b)=nat6_newsrcip, OIP_DST(a)=nat6_newdstip |
1673 | *=> OIP_SRC(a)=nat6_oldsrcip, OIP_DST(c)=nat6_olddstip |
1674 | */ |
1675 | |
1676 | if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) || |
1677 | ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) { |
1678 | a1 = nat->nat_osrc6; |
1679 | a4.in6 = oip6->ip6_src; |
1680 | a3 = nat->nat_odst6; |
1681 | a2.in6 = oip6->ip6_dst; |
1682 | oip6->ip6_src = a1.in6; |
1683 | oip6->ip6_dst = a3.in6; |
1684 | odst = 1; |
1685 | } else { |
1686 | a1 = nat->nat_ndst6; |
1687 | a2.in6 = oip6->ip6_dst; |
1688 | a3 = nat->nat_nsrc6; |
1689 | a4.in6 = oip6->ip6_src; |
1690 | oip6->ip6_dst = a3.in6; |
1691 | oip6->ip6_src = a1.in6; |
1692 | odst = 0; |
1693 | } |
1694 | |
1695 | sumd = 0; |
1696 | if (IP6_NEQ(&a3, &a2) || IP6_NEQ(&a1, &a4)) { |
1697 | if (IP6_GT(&a3, &a2)) { |
1698 | sumd = ipf_nat6_ip6subtract(&a2, &a3); |
1699 | sumd--; |
1700 | } else { |
1701 | sumd = ipf_nat6_ip6subtract(&a2, &a3); |
1702 | } |
1703 | if (IP6_GT(&a1, &a4)) { |
1704 | sumd += ipf_nat6_ip6subtract(&a4, &a1); |
1705 | sumd--; |
1706 | } else { |
1707 | sumd += ipf_nat6_ip6subtract(&a4, &a1); |
1708 | } |
1709 | sumd = ~sumd; |
1710 | } |
1711 | |
1712 | sumd2 = sumd; |
1713 | sum1 = 0; |
1714 | sum2 = 0; |
1715 | |
1716 | /* |
1717 | * Fix UDP pseudo header checksum to compensate for the |
1718 | * IP address change. |
1719 | */ |
1720 | if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) { |
1721 | u_32_t sum3, sum4; |
1722 | /* |
1723 | * Step 2 : |
1724 | * For offending TCP/UDP IP packets, translate the ports as |
1725 | * well, based on the NAT specification. Of course such |
1726 | * a change may be reflected in the ICMP checksum as well. |
1727 | * |
1728 | * Since the port fields are part of the TCP/UDP checksum |
1729 | * of the offending IP packet, you need to adjust that checksum |
1730 | * as well... except that the change in the port numbers should |
1731 | * be offset by the checksum change. However, the TCP/UDP |
1732 | * checksum will also need to change if there has been an |
1733 | * IP address change. |
1734 | */ |
1735 | if (odst == 1) { |
1736 | sum1 = ntohs(nat->nat_osport); |
1737 | sum4 = ntohs(tcp->th_sport); |
1738 | sum3 = ntohs(nat->nat_odport); |
1739 | sum2 = ntohs(tcp->th_dport); |
1740 | |
1741 | tcp->th_sport = htons(sum1); |
1742 | tcp->th_dport = htons(sum3); |
1743 | } else { |
1744 | sum1 = ntohs(nat->nat_ndport); |
1745 | sum2 = ntohs(tcp->th_dport); |
1746 | sum3 = ntohs(nat->nat_nsport); |
1747 | sum4 = ntohs(tcp->th_sport); |
1748 | |
1749 | tcp->th_dport = htons(sum3); |
1750 | tcp->th_sport = htons(sum1); |
1751 | } |
1752 | sumd += sum1 - sum4; |
1753 | sumd += sum3 - sum2; |
1754 | |
1755 | if (sumd != 0 || sumd2 != 0) { |
1756 | /* |
1757 | * At this point, sumd is the delta to apply to the |
1758 | * TCP/UDP header, given the changes in both the IP |
1759 | * address and the ports and sumd2 is the delta to |
1760 | * apply to the ICMP header, given the IP address |
1761 | * change delta that may need to be applied to the |
1762 | * TCP/UDP checksum instead. |
1763 | * |
1764 | * If we will both the IP and TCP/UDP checksums |
1765 | * then the ICMP checksum changes by the address |
1766 | * delta applied to the TCP/UDP checksum. If we |
1767 | * do not change the TCP/UDP checksum them we |
1768 | * apply the delta in ports to the ICMP checksum. |
1769 | */ |
1770 | if (oip6->ip6_nxt == IPPROTO_UDP) { |
1771 | if ((dlen >= 8) && (*csump != 0)) { |
1772 | ipf_fix_datacksum(csump, sumd); |
1773 | } else { |
1774 | sumd2 = sum4 - sum1; |
1775 | if (sum1 > sum4) |
1776 | sumd2--; |
1777 | sumd2 += sum2 - sum3; |
1778 | if (sum3 > sum2) |
1779 | sumd2--; |
1780 | } |
1781 | } else if (oip6->ip6_nxt == IPPROTO_TCP) { |
1782 | if (dlen >= 18) { |
1783 | ipf_fix_datacksum(csump, sumd); |
1784 | } else { |
1785 | sumd2 = sum4 - sum1; |
1786 | if (sum1 > sum4) |
1787 | sumd2--; |
1788 | sumd2 += sum2 - sum3; |
1789 | if (sum3 > sum2) |
1790 | sumd2--; |
1791 | } |
1792 | } |
1793 | if (sumd2 != 0) { |
1794 | sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); |
1795 | sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); |
1796 | sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); |
1797 | ipf_fix_incksum(0, &icmp6->icmp6_cksum, |
1798 | sumd2, 0); |
1799 | } |
1800 | } |
1801 | } else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) { |
1802 | struct icmp6_hdr *orgicmp; |
1803 | |
1804 | /* |
1805 | * XXX - what if this is bogus hl and we go off the end ? |
1806 | * In this case, ipf_nat6_icmperrorlookup() will have |
1807 | * returned NULL. |
1808 | */ |
1809 | orgicmp = (struct icmp6_hdr *)dp; |
1810 | |
1811 | if (odst == 1) { |
1812 | if (orgicmp->icmp6_id != nat->nat_osport) { |
1813 | |
1814 | /* |
1815 | * Fix ICMP checksum (of the offening ICMP |
1816 | * query packet) to compensate the change |
1817 | * in the ICMP id of the offending ICMP |
1818 | * packet. |
1819 | * |
1820 | * Since you modify orgicmp->icmp6_id with |
1821 | * a delta (say x) and you compensate that |
1822 | * in origicmp->icmp6_cksum with a delta |
1823 | * minus x, you don't have to adjust the |
1824 | * overall icmp->icmp6_cksum |
1825 | */ |
1826 | sum1 = ntohs(orgicmp->icmp6_id); |
1827 | sum2 = ntohs(nat->nat_osport); |
1828 | CALC_SUMD(sum1, sum2, sumd); |
1829 | orgicmp->icmp6_id = nat->nat_oicmpid; |
1830 | ipf_fix_datacksum(&orgicmp->icmp6_cksum, sumd); |
1831 | } |
1832 | } /* nat6_dir == NAT_INBOUND is impossible for icmp queries */ |
1833 | } |
1834 | return nat; |
1835 | } |
1836 | |
1837 | |
1838 | /* |
1839 | * MAP-IN MAP-OUT RDR-IN RDR-OUT |
1840 | * osrc X == src == src X |
1841 | * odst X == dst == dst X |
1842 | * nsrc == dst X X == dst |
1843 | * ndst == src X X == src |
1844 | * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND |
1845 | */ |
1846 | /* |
1847 | * NB: these lookups don't lock access to the list, it assumed that it has |
1848 | * already been done! |
1849 | */ |
1850 | /* ------------------------------------------------------------------------ */ |
1851 | /* Function: ipf_nat6_inlookup */ |
1852 | /* Returns: nat6_t* - NULL == no match, */ |
1853 | /* else pointer to matching NAT entry */ |
1854 | /* Parameters: fin(I) - pointer to packet information */ |
1855 | /* flags(I) - NAT flags for this packet */ |
1856 | /* p(I) - protocol for this packet */ |
1857 | /* src(I) - source IP address */ |
1858 | /* mapdst(I) - destination IP address */ |
1859 | /* */ |
1860 | /* Lookup a nat entry based on the mapped destination ip address/port and */ |
1861 | /* real source address/port. We use this lookup when receiving a packet, */ |
1862 | /* we're looking for a table entry, based on the destination address. */ |
1863 | /* */ |
1864 | /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ |
1865 | /* */ |
1866 | /* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ |
1867 | /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ |
1868 | /* */ |
1869 | /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ |
1870 | /* the packet is of said protocol */ |
1871 | /* ------------------------------------------------------------------------ */ |
1872 | nat_t * |
1873 | ipf_nat6_inlookup(fr_info_t *fin, u_int flags, u_int p, struct in6_addr *src, |
1874 | struct in6_addr *mapdst) |
1875 | { |
1876 | ipf_main_softc_t *softc = fin->fin_main_soft; |
1877 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
1878 | u_short sport, dport; |
1879 | nat_t *nat; |
1880 | int nflags; |
1881 | i6addr_t dst; |
1882 | void *ifp; |
1883 | u_int hv; |
1884 | |
1885 | ifp = fin->fin_ifp; |
1886 | sport = 0; |
1887 | dport = 0; |
1888 | dst.in6 = *mapdst; |
1889 | |
1890 | switch (p) |
1891 | { |
1892 | case IPPROTO_TCP : |
1893 | case IPPROTO_UDP : |
1894 | sport = htons(fin->fin_data[0]); |
1895 | dport = htons(fin->fin_data[1]); |
1896 | break; |
1897 | case IPPROTO_ICMPV6 : |
1898 | if (flags & IPN_ICMPERR) |
1899 | sport = fin->fin_data[1]; |
1900 | else |
1901 | dport = fin->fin_data[1]; |
1902 | break; |
1903 | default : |
1904 | break; |
1905 | } |
1906 | |
1907 | |
1908 | if ((flags & SI_WILDP) != 0) |
1909 | goto find_in_wild_ports; |
1910 | |
1911 | hv = NAT_HASH_FN6(&dst, dport, 0xffffffff); |
1912 | hv = NAT_HASH_FN6(src, hv + sport, softn->ipf_nat_table_sz); |
1913 | nat = softn->ipf_nat_table[1][hv]; |
1914 | /* TRACE dst, dport, src, sport, hv, nat */ |
1915 | |
1916 | for (; nat; nat = nat->nat_hnext[1]) { |
1917 | if (nat->nat_ifps[0] != NULL) { |
1918 | if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) |
1919 | continue; |
1920 | } |
1921 | |
1922 | if (nat->nat_pr[0] != p) |
1923 | continue; |
1924 | |
1925 | switch (nat->nat_dir) |
1926 | { |
1927 | case NAT_INBOUND : |
1928 | if (nat->nat_v[0] != 6) |
1929 | continue; |
1930 | if (IP6_NEQ(&nat->nat_osrc6, src) || |
1931 | IP6_NEQ(&nat->nat_odst6, &dst)) |
1932 | continue; |
1933 | if ((nat->nat_flags & IPN_TCPUDP) != 0) { |
1934 | if (nat->nat_osport != sport) |
1935 | continue; |
1936 | if (nat->nat_odport != dport) |
1937 | continue; |
1938 | |
1939 | } else if (p == IPPROTO_ICMPV6) { |
1940 | if (nat->nat_osport != dport) { |
1941 | continue; |
1942 | } |
1943 | } |
1944 | break; |
1945 | case NAT_OUTBOUND : |
1946 | if (nat->nat_v[1] != 6) |
1947 | continue; |
1948 | if (IP6_NEQ(&nat->nat_ndst6, src) || |
1949 | IP6_NEQ(&nat->nat_nsrc6, &dst)) |
1950 | continue; |
1951 | if ((nat->nat_flags & IPN_TCPUDP) != 0) { |
1952 | if (nat->nat_ndport != sport) |
1953 | continue; |
1954 | if (nat->nat_nsport != dport) |
1955 | continue; |
1956 | |
1957 | } else if (p == IPPROTO_ICMPV6) { |
1958 | if (nat->nat_osport != dport) { |
1959 | continue; |
1960 | } |
1961 | } |
1962 | break; |
1963 | } |
1964 | |
1965 | |
1966 | if ((nat->nat_flags & IPN_TCPUDP) != 0) { |
1967 | #ifdef IPF_V6_PROXIES |
1968 | if ((nat->nat_ptr != NULL) && (nat->nat_aps != NULL)) |
1969 | if (appr_match(fin, nat) != 0) |
1970 | continue; |
1971 | #endif |
1972 | } |
1973 | if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { |
1974 | nat->nat_ifps[0] = ifp; |
1975 | nat->nat_mtu[0] = GETIFMTU_6(ifp); |
1976 | } |
1977 | return nat; |
1978 | } |
1979 | |
1980 | /* |
1981 | * So if we didn't find it but there are wildcard members in the hash |
1982 | * table, go back and look for them. We do this search and update here |
1983 | * because it is modifying the NAT table and we want to do this only |
1984 | * for the first packet that matches. The exception, of course, is |
1985 | * for "dummy" (FI_IGNORE) lookups. |
1986 | */ |
1987 | find_in_wild_ports: |
1988 | if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { |
1989 | NBUMPSIDE6DX(0, ns_lookup_miss, ns_lookup_miss_1); |
1990 | return NULL; |
1991 | } |
1992 | if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { |
1993 | NBUMPSIDE6D(0, ns_lookup_nowild); |
1994 | return NULL; |
1995 | } |
1996 | |
1997 | RWLOCK_EXIT(&softc->ipf_nat); |
1998 | |
1999 | hv = NAT_HASH_FN6(&dst, 0, 0xffffffff); |
2000 | hv = NAT_HASH_FN6(src, hv, softn->ipf_nat_table_sz); |
2001 | WRITE_ENTER(&softc->ipf_nat); |
2002 | |
2003 | nat = softn->ipf_nat_table[1][hv]; |
2004 | /* TRACE dst, src, hv, nat */ |
2005 | for (; nat; nat = nat->nat_hnext[1]) { |
2006 | if (nat->nat_ifps[0] != NULL) { |
2007 | if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) |
2008 | continue; |
2009 | } |
2010 | |
2011 | if (nat->nat_pr[0] != fin->fin_p) |
2012 | continue; |
2013 | |
2014 | switch (nat->nat_dir) |
2015 | { |
2016 | case NAT_INBOUND : |
2017 | if (nat->nat_v[0] != 6) |
2018 | continue; |
2019 | if (IP6_NEQ(&nat->nat_osrc6, src) || |
2020 | IP6_NEQ(&nat->nat_odst6, &dst)) |
2021 | continue; |
2022 | break; |
2023 | case NAT_OUTBOUND : |
2024 | if (nat->nat_v[1] != 6) |
2025 | continue; |
2026 | if (IP6_NEQ(&nat->nat_ndst6, src) || |
2027 | IP6_NEQ(&nat->nat_nsrc6, &dst)) |
2028 | continue; |
2029 | break; |
2030 | } |
2031 | |
2032 | nflags = nat->nat_flags; |
2033 | if (!(nflags & (NAT_TCPUDP|SI_WILDP))) |
2034 | continue; |
2035 | |
2036 | if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags, |
2037 | NAT_INBOUND) == 1) { |
2038 | if ((fin->fin_flx & FI_IGNORE) != 0) |
2039 | break; |
2040 | if ((nflags & SI_CLONE) != 0) { |
2041 | nat = ipf_nat_clone(fin, nat); |
2042 | if (nat == NULL) |
2043 | break; |
2044 | } else { |
2045 | MUTEX_ENTER(&softn->ipf_nat_new); |
2046 | softn->ipf_nat_stats.ns_wilds--; |
2047 | MUTEX_EXIT(&softn->ipf_nat_new); |
2048 | } |
2049 | |
2050 | if (nat->nat_dir == NAT_INBOUND) { |
2051 | if (nat->nat_osport == 0) { |
2052 | nat->nat_osport = sport; |
2053 | nat->nat_nsport = sport; |
2054 | } |
2055 | if (nat->nat_odport == 0) { |
2056 | nat->nat_odport = dport; |
2057 | nat->nat_ndport = dport; |
2058 | } |
2059 | } else { |
2060 | if (nat->nat_osport == 0) { |
2061 | nat->nat_osport = dport; |
2062 | nat->nat_nsport = dport; |
2063 | } |
2064 | if (nat->nat_odport == 0) { |
2065 | nat->nat_odport = sport; |
2066 | nat->nat_ndport = sport; |
2067 | } |
2068 | } |
2069 | if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { |
2070 | nat->nat_ifps[0] = ifp; |
2071 | nat->nat_mtu[0] = GETIFMTU_6(ifp); |
2072 | } |
2073 | nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); |
2074 | ipf_nat6_tabmove(softn, nat); |
2075 | break; |
2076 | } |
2077 | } |
2078 | |
2079 | MUTEX_DOWNGRADE(&softc->ipf_nat); |
2080 | |
2081 | if (nat == NULL) { |
2082 | NBUMPSIDE6DX(0, ns_lookup_miss, ns_lookup_miss_2); |
2083 | } |
2084 | return nat; |
2085 | } |
2086 | |
2087 | |
2088 | /* ------------------------------------------------------------------------ */ |
2089 | /* Function: ipf_nat6_tabmove */ |
2090 | /* Returns: Nil */ |
2091 | /* Parameters: nat(I) - pointer to NAT structure */ |
2092 | /* Write Lock: ipf_nat */ |
2093 | /* */ |
2094 | /* This function is only called for TCP/UDP NAT table entries where the */ |
2095 | /* original was placed in the table without hashing on the ports and we now */ |
2096 | /* want to include hashing on port numbers. */ |
2097 | /* ------------------------------------------------------------------------ */ |
2098 | static void |
2099 | ipf_nat6_tabmove(ipf_nat_softc_t *softn, nat_t *nat) |
2100 | { |
2101 | u_int rhv0, rhv1, hv0, hv1; |
2102 | nat_t **natp; |
2103 | |
2104 | if (nat->nat_flags & SI_CLONE) |
2105 | return; |
2106 | |
2107 | /* |
2108 | * Remove the NAT entry from the old location |
2109 | */ |
2110 | if (nat->nat_hnext[0]) |
2111 | nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; |
2112 | *nat->nat_phnext[0] = nat->nat_hnext[0]; |
2113 | softn->ipf_nat_stats.ns_side[0].ns_bucketlen[nat->nat_hv[0]]--; |
2114 | |
2115 | if (nat->nat_hnext[1]) |
2116 | nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; |
2117 | *nat->nat_phnext[1] = nat->nat_hnext[1]; |
2118 | softn->ipf_nat_stats.ns_side[1].ns_bucketlen[nat->nat_hv[1]]--; |
2119 | |
2120 | /* |
2121 | * Add into the NAT table in the new position |
2122 | */ |
2123 | rhv0 = NAT_HASH_FN6(&nat->nat_osrc6, nat->nat_osport, 0xffffffff); |
2124 | rhv0 = NAT_HASH_FN6(&nat->nat_odst6, rhv0 + nat->nat_odport, |
2125 | softn->ipf_nat_table_sz); |
2126 | rhv1 = NAT_HASH_FN6(&nat->nat_nsrc6, nat->nat_nsport, 0xffffffff); |
2127 | rhv1 = NAT_HASH_FN6(&nat->nat_ndst6, rhv1 + nat->nat_ndport, |
2128 | softn->ipf_nat_table_sz); |
2129 | |
2130 | if ((nat->nat_dir & NAT_OUTBOUND) == NAT_OUTBOUND) { |
2131 | nat->nat_hv[0] = rhv0; |
2132 | nat->nat_hv[1] = rhv1; |
2133 | } else { |
2134 | nat->nat_hv[0] = rhv1; |
2135 | nat->nat_hv[1] = rhv0; |
2136 | } |
2137 | |
2138 | hv0 = nat->nat_hv[0] % softn->ipf_nat_table_sz; |
2139 | hv1 = nat->nat_hv[1] % softn->ipf_nat_table_sz; |
2140 | |
2141 | /* TRACE nat_osrc6, nat_osport, nat_odst6, nat_odport, hv0 */ |
2142 | /* TRACE nat_nsrc6, nat_nsport, nat_ndst6, nat_ndport, hv1 */ |
2143 | |
2144 | natp = &softn->ipf_nat_table[0][hv0]; |
2145 | if (*natp) |
2146 | (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; |
2147 | nat->nat_phnext[0] = natp; |
2148 | nat->nat_hnext[0] = *natp; |
2149 | *natp = nat; |
2150 | softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]++; |
2151 | |
2152 | natp = &softn->ipf_nat_table[1][hv1]; |
2153 | if (*natp) |
2154 | (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; |
2155 | nat->nat_phnext[1] = natp; |
2156 | nat->nat_hnext[1] = *natp; |
2157 | *natp = nat; |
2158 | softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]++; |
2159 | } |
2160 | |
2161 | |
2162 | /* ------------------------------------------------------------------------ */ |
2163 | /* Function: ipf_nat6_outlookup */ |
2164 | /* Returns: nat6_t* - NULL == no match, */ |
2165 | /* else pointer to matching NAT entry */ |
2166 | /* Parameters: fin(I) - pointer to packet information */ |
2167 | /* flags(I) - NAT flags for this packet */ |
2168 | /* p(I) - protocol for this packet */ |
2169 | /* src(I) - source IP address */ |
2170 | /* dst(I) - destination IP address */ |
2171 | /* rw(I) - 1 == write lock on held, 0 == read lock. */ |
2172 | /* */ |
2173 | /* Lookup a nat entry based on the source 'real' ip address/port and */ |
2174 | /* destination address/port. We use this lookup when sending a packet out, */ |
2175 | /* we're looking for a table entry, based on the source address. */ |
2176 | /* */ |
2177 | /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ |
2178 | /* */ |
2179 | /* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ |
2180 | /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ |
2181 | /* */ |
2182 | /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ |
2183 | /* the packet is of said protocol */ |
2184 | /* ------------------------------------------------------------------------ */ |
2185 | nat_t * |
2186 | ipf_nat6_outlookup(fr_info_t *fin, u_int flags, u_int p, struct in6_addr *src, |
2187 | struct in6_addr *dst) |
2188 | { |
2189 | ipf_main_softc_t *softc = fin->fin_main_soft; |
2190 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
2191 | u_short sport, dport; |
2192 | nat_t *nat; |
2193 | void *ifp; |
2194 | u_int hv; |
2195 | |
2196 | ifp = fin->fin_ifp; |
2197 | sport = 0; |
2198 | dport = 0; |
2199 | |
2200 | switch (p) |
2201 | { |
2202 | case IPPROTO_TCP : |
2203 | case IPPROTO_UDP : |
2204 | sport = htons(fin->fin_data[0]); |
2205 | dport = htons(fin->fin_data[1]); |
2206 | break; |
2207 | case IPPROTO_ICMPV6 : |
2208 | if (flags & IPN_ICMPERR) |
2209 | sport = fin->fin_data[1]; |
2210 | else |
2211 | dport = fin->fin_data[1]; |
2212 | break; |
2213 | default : |
2214 | break; |
2215 | } |
2216 | |
2217 | if ((flags & SI_WILDP) != 0) |
2218 | goto find_out_wild_ports; |
2219 | |
2220 | hv = NAT_HASH_FN6(src, sport, 0xffffffff); |
2221 | hv = NAT_HASH_FN6(dst, hv + dport, softn->ipf_nat_table_sz); |
2222 | nat = softn->ipf_nat_table[0][hv]; |
2223 | |
2224 | /* TRACE src, sport, dst, dport, hv, nat */ |
2225 | |
2226 | for (; nat; nat = nat->nat_hnext[0]) { |
2227 | if (nat->nat_ifps[1] != NULL) { |
2228 | if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) |
2229 | continue; |
2230 | } |
2231 | |
2232 | if (nat->nat_pr[1] != p) |
2233 | continue; |
2234 | |
2235 | switch (nat->nat_dir) |
2236 | { |
2237 | case NAT_INBOUND : |
2238 | if (nat->nat_v[1] != 6) |
2239 | continue; |
2240 | if (IP6_NEQ(&nat->nat_ndst6, src) || |
2241 | IP6_NEQ(&nat->nat_nsrc6, dst)) |
2242 | continue; |
2243 | |
2244 | if ((nat->nat_flags & IPN_TCPUDP) != 0) { |
2245 | if (nat->nat_ndport != sport) |
2246 | continue; |
2247 | if (nat->nat_nsport != dport) |
2248 | continue; |
2249 | |
2250 | } else if (p == IPPROTO_ICMPV6) { |
2251 | if (nat->nat_osport != dport) { |
2252 | continue; |
2253 | } |
2254 | } |
2255 | break; |
2256 | case NAT_OUTBOUND : |
2257 | if (nat->nat_v[0] != 6) |
2258 | continue; |
2259 | if (IP6_NEQ(&nat->nat_osrc6, src) || |
2260 | IP6_NEQ(&nat->nat_odst6, dst)) |
2261 | continue; |
2262 | |
2263 | if ((nat->nat_flags & IPN_TCPUDP) != 0) { |
2264 | if (nat->nat_odport != dport) |
2265 | continue; |
2266 | if (nat->nat_osport != sport) |
2267 | continue; |
2268 | |
2269 | } else if (p == IPPROTO_ICMPV6) { |
2270 | if (nat->nat_osport != dport) { |
2271 | continue; |
2272 | } |
2273 | } |
2274 | break; |
2275 | } |
2276 | |
2277 | #ifdef IPF_V6_PROXIES |
2278 | if ((nat->nat_ptr != NULL) && (nat->nat_aps != NULL)) |
2279 | if (appr_match(fin, nat) != 0) |
2280 | continue; |
2281 | #endif |
2282 | |
2283 | if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { |
2284 | nat->nat_ifps[1] = ifp; |
2285 | nat->nat_mtu[1] = GETIFMTU_6(ifp); |
2286 | } |
2287 | return nat; |
2288 | } |
2289 | |
2290 | /* |
2291 | * So if we didn't find it but there are wildcard members in the hash |
2292 | * table, go back and look for them. We do this search and update here |
2293 | * because it is modifying the NAT table and we want to do this only |
2294 | * for the first packet that matches. The exception, of course, is |
2295 | * for "dummy" (FI_IGNORE) lookups. |
2296 | */ |
2297 | find_out_wild_ports: |
2298 | if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { |
2299 | NBUMPSIDE6DX(1, ns_lookup_miss, ns_lookup_miss_3); |
2300 | return NULL; |
2301 | } |
2302 | if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { |
2303 | NBUMPSIDE6D(1, ns_lookup_nowild); |
2304 | return NULL; |
2305 | } |
2306 | |
2307 | RWLOCK_EXIT(&softc->ipf_nat); |
2308 | |
2309 | hv = NAT_HASH_FN6(src, 0, 0xffffffff); |
2310 | hv = NAT_HASH_FN6(dst, hv, softn->ipf_nat_table_sz); |
2311 | |
2312 | WRITE_ENTER(&softc->ipf_nat); |
2313 | |
2314 | nat = softn->ipf_nat_table[0][hv]; |
2315 | for (; nat; nat = nat->nat_hnext[0]) { |
2316 | if (nat->nat_ifps[1] != NULL) { |
2317 | if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) |
2318 | continue; |
2319 | } |
2320 | |
2321 | if (nat->nat_pr[1] != fin->fin_p) |
2322 | continue; |
2323 | |
2324 | switch (nat->nat_dir) |
2325 | { |
2326 | case NAT_INBOUND : |
2327 | if (nat->nat_v[1] != 6) |
2328 | continue; |
2329 | if (IP6_NEQ(&nat->nat_ndst6, src) || |
2330 | IP6_NEQ(&nat->nat_nsrc6, dst)) |
2331 | continue; |
2332 | break; |
2333 | case NAT_OUTBOUND : |
2334 | if (nat->nat_v[0] != 6) |
2335 | continue; |
2336 | if (IP6_NEQ(&nat->nat_osrc6, src) || |
2337 | IP6_NEQ(&nat->nat_odst6, dst)) |
2338 | continue; |
2339 | break; |
2340 | } |
2341 | |
2342 | if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP))) |
2343 | continue; |
2344 | |
2345 | if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags, |
2346 | NAT_OUTBOUND) == 1) { |
2347 | if ((fin->fin_flx & FI_IGNORE) != 0) |
2348 | break; |
2349 | if ((nat->nat_flags & SI_CLONE) != 0) { |
2350 | nat = ipf_nat_clone(fin, nat); |
2351 | if (nat == NULL) |
2352 | break; |
2353 | } else { |
2354 | MUTEX_ENTER(&softn->ipf_nat_new); |
2355 | softn->ipf_nat_stats.ns_wilds--; |
2356 | MUTEX_EXIT(&softn->ipf_nat_new); |
2357 | } |
2358 | |
2359 | if (nat->nat_dir == NAT_OUTBOUND) { |
2360 | if (nat->nat_osport == 0) { |
2361 | nat->nat_osport = sport; |
2362 | nat->nat_nsport = sport; |
2363 | } |
2364 | if (nat->nat_odport == 0) { |
2365 | nat->nat_odport = dport; |
2366 | nat->nat_ndport = dport; |
2367 | } |
2368 | } else { |
2369 | if (nat->nat_osport == 0) { |
2370 | nat->nat_osport = dport; |
2371 | nat->nat_nsport = dport; |
2372 | } |
2373 | if (nat->nat_odport == 0) { |
2374 | nat->nat_odport = sport; |
2375 | nat->nat_ndport = sport; |
2376 | } |
2377 | } |
2378 | if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { |
2379 | nat->nat_ifps[1] = ifp; |
2380 | nat->nat_mtu[1] = GETIFMTU_6(ifp); |
2381 | } |
2382 | nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); |
2383 | ipf_nat6_tabmove(softn, nat); |
2384 | break; |
2385 | } |
2386 | } |
2387 | |
2388 | MUTEX_DOWNGRADE(&softc->ipf_nat); |
2389 | |
2390 | if (nat == NULL) { |
2391 | NBUMPSIDE6DX(1, ns_lookup_miss, ns_lookup_miss_4); |
2392 | } |
2393 | return nat; |
2394 | } |
2395 | |
2396 | |
2397 | /* ------------------------------------------------------------------------ */ |
2398 | /* Function: ipf_nat6_lookupredir */ |
2399 | /* Returns: nat6_t* - NULL == no match, */ |
2400 | /* else pointer to matching NAT entry */ |
2401 | /* Parameters: softc(I) - pointer to soft context main structure */ |
2402 | /* np(I) - pointer to description of packet to find NAT */ |
2403 | /* table entry for. */ |
2404 | /* */ |
2405 | /* Lookup the NAT tables to search for a matching redirect */ |
2406 | /* The contents of natlookup_t should imitate those found in a packet that */ |
2407 | /* would be translated - ie a packet coming in for RDR or going out for MAP.*/ |
2408 | /* We can do the lookup in one of two ways, imitating an inbound or */ |
2409 | /* outbound packet. By default we assume outbound, unless IPN_IN is set. */ |
2410 | /* For IN, the fields are set as follows: */ |
2411 | /* nl_real* = source information */ |
2412 | /* nl_out* = destination information (translated) */ |
2413 | /* For an out packet, the fields are set like this: */ |
2414 | /* nl_in* = source information (untranslated) */ |
2415 | /* nl_out* = destination information (translated) */ |
2416 | /* ------------------------------------------------------------------------ */ |
2417 | nat_t * |
2418 | ipf_nat6_lookupredir(ipf_main_softc_t *softc, natlookup_t *np) |
2419 | { |
2420 | fr_info_t fi; |
2421 | nat_t *nat; |
2422 | |
2423 | bzero((char *)&fi, sizeof(fi)); |
2424 | fi.fin_main_soft = softc; |
2425 | if (np->nl_flags & IPN_IN) { |
2426 | fi.fin_data[0] = ntohs(np->nl_realport); |
2427 | fi.fin_data[1] = ntohs(np->nl_outport); |
2428 | } else { |
2429 | fi.fin_data[0] = ntohs(np->nl_inport); |
2430 | fi.fin_data[1] = ntohs(np->nl_outport); |
2431 | } |
2432 | if (np->nl_flags & IPN_TCP) |
2433 | fi.fin_p = IPPROTO_TCP; |
2434 | else if (np->nl_flags & IPN_UDP) |
2435 | fi.fin_p = IPPROTO_UDP; |
2436 | else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) |
2437 | fi.fin_p = IPPROTO_ICMPV6; |
2438 | |
2439 | /* |
2440 | * We can do two sorts of lookups: |
2441 | * - IPN_IN: we have the `real' and `out' address, look for `in'. |
2442 | * - default: we have the `in' and `out' address, look for `real'. |
2443 | */ |
2444 | if (np->nl_flags & IPN_IN) { |
2445 | if ((nat = ipf_nat6_inlookup(&fi, np->nl_flags, fi.fin_p, |
2446 | &np->nl_realip6, |
2447 | &np->nl_outip6))) { |
2448 | np->nl_inip6 = nat->nat_odst6.in6; |
2449 | np->nl_inport = nat->nat_odport; |
2450 | } |
2451 | } else { |
2452 | /* |
2453 | * If nl_inip is non null, this is a lookup based on the real |
2454 | * ip address. Else, we use the fake. |
2455 | */ |
2456 | if ((nat = ipf_nat6_outlookup(&fi, np->nl_flags, fi.fin_p, |
2457 | &np->nl_inip6, &np->nl_outip6))) { |
2458 | |
2459 | if ((np->nl_flags & IPN_FINDFORWARD) != 0) { |
2460 | fr_info_t fin; |
2461 | bzero((char *)&fin, sizeof(fin)); |
2462 | fin.fin_p = nat->nat_pr[0]; |
2463 | fin.fin_data[0] = ntohs(nat->nat_ndport); |
2464 | fin.fin_data[1] = ntohs(nat->nat_nsport); |
2465 | if (ipf_nat6_inlookup(&fin, np->nl_flags, |
2466 | fin.fin_p, |
2467 | &nat->nat_ndst6.in6, |
2468 | &nat->nat_nsrc6.in6) != |
2469 | NULL) { |
2470 | np->nl_flags &= ~IPN_FINDFORWARD; |
2471 | } |
2472 | } |
2473 | |
2474 | np->nl_realip6 = nat->nat_odst6.in6; |
2475 | np->nl_realport = nat->nat_odport; |
2476 | } |
2477 | } |
2478 | |
2479 | return nat; |
2480 | } |
2481 | |
2482 | |
2483 | /* ------------------------------------------------------------------------ */ |
2484 | /* Function: ipf_nat6_match */ |
2485 | /* Returns: int - 0 == no match, 1 == match */ |
2486 | /* Parameters: fin(I) - pointer to packet information */ |
2487 | /* np(I) - pointer to NAT rule */ |
2488 | /* */ |
2489 | /* Pull the matching of a packet against a NAT rule out of that complex */ |
2490 | /* loop inside ipf_nat6_checkin() and lay it out properly in its own */ |
2491 | /* function. */ |
2492 | /* ------------------------------------------------------------------------ */ |
2493 | static int |
2494 | ipf_nat6_match(fr_info_t *fin, ipnat_t *np) |
2495 | { |
2496 | frtuc_t *ft; |
2497 | int match; |
2498 | |
2499 | match = 0; |
2500 | switch (np->in_osrcatype) |
2501 | { |
2502 | case FRI_NORMAL : |
2503 | match = IP6_MASKNEQ(&fin->fin_src6, &np->in_osrcmsk6, |
2504 | &np->in_osrcip6); |
2505 | break; |
2506 | case FRI_LOOKUP : |
2507 | match = (*np->in_osrcfunc)(fin->fin_main_soft, np->in_osrcptr, |
2508 | 6, &fin->fin_src6, fin->fin_plen); |
2509 | break; |
2510 | } |
2511 | match ^= ((np->in_flags & IPN_NOTSRC) != 0); |
2512 | if (match) |
2513 | return 0; |
2514 | |
2515 | match = 0; |
2516 | switch (np->in_odstatype) |
2517 | { |
2518 | case FRI_NORMAL : |
2519 | match = IP6_MASKNEQ(&fin->fin_dst6, &np->in_odstmsk6, |
2520 | &np->in_odstip6); |
2521 | break; |
2522 | case FRI_LOOKUP : |
2523 | match = (*np->in_odstfunc)(fin->fin_main_soft, np->in_odstptr, |
2524 | 6, &fin->fin_dst6, fin->fin_plen); |
2525 | break; |
2526 | } |
2527 | |
2528 | match ^= ((np->in_flags & IPN_NOTDST) != 0); |
2529 | if (match) |
2530 | return 0; |
2531 | |
2532 | ft = &np->in_tuc; |
2533 | if (!(fin->fin_flx & FI_TCPUDP) || |
2534 | (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { |
2535 | if (ft->ftu_scmp || ft->ftu_dcmp) |
2536 | return 0; |
2537 | return 1; |
2538 | } |
2539 | |
2540 | return ipf_tcpudpchk(&fin->fin_fi, ft); |
2541 | } |
2542 | |
2543 | |
2544 | /* ------------------------------------------------------------------------ */ |
2545 | /* Function: ipf_nat6_checkout */ |
2546 | /* Returns: int - -1 == packet failed NAT checks so block it, */ |
2547 | /* 0 == no packet translation occurred, */ |
2548 | /* 1 == packet was successfully translated. */ |
2549 | /* Parameters: fin(I) - pointer to packet information */ |
2550 | /* passp(I) - pointer to filtering result flags */ |
2551 | /* */ |
2552 | /* Check to see if an outcoming packet should be changed. ICMP packets are */ |
2553 | /* first checked to see if they match an existing entry (if an error), */ |
2554 | /* otherwise a search of the current NAT table is made. If neither results */ |
2555 | /* in a match then a search for a matching NAT rule is made. Create a new */ |
2556 | /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ |
2557 | /* packet header(s) as required. */ |
2558 | /* ------------------------------------------------------------------------ */ |
2559 | int |
2560 | ipf_nat6_checkout(fr_info_t *fin, u_32_t *passp) |
2561 | { |
2562 | ipf_main_softc_t *softc = fin->fin_main_soft; |
2563 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
2564 | struct icmp6_hdr *icmp6 = NULL; |
2565 | struct ifnet *ifp, *sifp; |
2566 | #ifdef IPF_V6_PROXIES |
2567 | tcphdr_t *tcp = NULL; |
2568 | #endif |
2569 | int rval, natfailed; |
2570 | ipnat_t *np = NULL; |
2571 | u_int nflags = 0; |
2572 | i6addr_t ipa, iph; |
2573 | int natadd = 1; |
2574 | frentry_t *fr; |
2575 | nat_t *nat; |
2576 | |
2577 | if (softn->ipf_nat_stats.ns_rules == 0 || softn->ipf_nat_lock != 0) |
2578 | return 0; |
2579 | |
2580 | icmp6 = NULL; |
2581 | natfailed = 0; |
2582 | fr = fin->fin_fr; |
2583 | sifp = fin->fin_ifp; |
2584 | if (fr != NULL) { |
2585 | ifp = fr->fr_tifs[fin->fin_rev].fd_ptr; |
2586 | if ((ifp != NULL) && (ifp != (void *)-1)) |
2587 | fin->fin_ifp = ifp; |
2588 | } |
2589 | ifp = fin->fin_ifp; |
2590 | |
2591 | if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { |
2592 | switch (fin->fin_p) |
2593 | { |
2594 | case IPPROTO_TCP : |
2595 | nflags = IPN_TCP; |
2596 | break; |
2597 | case IPPROTO_UDP : |
2598 | nflags = IPN_UDP; |
2599 | break; |
2600 | case IPPROTO_ICMPV6 : |
2601 | icmp6 = fin->fin_dp; |
2602 | |
2603 | /* |
2604 | * Apart from ECHO request and reply, all other |
2605 | * informational messages should not be translated |
2606 | * so as to keep IPv6 working. |
2607 | */ |
2608 | if (icmp6->icmp6_type > ICMP6_ECHO_REPLY) |
2609 | return 0; |
2610 | |
2611 | /* |
2612 | * This is an incoming packet, so the destination is |
2613 | * the icmp6_id and the source port equals 0 |
2614 | */ |
2615 | if ((fin->fin_flx & FI_ICMPQUERY) != 0) |
2616 | nflags = IPN_ICMPQUERY; |
2617 | break; |
2618 | default : |
2619 | break; |
2620 | } |
2621 | |
2622 | #ifdef IPF_V6_PROXIES |
2623 | if ((nflags & IPN_TCPUDP)) |
2624 | tcp = fin->fin_dp; |
2625 | #endif |
2626 | } |
2627 | |
2628 | ipa = fin->fin_src6; |
2629 | |
2630 | READ_ENTER(&softc->ipf_nat); |
2631 | |
2632 | if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) && |
2633 | (nat = ipf_nat6_icmperror(fin, &nflags, NAT_OUTBOUND))) |
2634 | /*EMPTY*/; |
2635 | else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) |
2636 | natadd = 0; |
2637 | else if ((nat = ipf_nat6_outlookup(fin, nflags|NAT_SEARCH, |
2638 | (u_int)fin->fin_p, |
2639 | &fin->fin_src6.in6, |
2640 | &fin->fin_dst6.in6))) { |
2641 | nflags = nat->nat_flags; |
2642 | } else if (fin->fin_off == 0) { |
2643 | u_32_t hv, nmsk = 0; |
2644 | i6addr_t *msk; |
2645 | |
2646 | /* |
2647 | * If there is no current entry in the nat table for this IP#, |
2648 | * create one for it (if there is a matching rule). |
2649 | */ |
2650 | maskloop: |
2651 | msk = &softn->ipf_nat6_map_active_masks[nmsk]; |
2652 | IP6_AND(&ipa, msk, &iph); |
2653 | hv = NAT_HASH_FN6(&iph, 0, softn->ipf_nat_maprules_sz); |
2654 | for (np = softn->ipf_nat_map_rules[hv]; np; np = np->in_mnext) { |
2655 | if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) |
2656 | continue; |
2657 | if (np->in_v[0] != 6) |
2658 | continue; |
2659 | if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p)) |
2660 | continue; |
2661 | if ((np->in_flags & IPN_RF) && |
2662 | !(np->in_flags & nflags)) |
2663 | continue; |
2664 | if (np->in_flags & IPN_FILTER) { |
2665 | switch (ipf_nat6_match(fin, np)) |
2666 | { |
2667 | case 0 : |
2668 | continue; |
2669 | case -1 : |
2670 | rval = -1; |
2671 | goto outmatchfail; |
2672 | case 1 : |
2673 | default : |
2674 | break; |
2675 | } |
2676 | } else if (!IP6_MASKEQ(&ipa, &np->in_osrcmsk, |
2677 | &np->in_osrcip6)) |
2678 | continue; |
2679 | |
2680 | if ((fr != NULL) && |
2681 | !ipf_matchtag(&np->in_tag, &fr->fr_nattag)) |
2682 | continue; |
2683 | |
2684 | #ifdef IPF_V6_PROXIES |
2685 | if (np->in_plabel != -1) { |
2686 | if (((np->in_flags & IPN_FILTER) == 0) && |
2687 | (np->in_odport != fin->fin_data[1])) |
2688 | continue; |
2689 | if (appr_ok(fin, tcp, np) == 0) |
2690 | continue; |
2691 | } |
2692 | #endif |
2693 | |
2694 | if (np->in_flags & IPN_NO) { |
2695 | np->in_hits++; |
2696 | break; |
2697 | } |
2698 | |
2699 | MUTEX_ENTER(&softn->ipf_nat_new); |
2700 | nat = ipf_nat6_add(fin, np, NULL, nflags, NAT_OUTBOUND); |
2701 | MUTEX_EXIT(&softn->ipf_nat_new); |
2702 | if (nat != NULL) { |
2703 | np->in_hits++; |
2704 | break; |
2705 | } |
2706 | natfailed = -1; |
2707 | } |
2708 | if ((np == NULL) && (nmsk < softn->ipf_nat6_map_max)) { |
2709 | nmsk++; |
2710 | goto maskloop; |
2711 | } |
2712 | } |
2713 | |
2714 | if (nat != NULL) { |
2715 | rval = ipf_nat6_out(fin, nat, natadd, nflags); |
2716 | if (rval == 1) { |
2717 | MUTEX_ENTER(&nat->nat_lock); |
2718 | ipf_nat_update(fin, nat); |
2719 | nat->nat_bytes[1] += fin->fin_plen; |
2720 | nat->nat_pkts[1]++; |
2721 | MUTEX_EXIT(&nat->nat_lock); |
2722 | } |
2723 | } else |
2724 | rval = natfailed; |
2725 | outmatchfail: |
2726 | RWLOCK_EXIT(&softc->ipf_nat); |
2727 | |
2728 | switch (rval) |
2729 | { |
2730 | case -1 : |
2731 | if (passp != NULL) { |
2732 | NBUMPSIDE6D(1, ns_drop); |
2733 | *passp = FR_BLOCK; |
2734 | fin->fin_reason = FRB_NATV6; |
2735 | } |
2736 | fin->fin_flx |= FI_BADNAT; |
2737 | NBUMPSIDE6D(1, ns_badnat); |
2738 | break; |
2739 | case 0 : |
2740 | NBUMPSIDE6D(1, ns_ignored); |
2741 | break; |
2742 | case 1 : |
2743 | NBUMPSIDE6D(1, ns_translated); |
2744 | break; |
2745 | } |
2746 | fin->fin_ifp = sifp; |
2747 | return rval; |
2748 | } |
2749 | |
2750 | /* ------------------------------------------------------------------------ */ |
2751 | /* Function: ipf_nat6_out */ |
2752 | /* Returns: int - -1 == packet failed NAT checks so block it, */ |
2753 | /* 1 == packet was successfully translated. */ |
2754 | /* Parameters: fin(I) - pointer to packet information */ |
2755 | /* nat(I) - pointer to NAT structure */ |
2756 | /* natadd(I) - flag indicating if it is safe to add frag cache */ |
2757 | /* nflags(I) - NAT flags set for this packet */ |
2758 | /* */ |
2759 | /* Translate a packet coming "out" on an interface. */ |
2760 | /* ------------------------------------------------------------------------ */ |
2761 | static int |
2762 | ipf_nat6_out(fr_info_t *fin, nat_t *nat, int natadd, u_32_t nflags) |
2763 | { |
2764 | ipf_main_softc_t *softc = fin->fin_main_soft; |
2765 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
2766 | struct icmp6_hdr *icmp6; |
2767 | tcphdr_t *tcp; |
2768 | ipnat_t *np; |
2769 | int skip; |
2770 | int i; |
2771 | |
2772 | tcp = NULL; |
2773 | icmp6 = NULL; |
2774 | np = nat->nat_ptr; |
2775 | |
2776 | if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL)) |
2777 | (void) ipf_frag_natnew(softc, fin, 0, nat); |
2778 | |
2779 | /* |
2780 | * Address assignment is after the checksum modification because |
2781 | * we are using the address in the packet for determining the |
2782 | * correct checksum offset (the ICMP error could be coming from |
2783 | * anyone...) |
2784 | */ |
2785 | switch (nat->nat_dir) |
2786 | { |
2787 | case NAT_OUTBOUND : |
2788 | fin->fin_ip6->ip6_src = nat->nat_nsrc6.in6; |
2789 | fin->fin_src6 = nat->nat_nsrc6; |
2790 | fin->fin_ip6->ip6_dst = nat->nat_ndst6.in6; |
2791 | fin->fin_dst6 = nat->nat_ndst6; |
2792 | break; |
2793 | |
2794 | case NAT_INBOUND : |
2795 | fin->fin_ip6->ip6_src = nat->nat_odst6.in6; |
2796 | fin->fin_src6 = nat->nat_ndst6; |
2797 | fin->fin_ip6->ip6_dst = nat->nat_osrc6.in6; |
2798 | fin->fin_dst6 = nat->nat_nsrc6; |
2799 | break; |
2800 | |
2801 | case NAT_DIVERTIN : |
2802 | { |
2803 | mb_t *m; |
2804 | |
2805 | skip = ipf_nat6_decap(fin, nat); |
2806 | if (skip <= 0) { |
2807 | NBUMPSIDE6D(1, ns_decap_fail); |
2808 | return -1; |
2809 | } |
2810 | |
2811 | m = fin->fin_m; |
2812 | |
2813 | #if defined(MENTAT) && defined(_KERNEL) |
2814 | m->b_rptr += skip; |
2815 | #else |
2816 | m->m_data += skip; |
2817 | m->m_len -= skip; |
2818 | |
2819 | # ifdef M_PKTHDR |
2820 | if (m->m_flags & M_PKTHDR) |
2821 | m->m_pkthdr.len -= skip; |
2822 | # endif |
2823 | #endif |
2824 | |
2825 | MUTEX_ENTER(&nat->nat_lock); |
2826 | ipf_nat_update(fin, nat); |
2827 | MUTEX_EXIT(&nat->nat_lock); |
2828 | fin->fin_flx |= FI_NATED; |
2829 | if (np != NULL && np->in_tag.ipt_num[0] != 0) |
2830 | fin->fin_nattag = &np->in_tag; |
2831 | return 1; |
2832 | /* NOTREACHED */ |
2833 | } |
2834 | |
2835 | case NAT_DIVERTOUT : |
2836 | { |
2837 | udphdr_t *uh; |
2838 | ip6_t *ip6; |
2839 | mb_t *m; |
2840 | |
2841 | m = M_DUP(np->in_divmp); |
2842 | if (m == NULL) { |
2843 | NBUMPSIDE6D(1, ns_divert_dup); |
2844 | return -1; |
2845 | } |
2846 | |
2847 | ip6 = MTOD(m, ip6_t *); |
2848 | |
2849 | ip6->ip6_plen = htons(fin->fin_plen + 8); |
2850 | |
2851 | uh = (udphdr_t *)(ip6 + 1); |
2852 | uh->uh_ulen = htons(fin->fin_plen); |
2853 | |
2854 | PREP_MB_T(fin, m); |
2855 | |
2856 | fin->fin_ip6 = ip6; |
2857 | fin->fin_plen += sizeof(ip6_t) + 8; /* UDP + new IPv4 hdr */ |
2858 | fin->fin_dlen += sizeof(ip6_t) + 8; /* UDP + old IPv4 hdr */ |
2859 | |
2860 | nflags &= ~IPN_TCPUDPICMP; |
2861 | |
2862 | break; |
2863 | } |
2864 | |
2865 | default : |
2866 | break; |
2867 | } |
2868 | |
2869 | if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { |
2870 | u_short *csump; |
2871 | |
2872 | if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) { |
2873 | tcp = fin->fin_dp; |
2874 | |
2875 | switch (nat->nat_dir) |
2876 | { |
2877 | case NAT_OUTBOUND : |
2878 | tcp->th_sport = nat->nat_nsport; |
2879 | fin->fin_data[0] = ntohs(nat->nat_nsport); |
2880 | tcp->th_dport = nat->nat_ndport; |
2881 | fin->fin_data[1] = ntohs(nat->nat_ndport); |
2882 | break; |
2883 | |
2884 | case NAT_INBOUND : |
2885 | tcp->th_sport = nat->nat_odport; |
2886 | fin->fin_data[0] = ntohs(nat->nat_odport); |
2887 | tcp->th_dport = nat->nat_osport; |
2888 | fin->fin_data[1] = ntohs(nat->nat_osport); |
2889 | break; |
2890 | } |
2891 | } |
2892 | |
2893 | if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) { |
2894 | icmp6 = fin->fin_dp; |
2895 | icmp6->icmp6_id = nat->nat_nicmpid; |
2896 | } |
2897 | |
2898 | csump = ipf_nat_proto(fin, nat, nflags); |
2899 | |
2900 | /* |
2901 | * The above comments do not hold for layer 4 (or higher) |
2902 | * checksums... |
2903 | */ |
2904 | if (csump != NULL) { |
2905 | if (nat->nat_dir == NAT_OUTBOUND) |
2906 | ipf_fix_outcksum(fin->fin_cksum, csump, |
2907 | nat->nat_sumd[0], |
2908 | nat->nat_sumd[1] + |
2909 | fin->fin_dlen); |
2910 | else |
2911 | ipf_fix_incksum(fin->fin_cksum, csump, |
2912 | nat->nat_sumd[0], |
2913 | nat->nat_sumd[1] + |
2914 | fin->fin_dlen); |
2915 | } |
2916 | } |
2917 | |
2918 | ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); |
2919 | /* ------------------------------------------------------------- */ |
2920 | /* A few quick notes: */ |
2921 | /* Following are test conditions prior to calling the */ |
2922 | /* ipf_proxy_check routine. */ |
2923 | /* */ |
2924 | /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ |
2925 | /* with a redirect rule, we attempt to match the packet's */ |
2926 | /* source port against in_dport, otherwise we'd compare the */ |
2927 | /* packet's destination. */ |
2928 | /* ------------------------------------------------------------- */ |
2929 | if ((np != NULL) && (np->in_apr != NULL)) { |
2930 | i = ipf_proxy_check(fin, nat); |
2931 | if (i == 0) { |
2932 | i = 1; |
2933 | } else if (i == -1) { |
2934 | NBUMPSIDE6D(1, ns_ipf_proxy_fail); |
2935 | } |
2936 | } else { |
2937 | i = 1; |
2938 | } |
2939 | fin->fin_flx |= FI_NATED; |
2940 | return i; |
2941 | } |
2942 | |
2943 | |
2944 | /* ------------------------------------------------------------------------ */ |
2945 | /* Function: ipf_nat6_checkin */ |
2946 | /* Returns: int - -1 == packet failed NAT checks so block it, */ |
2947 | /* 0 == no packet translation occurred, */ |
2948 | /* 1 == packet was successfully translated. */ |
2949 | /* Parameters: fin(I) - pointer to packet information */ |
2950 | /* passp(I) - pointer to filtering result flags */ |
2951 | /* */ |
2952 | /* Check to see if an incoming packet should be changed. ICMP packets are */ |
2953 | /* first checked to see if they match an existing entry (if an error), */ |
2954 | /* otherwise a search of the current NAT table is made. If neither results */ |
2955 | /* in a match then a search for a matching NAT rule is made. Create a new */ |
2956 | /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ |
2957 | /* packet header(s) as required. */ |
2958 | /* ------------------------------------------------------------------------ */ |
2959 | int |
2960 | ipf_nat6_checkin(fr_info_t *fin, u_32_t *passp) |
2961 | { |
2962 | ipf_main_softc_t *softc = fin->fin_main_soft; |
2963 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
2964 | struct icmp6_hdr *icmp6; |
2965 | u_int nflags, natadd; |
2966 | int rval, natfailed; |
2967 | struct ifnet *ifp; |
2968 | i6addr_t ipa, iph; |
2969 | #ifdef IPF_V6_PROXIES |
2970 | tcphdr_t *tcp; |
2971 | #endif |
2972 | u_short dport; |
2973 | ipnat_t *np; |
2974 | nat_t *nat; |
2975 | |
2976 | if (softn->ipf_nat_stats.ns_rules == 0 || softn->ipf_nat_lock != 0) |
2977 | return 0; |
2978 | |
2979 | #ifdef IPF_V6_PROXIES |
2980 | tcp = NULL; |
2981 | #endif |
2982 | icmp6 = NULL; |
2983 | dport = 0; |
2984 | natadd = 1; |
2985 | nflags = 0; |
2986 | natfailed = 0; |
2987 | ifp = fin->fin_ifp; |
2988 | |
2989 | if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { |
2990 | switch (fin->fin_p) |
2991 | { |
2992 | case IPPROTO_TCP : |
2993 | nflags = IPN_TCP; |
2994 | break; |
2995 | case IPPROTO_UDP : |
2996 | nflags = IPN_UDP; |
2997 | break; |
2998 | case IPPROTO_ICMPV6 : |
2999 | icmp6 = fin->fin_dp; |
3000 | |
3001 | /* |
3002 | * Apart from ECHO request and reply, all other |
3003 | * informational messages should not be translated |
3004 | * so as to keep IPv6 working. |
3005 | */ |
3006 | if (icmp6->icmp6_type > ICMP6_ECHO_REPLY) |
3007 | return 0; |
3008 | |
3009 | /* |
3010 | * This is an incoming packet, so the destination is |
3011 | * the icmp6_id and the source port equals 0 |
3012 | */ |
3013 | if ((fin->fin_flx & FI_ICMPQUERY) != 0) { |
3014 | nflags = IPN_ICMPQUERY; |
3015 | dport = icmp6->icmp6_id; |
3016 | } break; |
3017 | default : |
3018 | break; |
3019 | } |
3020 | |
3021 | if ((nflags & IPN_TCPUDP)) { |
3022 | #ifdef IPF_V6_PROXIES |
3023 | tcp = fin->fin_dp; |
3024 | #endif |
3025 | dport = fin->fin_data[1]; |
3026 | } |
3027 | } |
3028 | |
3029 | ipa = fin->fin_dst6; |
3030 | |
3031 | READ_ENTER(&softc->ipf_nat); |
3032 | |
3033 | if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) && |
3034 | (nat = ipf_nat6_icmperror(fin, &nflags, NAT_INBOUND))) |
3035 | /*EMPTY*/; |
3036 | else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) |
3037 | natadd = 0; |
3038 | else if ((nat = ipf_nat6_inlookup(fin, nflags|NAT_SEARCH, |
3039 | (u_int)fin->fin_p, |
3040 | &fin->fin_src6.in6, &ipa.in6))) { |
3041 | nflags = nat->nat_flags; |
3042 | } else if (fin->fin_off == 0) { |
3043 | u_32_t hv, rmsk = 0; |
3044 | i6addr_t *msk; |
3045 | |
3046 | /* |
3047 | * If there is no current entry in the nat table for this IP#, |
3048 | * create one for it (if there is a matching rule). |
3049 | */ |
3050 | maskloop: |
3051 | msk = &softn->ipf_nat6_rdr_active_masks[rmsk]; |
3052 | IP6_AND(&ipa, msk, &iph); |
3053 | hv = NAT_HASH_FN6(&iph, 0, softn->ipf_nat_rdrrules_sz); |
3054 | for (np = softn->ipf_nat_rdr_rules[hv]; np; np = np->in_rnext) { |
3055 | if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) |
3056 | continue; |
3057 | if (np->in_v[0] != 6) |
3058 | continue; |
3059 | if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p)) |
3060 | continue; |
3061 | if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) |
3062 | continue; |
3063 | if (np->in_flags & IPN_FILTER) { |
3064 | switch (ipf_nat6_match(fin, np)) |
3065 | { |
3066 | case 0 : |
3067 | continue; |
3068 | case -1 : |
3069 | rval = -1; |
3070 | goto inmatchfail; |
3071 | case 1 : |
3072 | default : |
3073 | break; |
3074 | } |
3075 | } else { |
3076 | if (!IP6_MASKEQ(&ipa, &np->in_odstmsk6, |
3077 | &np->in_odstip6)) { |
3078 | continue; |
3079 | } |
3080 | if (np->in_odport && |
3081 | ((np->in_dtop < dport) || |
3082 | (dport < np->in_odport))) |
3083 | continue; |
3084 | } |
3085 | |
3086 | #ifdef IPF_V6_PROXIES |
3087 | if (np->in_plabel != -1) { |
3088 | if (!appr_ok(fin, tcp, np)) { |
3089 | continue; |
3090 | } |
3091 | } |
3092 | #endif |
3093 | |
3094 | if (np->in_flags & IPN_NO) { |
3095 | np->in_hits++; |
3096 | break; |
3097 | } |
3098 | |
3099 | MUTEX_ENTER(&softn->ipf_nat_new); |
3100 | nat = ipf_nat6_add(fin, np, NULL, nflags, NAT_INBOUND); |
3101 | MUTEX_EXIT(&softn->ipf_nat_new); |
3102 | if (nat != NULL) { |
3103 | np->in_hits++; |
3104 | break; |
3105 | } |
3106 | natfailed = -1; |
3107 | } |
3108 | |
3109 | if ((np == NULL) && (rmsk < softn->ipf_nat6_rdr_max)) { |
3110 | rmsk++; |
3111 | goto maskloop; |
3112 | } |
3113 | } |
3114 | if (nat != NULL) { |
3115 | rval = ipf_nat6_in(fin, nat, natadd, nflags); |
3116 | if (rval == 1) { |
3117 | MUTEX_ENTER(&nat->nat_lock); |
3118 | ipf_nat_update(fin, nat); |
3119 | nat->nat_bytes[0] += fin->fin_plen; |
3120 | nat->nat_pkts[0]++; |
3121 | MUTEX_EXIT(&nat->nat_lock); |
3122 | } |
3123 | } else |
3124 | rval = natfailed; |
3125 | inmatchfail: |
3126 | RWLOCK_EXIT(&softc->ipf_nat); |
3127 | |
3128 | switch (rval) |
3129 | { |
3130 | case -1 : |
3131 | if (passp != NULL) { |
3132 | NBUMPSIDE6D(0, ns_drop); |
3133 | *passp = FR_BLOCK; |
3134 | fin->fin_reason = FRB_NATV6; |
3135 | } |
3136 | fin->fin_flx |= FI_BADNAT; |
3137 | NBUMPSIDE6D(0, ns_badnat); |
3138 | break; |
3139 | case 0 : |
3140 | NBUMPSIDE6D(0, ns_ignored); |
3141 | break; |
3142 | case 1 : |
3143 | NBUMPSIDE6D(0, ns_translated); |
3144 | break; |
3145 | } |
3146 | return rval; |
3147 | } |
3148 | |
3149 | |
3150 | /* ------------------------------------------------------------------------ */ |
3151 | /* Function: ipf_nat6_in */ |
3152 | /* Returns: int - -1 == packet failed NAT checks so block it, */ |
3153 | /* 1 == packet was successfully translated. */ |
3154 | /* Parameters: fin(I) - pointer to packet information */ |
3155 | /* nat(I) - pointer to NAT structure */ |
3156 | /* natadd(I) - flag indicating if it is safe to add frag cache */ |
3157 | /* nflags(I) - NAT flags set for this packet */ |
3158 | /* Locks Held: (READ) */ |
3159 | /* */ |
3160 | /* Translate a packet coming "in" on an interface. */ |
3161 | /* ------------------------------------------------------------------------ */ |
3162 | static int |
3163 | ipf_nat6_in(fr_info_t *fin, nat_t *nat, int natadd, u_32_t nflags) |
3164 | { |
3165 | ipf_main_softc_t *softc = fin->fin_main_soft; |
3166 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
3167 | struct icmp6_hdr *icmp6; |
3168 | u_short *csump; |
3169 | tcphdr_t *tcp; |
3170 | ipnat_t *np; |
3171 | int skip; |
3172 | int i; |
3173 | |
3174 | tcp = NULL; |
3175 | csump = NULL; |
3176 | np = nat->nat_ptr; |
3177 | fin->fin_fr = nat->nat_fr; |
3178 | |
3179 | if (np != NULL) { |
3180 | if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) |
3181 | (void) ipf_frag_natnew(softc, fin, 0, nat); |
3182 | |
3183 | /* ------------------------------------------------------------- */ |
3184 | /* A few quick notes: */ |
3185 | /* Following are test conditions prior to calling the */ |
3186 | /* ipf_proxy_check routine. */ |
3187 | /* */ |
3188 | /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ |
3189 | /* with a map rule, we attempt to match the packet's */ |
3190 | /* source port against in_dport, otherwise we'd compare the */ |
3191 | /* packet's destination. */ |
3192 | /* ------------------------------------------------------------- */ |
3193 | if (np->in_apr != NULL) { |
3194 | i = ipf_proxy_check(fin, nat); |
3195 | if (i == -1) { |
3196 | NBUMPSIDE6D(0, ns_ipf_proxy_fail); |
3197 | return -1; |
3198 | } |
3199 | } |
3200 | } |
3201 | |
3202 | ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); |
3203 | |
3204 | /* |
3205 | * Fix up checksums, not by recalculating them, but |
3206 | * simply computing adjustments. |
3207 | * Why only do this for some platforms on inbound packets ? |
3208 | * Because for those that it is done, IP processing is yet to happen |
3209 | * and so the IPv4 header checksum has not yet been evaluated. |
3210 | * Perhaps it should always be done for the benefit of things like |
3211 | * fast forwarding (so that it doesn't need to be recomputed) but with |
3212 | * header checksum offloading, perhaps it is a moot point. |
3213 | */ |
3214 | |
3215 | switch (nat->nat_dir) |
3216 | { |
3217 | case NAT_INBOUND : |
3218 | if ((fin->fin_flx & FI_ICMPERR) == 0) { |
3219 | fin->fin_ip6->ip6_src = nat->nat_nsrc6.in6; |
3220 | fin->fin_src6 = nat->nat_nsrc6; |
3221 | } |
3222 | fin->fin_ip6->ip6_dst = nat->nat_ndst6.in6; |
3223 | fin->fin_dst6 = nat->nat_ndst6; |
3224 | break; |
3225 | |
3226 | case NAT_OUTBOUND : |
3227 | if ((fin->fin_flx & FI_ICMPERR) == 0) { |
3228 | fin->fin_ip6->ip6_src = nat->nat_odst6.in6; |
3229 | fin->fin_src6 = nat->nat_odst6; |
3230 | } |
3231 | fin->fin_ip6->ip6_dst = nat->nat_osrc6.in6; |
3232 | fin->fin_dst6 = nat->nat_osrc6; |
3233 | break; |
3234 | |
3235 | case NAT_DIVERTIN : |
3236 | { |
3237 | udphdr_t *uh; |
3238 | ip6_t *ip6; |
3239 | mb_t *m; |
3240 | |
3241 | m = M_DUP(np->in_divmp); |
3242 | if (m == NULL) { |
3243 | NBUMPSIDE6D(0, ns_divert_dup); |
3244 | return -1; |
3245 | } |
3246 | |
3247 | ip6 = MTOD(m, ip6_t *); |
3248 | ip6->ip6_plen = htons(fin->fin_plen + sizeof(udphdr_t)); |
3249 | |
3250 | uh = (udphdr_t *)(ip6 + 1); |
3251 | uh->uh_ulen = ntohs(fin->fin_plen); |
3252 | |
3253 | PREP_MB_T(fin, m); |
3254 | |
3255 | fin->fin_ip6 = ip6; |
3256 | fin->fin_plen += sizeof(ip6_t) + 8; /* UDP + new IPv6 hdr */ |
3257 | fin->fin_dlen += sizeof(ip6_t) + 8; /* UDP + old IPv6 hdr */ |
3258 | |
3259 | nflags &= ~IPN_TCPUDPICMP; |
3260 | |
3261 | break; |
3262 | } |
3263 | |
3264 | case NAT_DIVERTOUT : |
3265 | { |
3266 | mb_t *m; |
3267 | |
3268 | skip = ipf_nat6_decap(fin, nat); |
3269 | if (skip <= 0) { |
3270 | NBUMPSIDE6D(0, ns_decap_fail); |
3271 | return -1; |
3272 | } |
3273 | |
3274 | m = fin->fin_m; |
3275 | |
3276 | #if defined(MENTAT) && defined(_KERNEL) |
3277 | m->b_rptr += skip; |
3278 | #else |
3279 | m->m_data += skip; |
3280 | m->m_len -= skip; |
3281 | |
3282 | # ifdef M_PKTHDR |
3283 | if (m->m_flags & M_PKTHDR) |
3284 | m->m_pkthdr.len -= skip; |
3285 | # endif |
3286 | #endif |
3287 | |
3288 | ipf_nat_update(fin, nat); |
3289 | fin->fin_flx |= FI_NATED; |
3290 | if (np != NULL && np->in_tag.ipt_num[0] != 0) |
3291 | fin->fin_nattag = &np->in_tag; |
3292 | return 1; |
3293 | /* NOTREACHED */ |
3294 | } |
3295 | } |
3296 | if (nflags & IPN_TCPUDP) |
3297 | tcp = fin->fin_dp; |
3298 | |
3299 | if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { |
3300 | if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) { |
3301 | switch (nat->nat_dir) |
3302 | { |
3303 | case NAT_INBOUND : |
3304 | tcp->th_sport = nat->nat_nsport; |
3305 | fin->fin_data[0] = ntohs(nat->nat_nsport); |
3306 | tcp->th_dport = nat->nat_ndport; |
3307 | fin->fin_data[1] = ntohs(nat->nat_ndport); |
3308 | break; |
3309 | |
3310 | case NAT_OUTBOUND : |
3311 | tcp->th_sport = nat->nat_odport; |
3312 | fin->fin_data[0] = ntohs(nat->nat_odport); |
3313 | tcp->th_dport = nat->nat_osport; |
3314 | fin->fin_data[1] = ntohs(nat->nat_osport); |
3315 | break; |
3316 | } |
3317 | } |
3318 | |
3319 | |
3320 | if ((nat->nat_odport != 0) && (nflags & IPN_ICMPQUERY)) { |
3321 | icmp6 = fin->fin_dp; |
3322 | |
3323 | icmp6->icmp6_id = nat->nat_nicmpid; |
3324 | } |
3325 | |
3326 | csump = ipf_nat_proto(fin, nat, nflags); |
3327 | } |
3328 | |
3329 | /* |
3330 | * The above comments do not hold for layer 4 (or higher) checksums... |
3331 | */ |
3332 | if (csump != NULL) { |
3333 | if (nat->nat_dir == NAT_OUTBOUND) |
3334 | ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0); |
3335 | else |
3336 | ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0); |
3337 | } |
3338 | fin->fin_flx |= FI_NATED; |
3339 | if (np != NULL && np->in_tag.ipt_num[0] != 0) |
3340 | fin->fin_nattag = &np->in_tag; |
3341 | return 1; |
3342 | } |
3343 | |
3344 | |
3345 | /* ------------------------------------------------------------------------ */ |
3346 | /* Function: ipf_nat6_newrewrite */ |
3347 | /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ |
3348 | /* allow rule to be moved if IPN_ROUNDR is set. */ |
3349 | /* Parameters: fin(I) - pointer to packet information */ |
3350 | /* nat(I) - pointer to NAT entry */ |
3351 | /* ni(I) - pointer to structure with misc. information needed */ |
3352 | /* to create new NAT entry. */ |
3353 | /* Write Lock: ipf_nat */ |
3354 | /* */ |
3355 | /* This function is responsible for setting up an active NAT session where */ |
3356 | /* we are changing both the source and destination parameters at the same */ |
3357 | /* time. The loop in here works differently to elsewhere - each iteration */ |
3358 | /* is responsible for changing a single parameter that can be incremented. */ |
3359 | /* So one pass may increase the source IP#, next source port, next dest. IP#*/ |
3360 | /* and the last destination port for a total of 4 iterations to try each. */ |
3361 | /* This is done to try and exhaustively use the translation space available.*/ |
3362 | /* ------------------------------------------------------------------------ */ |
3363 | int |
3364 | ipf_nat6_newrewrite(fr_info_t *fin, nat_t *nat, natinfo_t *nai) |
3365 | { |
3366 | int src_search = 1; |
3367 | int dst_search = 1; |
3368 | fr_info_t frnat; |
3369 | u_32_t flags; |
3370 | u_short swap; |
3371 | ipnat_t *np; |
3372 | nat_t *natl; |
3373 | int l = 0; |
3374 | int changed; |
3375 | |
3376 | natl = NULL; |
3377 | changed = -1; |
3378 | np = nai->nai_np; |
3379 | flags = nat->nat_flags; |
3380 | bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); |
3381 | |
3382 | nat->nat_hm = NULL; |
3383 | |
3384 | do { |
3385 | changed = -1; |
3386 | /* TRACE (l, src_search, dst_search, np) */ |
3387 | |
3388 | if ((src_search == 0) && (np->in_spnext == 0) && |
3389 | (dst_search == 0) && (np->in_dpnext == 0)) { |
3390 | if (l > 0) |
3391 | return -1; |
3392 | } |
3393 | |
3394 | /* |
3395 | * Find a new source address |
3396 | */ |
3397 | if (ipf_nat6_nextaddr(fin, &np->in_nsrc, &frnat.fin_src6, |
3398 | &frnat.fin_src6) == -1) { |
3399 | return -1; |
3400 | } |
3401 | |
3402 | if (IP6_ISZERO(&np->in_nsrcip6) && |
3403 | IP6_ISONES(&np->in_nsrcmsk6)) { |
3404 | src_search = 0; |
3405 | if (np->in_stepnext == 0) |
3406 | np->in_stepnext = 1; |
3407 | |
3408 | } else if (IP6_ISZERO(&np->in_nsrcip6) && |
3409 | IP6_ISZERO(&np->in_nsrcmsk6)) { |
3410 | src_search = 0; |
3411 | if (np->in_stepnext == 0) |
3412 | np->in_stepnext = 1; |
3413 | |
3414 | } else if (IP6_ISONES(&np->in_nsrcmsk)) { |
3415 | src_search = 0; |
3416 | if (np->in_stepnext == 0) |
3417 | np->in_stepnext = 1; |
3418 | |
3419 | } else if (!IP6_ISONES(&np->in_nsrcmsk6)) { |
3420 | if (np->in_stepnext == 0 && changed == -1) { |
3421 | IP6_INC(&np->in_snip); |
3422 | np->in_stepnext++; |
3423 | changed = 0; |
3424 | } |
3425 | } |
3426 | |
3427 | if ((flags & IPN_TCPUDPICMP) != 0) { |
3428 | if (np->in_spnext != 0) |
3429 | frnat.fin_data[0] = np->in_spnext; |
3430 | |
3431 | /* |
3432 | * Standard port translation. Select next port. |
3433 | */ |
3434 | if ((flags & IPN_FIXEDSPORT) != 0) { |
3435 | np->in_stepnext = 2; |
3436 | } else if ((np->in_stepnext == 1) && |
3437 | (changed == -1) && (natl != NULL)) { |
3438 | np->in_spnext++; |
3439 | np->in_stepnext++; |
3440 | changed = 1; |
3441 | if (np->in_spnext > np->in_spmax) |
3442 | np->in_spnext = np->in_spmin; |
3443 | } |
3444 | } else { |
3445 | np->in_stepnext = 2; |
3446 | } |
3447 | np->in_stepnext &= 0x3; |
3448 | |
3449 | /* |
3450 | * Find a new destination address |
3451 | */ |
3452 | /* TRACE (fin, np, l, frnat) */ |
3453 | |
3454 | if (ipf_nat6_nextaddr(fin, &np->in_ndst, &frnat.fin_dst6, |
3455 | &frnat.fin_dst6) == -1) |
3456 | return -1; |
3457 | |
3458 | if (IP6_ISZERO(&np->in_ndstip6) && |
3459 | IP6_ISONES(&np->in_ndstmsk6)) { |
3460 | dst_search = 0; |
3461 | if (np->in_stepnext == 2) |
3462 | np->in_stepnext = 3; |
3463 | |
3464 | } else if (IP6_ISZERO(&np->in_ndstip6) && |
3465 | IP6_ISZERO(&np->in_ndstmsk6)) { |
3466 | dst_search = 0; |
3467 | if (np->in_stepnext == 2) |
3468 | np->in_stepnext = 3; |
3469 | |
3470 | } else if (IP6_ISONES(&np->in_ndstmsk6)) { |
3471 | dst_search = 0; |
3472 | if (np->in_stepnext == 2) |
3473 | np->in_stepnext = 3; |
3474 | |
3475 | } else if (!IP6_ISONES(&np->in_ndstmsk6)) { |
3476 | if ((np->in_stepnext == 2) && (changed == -1) && |
3477 | (natl != NULL)) { |
3478 | changed = 2; |
3479 | np->in_stepnext++; |
3480 | IP6_INC(&np->in_dnip6); |
3481 | } |
3482 | } |
3483 | |
3484 | if ((flags & IPN_TCPUDPICMP) != 0) { |
3485 | if (np->in_dpnext != 0) |
3486 | frnat.fin_data[1] = np->in_dpnext; |
3487 | |
3488 | /* |
3489 | * Standard port translation. Select next port. |
3490 | */ |
3491 | if ((flags & IPN_FIXEDDPORT) != 0) { |
3492 | np->in_stepnext = 0; |
3493 | } else if (np->in_stepnext == 3 && changed == -1) { |
3494 | np->in_dpnext++; |
3495 | np->in_stepnext++; |
3496 | changed = 3; |
3497 | if (np->in_dpnext > np->in_dpmax) |
3498 | np->in_dpnext = np->in_dpmin; |
3499 | } |
3500 | } else { |
3501 | if (np->in_stepnext == 3) |
3502 | np->in_stepnext = 0; |
3503 | } |
3504 | |
3505 | /* TRACE (frnat) */ |
3506 | |
3507 | /* |
3508 | * Here we do a lookup of the connection as seen from |
3509 | * the outside. If an IP# pair already exists, try |
3510 | * again. So if you have A->B becomes C->B, you can |
3511 | * also have D->E become C->E but not D->B causing |
3512 | * another C->B. Also take protocol and ports into |
3513 | * account when determining whether a pre-existing |
3514 | * NAT setup will cause an external conflict where |
3515 | * this is appropriate. |
3516 | * |
3517 | * fin_data[] is swapped around because we are doing a |
3518 | * lookup of the packet is if it were moving in the opposite |
3519 | * direction of the one we are working with now. |
3520 | */ |
3521 | if (flags & IPN_TCPUDP) { |
3522 | swap = frnat.fin_data[0]; |
3523 | frnat.fin_data[0] = frnat.fin_data[1]; |
3524 | frnat.fin_data[1] = swap; |
3525 | } |
3526 | if (fin->fin_out == 1) { |
3527 | natl = ipf_nat6_inlookup(&frnat, |
3528 | flags & ~(SI_WILDP|NAT_SEARCH), |
3529 | (u_int)frnat.fin_p, |
3530 | &frnat.fin_dst6.in6, |
3531 | &frnat.fin_src6.in6); |
3532 | |
3533 | } else { |
3534 | natl = ipf_nat6_outlookup(&frnat, |
3535 | flags & ~(SI_WILDP|NAT_SEARCH), |
3536 | (u_int)frnat.fin_p, |
3537 | &frnat.fin_dst6.in6, |
3538 | &frnat.fin_src6.in6); |
3539 | } |
3540 | if (flags & IPN_TCPUDP) { |
3541 | swap = frnat.fin_data[0]; |
3542 | frnat.fin_data[0] = frnat.fin_data[1]; |
3543 | frnat.fin_data[1] = swap; |
3544 | } |
3545 | |
3546 | /* TRACE natl, in_stepnext, l */ |
3547 | |
3548 | if ((natl != NULL) && (l > 8)) /* XXX 8 is arbitrary */ |
3549 | return -1; |
3550 | |
3551 | np->in_stepnext &= 0x3; |
3552 | |
3553 | l++; |
3554 | changed = -1; |
3555 | } while (natl != NULL); |
3556 | nat->nat_osrc6 = fin->fin_src6; |
3557 | nat->nat_odst6 = fin->fin_dst6; |
3558 | nat->nat_nsrc6 = frnat.fin_src6; |
3559 | nat->nat_ndst6 = frnat.fin_dst6; |
3560 | |
3561 | if ((flags & IPN_TCPUDP) != 0) { |
3562 | nat->nat_osport = htons(fin->fin_data[0]); |
3563 | nat->nat_odport = htons(fin->fin_data[1]); |
3564 | nat->nat_nsport = htons(frnat.fin_data[0]); |
3565 | nat->nat_ndport = htons(frnat.fin_data[1]); |
3566 | } else if ((flags & IPN_ICMPQUERY) != 0) { |
3567 | nat->nat_oicmpid = fin->fin_data[1]; |
3568 | nat->nat_nicmpid = frnat.fin_data[1]; |
3569 | } |
3570 | |
3571 | return 0; |
3572 | } |
3573 | |
3574 | |
3575 | /* ------------------------------------------------------------------------ */ |
3576 | /* Function: ipf_nat6_newdivert */ |
3577 | /* Returns: int - -1 == error, 0 == success */ |
3578 | /* Parameters: fin(I) - pointer to packet information */ |
3579 | /* nat(I) - pointer to NAT entry */ |
3580 | /* ni(I) - pointer to structure with misc. information needed */ |
3581 | /* to create new NAT entry. */ |
3582 | /* Write Lock: ipf_nat */ |
3583 | /* */ |
3584 | /* Create a new NAT divert session as defined by the NAT rule. This is */ |
3585 | /* somewhat different to other NAT session creation routines because we */ |
3586 | /* do not iterate through either port numbers or IP addresses, searching */ |
3587 | /* for a unique mapping, however, a complimentary duplicate check is made. */ |
3588 | /* ------------------------------------------------------------------------ */ |
3589 | int |
3590 | ipf_nat6_newdivert(fr_info_t *fin, nat_t *nat, natinfo_t *nai) |
3591 | { |
3592 | ipf_main_softc_t *softc = fin->fin_main_soft; |
3593 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
3594 | fr_info_t frnat; |
3595 | ipnat_t *np; |
3596 | nat_t *natl; |
3597 | int p; |
3598 | |
3599 | np = nai->nai_np; |
3600 | bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); |
3601 | |
3602 | nat->nat_pr[0] = 0; |
3603 | nat->nat_osrc6 = fin->fin_src6; |
3604 | nat->nat_odst6 = fin->fin_dst6; |
3605 | nat->nat_osport = htons(fin->fin_data[0]); |
3606 | nat->nat_odport = htons(fin->fin_data[1]); |
3607 | frnat.fin_src6 = np->in_snip6; |
3608 | frnat.fin_dst6 = np->in_dnip6; |
3609 | |
3610 | if (np->in_redir & NAT_DIVERTUDP) { |
3611 | frnat.fin_data[0] = np->in_spnext; |
3612 | frnat.fin_data[1] = np->in_dpnext; |
3613 | frnat.fin_flx |= FI_TCPUDP; |
3614 | p = IPPROTO_UDP; |
3615 | } else { |
3616 | frnat.fin_flx &= ~FI_TCPUDP; |
3617 | p = IPPROTO_IPIP; |
3618 | } |
3619 | |
3620 | if (fin->fin_out == 1) { |
3621 | natl = ipf_nat6_inlookup(&frnat, 0, p, &frnat.fin_dst6.in6, |
3622 | &frnat.fin_src6.in6); |
3623 | |
3624 | } else { |
3625 | natl = ipf_nat6_outlookup(&frnat, 0, p, &frnat.fin_dst6.in6, |
3626 | &frnat.fin_src6.in6); |
3627 | } |
3628 | |
3629 | if (natl != NULL) { |
3630 | NBUMPSIDE6D(fin->fin_out, ns_divert_exist); |
3631 | return -1; |
3632 | } |
3633 | |
3634 | nat->nat_nsrc6 = frnat.fin_src6; |
3635 | nat->nat_ndst6 = frnat.fin_dst6; |
3636 | if (np->in_redir & NAT_DIVERTUDP) { |
3637 | nat->nat_nsport = htons(frnat.fin_data[0]); |
3638 | nat->nat_ndport = htons(frnat.fin_data[1]); |
3639 | } |
3640 | nat->nat_pr[fin->fin_out] = fin->fin_p; |
3641 | nat->nat_pr[1 - fin->fin_out] = p; |
3642 | |
3643 | if (np->in_redir & NAT_REDIRECT) |
3644 | nat->nat_dir = NAT_DIVERTIN; |
3645 | else |
3646 | nat->nat_dir = NAT_DIVERTOUT; |
3647 | |
3648 | return 0; |
3649 | } |
3650 | |
3651 | |
3652 | /* ------------------------------------------------------------------------ */ |
3653 | /* Function: nat6_builddivertmp */ |
3654 | /* Returns: int - -1 == error, 0 == success */ |
3655 | /* Parameters: np(I) - pointer to a NAT rule */ |
3656 | /* */ |
3657 | /* For divert rules, a skeleton packet representing what will be prepended */ |
3658 | /* to the real packet is created. Even though we don't have the full */ |
3659 | /* packet here, a checksum is calculated that we update later when we */ |
3660 | /* fill in the final details. At present a 0 checksum for UDP is being set */ |
3661 | /* here because it is expected that divert will be used for localhost. */ |
3662 | /* ------------------------------------------------------------------------ */ |
3663 | static int |
3664 | ipf_nat6_builddivertmp(ipf_nat_softc_t *softn, ipnat_t *np) |
3665 | { |
3666 | udphdr_t *uh; |
3667 | size_t len; |
3668 | ip6_t *ip6; |
3669 | |
3670 | if ((np->in_redir & NAT_DIVERTUDP) != 0) |
3671 | len = sizeof(ip6_t) + sizeof(udphdr_t); |
3672 | else |
3673 | len = sizeof(ip6_t); |
3674 | |
3675 | ALLOC_MB_T(np->in_divmp, len); |
3676 | if (np->in_divmp == NULL) { |
3677 | ATOMIC_INCL(softn->ipf_nat_stats.ns_divert_build); |
3678 | return -1; |
3679 | } |
3680 | |
3681 | /* |
3682 | * First, the header to get the packet diverted to the new destination |
3683 | */ |
3684 | ip6 = MTOD(np->in_divmp, ip6_t *); |
3685 | ip6->ip6_vfc = 0x60; |
3686 | if ((np->in_redir & NAT_DIVERTUDP) != 0) |
3687 | ip6->ip6_nxt = IPPROTO_UDP; |
3688 | else |
3689 | ip6->ip6_nxt = IPPROTO_IPIP; |
3690 | ip6->ip6_hlim = 255; |
3691 | ip6->ip6_plen = 0; |
3692 | ip6->ip6_src = np->in_snip6.in6; |
3693 | ip6->ip6_dst = np->in_dnip6.in6; |
3694 | |
3695 | if (np->in_redir & NAT_DIVERTUDP) { |
3696 | uh = (udphdr_t *)((u_char *)ip6 + sizeof(*ip6)); |
3697 | uh->uh_sum = 0; |
3698 | uh->uh_ulen = 8; |
3699 | uh->uh_sport = htons(np->in_spnext); |
3700 | uh->uh_dport = htons(np->in_dpnext); |
3701 | } |
3702 | |
3703 | return 0; |
3704 | } |
3705 | |
3706 | |
3707 | #define MINDECAP (sizeof(ip6_t) + sizeof(udphdr_t) + sizeof(ip6_t)) |
3708 | |
3709 | /* ------------------------------------------------------------------------ */ |
3710 | /* Function: nat6_decap */ |
3711 | /* Returns: int - -1 == error, 0 == success */ |
3712 | /* Parameters: fin(I) - pointer to packet information */ |
3713 | /* nat(I) - pointer to current NAT session */ |
3714 | /* */ |
3715 | /* This function is responsible for undoing a packet's encapsulation in the */ |
3716 | /* reverse of an encap/divert rule. After removing the outer encapsulation */ |
3717 | /* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/ |
3718 | /* match the "new" packet as it may still be used by IPFilter elsewhere. */ |
3719 | /* We use "dir" here as the basis for some of the expectations about the */ |
3720 | /* outer header. If we return an error, the goal is to leave the original */ |
3721 | /* packet information undisturbed - this falls short at the end where we'd */ |
3722 | /* need to back a backup copy of "fin" - expensive. */ |
3723 | /* ------------------------------------------------------------------------ */ |
3724 | static int |
3725 | ipf_nat6_decap(fr_info_t *fin, nat_t *nat) |
3726 | { |
3727 | ipf_main_softc_t *softc = fin->fin_main_soft; |
3728 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
3729 | char *hdr; |
3730 | int skip; |
3731 | mb_t *m; |
3732 | |
3733 | if ((fin->fin_flx & FI_ICMPERR) != 0) { |
3734 | return 0; |
3735 | } |
3736 | |
3737 | m = fin->fin_m; |
3738 | skip = fin->fin_hlen; |
3739 | |
3740 | switch (nat->nat_dir) |
3741 | { |
3742 | case NAT_DIVERTIN : |
3743 | case NAT_DIVERTOUT : |
3744 | if (fin->fin_plen < MINDECAP) |
3745 | return -1; |
3746 | skip += sizeof(udphdr_t); |
3747 | break; |
3748 | |
3749 | case NAT_ENCAPIN : |
3750 | case NAT_ENCAPOUT : |
3751 | if (fin->fin_plen < (skip + sizeof(ip6_t))) |
3752 | return -1; |
3753 | break; |
3754 | default : |
3755 | return -1; |
3756 | /* NOTREACHED */ |
3757 | } |
3758 | |
3759 | /* |
3760 | * The aim here is to keep the original packet details in "fin" for |
3761 | * as long as possible so that returning with an error is for the |
3762 | * original packet and there is little undoing work to do. |
3763 | */ |
3764 | if (M_LEN(m) < skip + sizeof(ip6_t)) { |
3765 | if (ipf_pr_pullup(fin, skip + sizeof(ip6_t)) == -1) |
3766 | return -1; |
3767 | } |
3768 | |
3769 | hdr = MTOD(fin->fin_m, char *); |
3770 | fin->fin_ip6 = (ip6_t *)(hdr + skip); |
3771 | |
3772 | if (ipf_pr_pullup(fin, skip + sizeof(ip6_t)) == -1) { |
3773 | NBUMPSIDE6D(fin->fin_out, ns_decap_pullup); |
3774 | return -1; |
3775 | } |
3776 | |
3777 | fin->fin_hlen = sizeof(ip6_t); |
3778 | fin->fin_dlen -= skip; |
3779 | fin->fin_plen -= skip; |
3780 | fin->fin_ipoff += skip; |
3781 | |
3782 | if (ipf_makefrip(sizeof(ip6_t), (ip_t *)hdr, fin) == -1) { |
3783 | NBUMPSIDE6D(fin->fin_out, ns_decap_bad); |
3784 | return -1; |
3785 | } |
3786 | |
3787 | return skip; |
3788 | } |
3789 | |
3790 | |
3791 | /* ------------------------------------------------------------------------ */ |
3792 | /* Function: nat6_nextaddr */ |
3793 | /* Returns: int - -1 == bad input (no new address), */ |
3794 | /* 0 == success and dst has new address */ |
3795 | /* Parameters: fin(I) - pointer to packet information */ |
3796 | /* na(I) - how to generate new address */ |
3797 | /* old(I) - original address being replaced */ |
3798 | /* dst(O) - where to put the new address */ |
3799 | /* Write Lock: ipf_nat */ |
3800 | /* */ |
3801 | /* This function uses the contents of the "na" structure, in combination */ |
3802 | /* with "old" to produce a new address to store in "dst". Not all of the */ |
3803 | /* possible uses of "na" will result in a new address. */ |
3804 | /* ------------------------------------------------------------------------ */ |
3805 | static int |
3806 | ipf_nat6_nextaddr(fr_info_t *fin, nat_addr_t *na, i6addr_t *old, i6addr_t *dst) |
3807 | { |
3808 | ipf_main_softc_t *softc = fin->fin_main_soft; |
3809 | ipf_nat_softc_t *softn = softc->ipf_nat_soft; |
3810 | i6addr_t newip, new; |
3811 | u_32_t amin, amax; |
3812 | int error; |
3813 | |
3814 | new.i6[0] = 0; |
3815 | new.i6[1] = 0; |
3816 | new.i6[2] = 0; |
3817 | new.i6[3] = 0; |
3818 | amin = na->na_addr[0].in4.s_addr; |
3819 | |
3820 | switch (na->na_atype) |
3821 | { |
3822 | case FRI_RANGE : |
3823 | amax = na->na_addr[1].in4.s_addr; |
3824 | break; |
3825 | |
3826 | case FRI_NETMASKED : |
3827 | case FRI_DYNAMIC : |
3828 | case FRI_NORMAL : |
3829 | /* |
3830 | * Compute the maximum address by adding the inverse of the |
3831 | * netmask to the minimum address. |
3832 | */ |
3833 | amax = ~na->na_addr[1].in4.s_addr; |
3834 | amax |= amin; |
3835 | break; |
3836 | |
3837 | case FRI_LOOKUP : |
3838 | break; |
3839 | |
3840 | case FRI_BROADCAST : |
3841 | case FRI_PEERADDR : |
3842 | case FRI_NETWORK : |
3843 | default : |
3844 | return -1; |
3845 | } |
3846 | |
3847 | error = -1; |
3848 | switch (na->na_function) |
3849 | { |
3850 | case IPLT_DSTLIST : |
3851 | error = ipf_dstlist_select_node(fin, na->na_ptr, dst->i6, |
3852 | NULL); |
3853 | break; |
3854 | |
3855 | case IPLT_NONE : |
3856 | /* |
3857 | * 0/0 as the new address means leave it alone. |
3858 | */ |
3859 | if (na->na_addr[0].in4.s_addr == 0 && |
3860 | na->na_addr[1].in4.s_addr == 0) { |
3861 | new = *old; |
3862 | |
3863 | /* |
3864 | * 0/32 means get the interface's address |
3865 | */ |
3866 | } else if (IP6_ISZERO(&na->na_addr[0].in6) && |
3867 | IP6_ISONES(&na->na_addr[1].in6)) { |
3868 | if (ipf_ifpaddr(softc, 6, na->na_atype, |
3869 | fin->fin_ifp, &newip, NULL) == -1) { |
3870 | NBUMPSIDE6(fin->fin_out, ns_ifpaddrfail); |
3871 | return -1; |
3872 | } |
3873 | new = newip; |
3874 | } else { |
3875 | new.in6 = na->na_nextip6; |
3876 | } |
3877 | *dst = new; |
3878 | error = 0; |
3879 | break; |
3880 | |
3881 | default : |
3882 | NBUMPSIDE6(fin->fin_out, ns_badnextaddr); |
3883 | break; |
3884 | } |
3885 | |
3886 | return error; |
3887 | } |
3888 | |
3889 | |
3890 | /* ------------------------------------------------------------------------ */ |
3891 | /* Function: ipf_nat6_nextaddrinit */ |
3892 | /* Returns: int - 0 == success, else error number */ |
3893 | /* Parameters: na(I) - NAT address information for generating new addr*/ |
3894 | /* base(I) - start of where to find strings */ |
3895 | /* initial(I) - flag indicating if it is the first call for */ |
3896 | /* this "na" structure. */ |
3897 | /* ifp(I) - network interface to derive address */ |
3898 | /* information from. */ |
3899 | /* */ |
3900 | /* This function is expected to be called in two scenarious: when a new NAT */ |
3901 | /* rule is loaded into the kernel and when the list of NAT rules is sync'd */ |
3902 | /* up with the valid network interfaces (possibly due to them changing.) */ |
3903 | /* To distinguish between these, the "initial" parameter is used. If it is */ |
3904 | /* 1 then this indicates the rule has just been reloaded and 0 for when we */ |
3905 | /* are updating information. This difference is important because in */ |
3906 | /* instances where we are not updating address information associated with */ |
3907 | /* a network interface, we don't want to disturb what the "next" address to */ |
3908 | /* come out of ipf_nat6_nextaddr() will be. */ |
3909 | /* ------------------------------------------------------------------------ */ |
3910 | static int |
3911 | ipf_nat6_nextaddrinit(ipf_main_softc_t *softc, char *base, nat_addr_t *na, |
3912 | int initial, void *ifp) |
3913 | { |
3914 | switch (na->na_atype) |
3915 | { |
3916 | case FRI_LOOKUP : |
3917 | if (na->na_subtype == 0) { |
3918 | na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT, |
3919 | na->na_type, |
3920 | na->na_num, |
3921 | &na->na_func); |
3922 | } else if (na->na_subtype == 1) { |
3923 | na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT, |
3924 | na->na_type, |
3925 | base + na->na_num, |
3926 | &na->na_func); |
3927 | } |
3928 | if (na->na_func == NULL) { |
3929 | IPFERROR(60072); |
3930 | return ESRCH; |
3931 | } |
3932 | if (na->na_ptr == NULL) { |
3933 | IPFERROR(60073); |
3934 | return ESRCH; |
3935 | } |
3936 | break; |
3937 | case FRI_DYNAMIC : |
3938 | case FRI_BROADCAST : |
3939 | case FRI_NETWORK : |
3940 | case FRI_NETMASKED : |
3941 | case FRI_PEERADDR : |
3942 | if (ifp != NULL) |
3943 | (void )ipf_ifpaddr(softc, 6, na->na_atype, ifp, |
3944 | &na->na_addr[0], |
3945 | &na->na_addr[1]); |
3946 | break; |
3947 | |
3948 | case FRI_SPLIT : |
3949 | case FRI_RANGE : |
3950 | if (initial) |
3951 | na->na_nextip6 = na->na_addr[0].in6; |
3952 | break; |
3953 | |
3954 | case FRI_NONE : |
3955 | IP6_ANDASSIGN(&na->na_addr[0].in6, &na->na_addr[1].in6); |
3956 | return 0; |
3957 | |
3958 | case FRI_NORMAL : |
3959 | IP6_ANDASSIGN(&na->na_addr[0].in6, &na->na_addr[1].in6); |
3960 | break; |
3961 | |
3962 | default : |
3963 | IPFERROR(60074); |
3964 | return EINVAL; |
3965 | } |
3966 | |
3967 | if (initial && (na->na_atype == FRI_NORMAL)) { |
3968 | if (IP6_ISZERO(&na->na_addr[0].in6)) { |
3969 | if (IP6_ISONES(&na->na_addr[1].in6) || |
3970 | IP6_ISZERO(&na->na_addr[1].in6)) { |
3971 | return 0; |
3972 | } |
3973 | } |
3974 | |
3975 | na->na_nextip6 = na->na_addr[0].in6; |
3976 | if (!IP6_ISONES(&na->na_addr[1].in6)) { |
3977 | IP6_INC(&na->na_nextip6); |
3978 | } |
3979 | } |
3980 | |
3981 | return 0; |
3982 | } |
3983 | |
3984 | |
3985 | /* ------------------------------------------------------------------------ */ |
3986 | /* Function: ipf_nat6_icmpquerytype */ |
3987 | /* Returns: int - 1 == success, 0 == failure */ |
3988 | /* Parameters: icmptype(I) - ICMP type number */ |
3989 | /* */ |
3990 | /* Tests to see if the ICMP type number passed is a query/response type or */ |
3991 | /* not. */ |
3992 | /* ------------------------------------------------------------------------ */ |
3993 | static int |
3994 | ipf_nat6_icmpquerytype(int icmptype) |
3995 | { |
3996 | |
3997 | /* |
3998 | * For the ICMP query NAT code, it is essential that both the query |
3999 | * and the reply match on the NAT rule. Because the NAT structure |
4000 | * does not keep track of the icmptype, and a single NAT structure |
4001 | * is used for all icmp types with the same src, dest and id, we |
4002 | * simply define the replies as queries as well. The funny thing is, |
4003 | * altough it seems silly to call a reply a query, this is exactly |
4004 | * as it is defined in the IPv4 specification |
4005 | */ |
4006 | |
4007 | switch (icmptype) |
4008 | { |
4009 | |
4010 | case ICMP6_ECHO_REPLY: |
4011 | case ICMP6_ECHO_REQUEST: |
4012 | /* route aedvertisement/solliciation is currently unsupported: */ |
4013 | /* it would require rewriting the ICMP data section */ |
4014 | case ICMP6_MEMBERSHIP_QUERY: |
4015 | case ICMP6_MEMBERSHIP_REPORT: |
4016 | case ICMP6_MEMBERSHIP_REDUCTION: |
4017 | case ICMP6_WRUREQUEST: |
4018 | case ICMP6_WRUREPLY: |
4019 | case MLD6_MTRACE_RESP: |
4020 | case MLD6_MTRACE: |
4021 | return 1; |
4022 | default: |
4023 | return 0; |
4024 | } |
4025 | } |
4026 | #endif /* USE_INET6 */ |
4027 | |