17176Syx160601 /*
27176Syx160601 * Copyright (C) 1995-2003 by Darren Reed.
37176Syx160601 *
47176Syx160601 * See the IPFILTER.LICENCE file for details on licencing.
57176Syx160601 *
6*8624SDarren.Reed@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
77176Syx160601 * Use is subject to license terms.
87176Syx160601 */
97176Syx160601
107176Syx160601 #if defined(KERNEL) || defined(_KERNEL)
117176Syx160601 # undef KERNEL
127176Syx160601 # undef _KERNEL
137176Syx160601 # define KERNEL 1
147176Syx160601 # define _KERNEL 1
157176Syx160601 #endif
167176Syx160601 #include <sys/errno.h>
177176Syx160601 #include <sys/types.h>
187176Syx160601 #include <sys/param.h>
197176Syx160601 #include <sys/time.h>
207176Syx160601 #include <sys/file.h>
217176Syx160601 #if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \
227176Syx160601 defined(_KERNEL)
237176Syx160601 # include "opt_ipfilter_log.h"
247176Syx160601 #endif
257176Syx160601 #if !defined(_KERNEL)
267176Syx160601 # include <stdio.h>
277176Syx160601 # include <string.h>
287176Syx160601 # include <stdlib.h>
297176Syx160601 # define _KERNEL
307176Syx160601 # ifdef __OpenBSD__
317176Syx160601 struct file;
327176Syx160601 # endif
337176Syx160601 # include <sys/uio.h>
347176Syx160601 # undef _KERNEL
357176Syx160601 #endif
367176Syx160601 #if defined(_KERNEL) && (__FreeBSD_version >= 220000)
377176Syx160601 # include <sys/filio.h>
387176Syx160601 # include <sys/fcntl.h>
397176Syx160601 #else
407176Syx160601 # include <sys/ioctl.h>
417176Syx160601 #endif
427176Syx160601 #if !defined(AIX)
437176Syx160601 # include <sys/fcntl.h>
447176Syx160601 #endif
457176Syx160601 #if !defined(linux)
467176Syx160601 # include <sys/protosw.h>
477176Syx160601 #endif
487176Syx160601 #include <sys/socket.h>
497176Syx160601 #if defined(_KERNEL)
507176Syx160601 # include <sys/systm.h>
517176Syx160601 # if !defined(__SVR4) && !defined(__svr4__)
527176Syx160601 # include <sys/mbuf.h>
537176Syx160601 # endif
547176Syx160601 #endif
557176Syx160601 #if defined(__SVR4) || defined(__svr4__)
567176Syx160601 # include <sys/filio.h>
577176Syx160601 # include <sys/byteorder.h>
587176Syx160601 # ifdef _KERNEL
597176Syx160601 # include <sys/dditypes.h>
607176Syx160601 # endif
617176Syx160601 # include <sys/stream.h>
627176Syx160601 # include <sys/kmem.h>
637176Syx160601 #endif
647176Syx160601 #if __FreeBSD_version >= 300000
657176Syx160601 # include <sys/queue.h>
667176Syx160601 #endif
677176Syx160601 #include <net/if.h>
687176Syx160601 #if __FreeBSD_version >= 300000
697176Syx160601 # include <net/if_var.h>
707176Syx160601 # if defined(_KERNEL) && !defined(IPFILTER_LKM)
717176Syx160601 # include "opt_ipfilter.h"
727176Syx160601 # endif
737176Syx160601 #endif
747176Syx160601 #ifdef sun
757176Syx160601 # include <net/af.h>
767176Syx160601 #endif
777176Syx160601 #include <net/route.h>
787176Syx160601 #include <netinet/in.h>
797176Syx160601 #include <netinet/in_systm.h>
807176Syx160601 #include <netinet/ip.h>
817176Syx160601
827176Syx160601 #ifdef RFC1825
837176Syx160601 # include <vpn/md5.h>
847176Syx160601 # include <vpn/ipsec.h>
857176Syx160601 extern struct ifnet vpnif;
867176Syx160601 #endif
877176Syx160601
887176Syx160601 #if !defined(linux)
897176Syx160601 # include <netinet/ip_var.h>
907176Syx160601 #endif
917176Syx160601 #include <netinet/tcp.h>
927176Syx160601 #include <netinet/udp.h>
937176Syx160601 #include <netinet/ip_icmp.h>
947176Syx160601 #include "netinet/ip_compat.h"
957176Syx160601 #include <netinet/tcpip.h>
967176Syx160601 #include "netinet/ip_fil.h"
977176Syx160601 #include "netinet/ip_nat.h"
987176Syx160601 #include "netinet/ip_frag.h"
997176Syx160601 #include "netinet/ip_state.h"
1007176Syx160601 #include "netinet/ip_proxy.h"
1017176Syx160601 #include "netinet/ipf_stack.h"
1027176Syx160601 #ifdef IPFILTER_SYNC
1037176Syx160601 #include "netinet/ip_sync.h"
1047176Syx160601 #endif
1057176Syx160601 #if (__FreeBSD_version >= 300000)
1067176Syx160601 # include <sys/malloc.h>
1077176Syx160601 #endif
1087176Syx160601 /* END OF INCLUDES */
1097176Syx160601
1107176Syx160601 #undef SOCKADDR_IN
1117176Syx160601 #define SOCKADDR_IN struct sockaddr_in
1127176Syx160601
1137176Syx160601 #if !defined(lint)
1147176Syx160601 static const char rcsid[] = "@(#)$Id: ip_nat6.c,v 1.2 2008/02/14 21:05:50 darrenr Exp $";
1157176Syx160601 #endif
1167176Syx160601
1177176Syx160601 static hostmap_t *nat6_hostmap __P((ipnat_t *, i6addr_t *, i6addr_t *,
1187176Syx160601 i6addr_t *, u_32_t, ipf_stack_t *));
1197176Syx160601 static INLINE int nat6_newmap __P((fr_info_t *, nat_t *, natinfo_t *));
1207176Syx160601 static INLINE int nat6_newrdr __P((fr_info_t *, nat_t *, natinfo_t *));
1217176Syx160601 static INLINE int nat6_finalise __P((fr_info_t *, nat_t *, natinfo_t *,
1227176Syx160601 tcphdr_t *, nat_t **, int));
1237176Syx160601 static void nat6_tabmove __P((nat_t *, ipf_stack_t *));
1247176Syx160601 static int nat6_match __P((fr_info_t *, ipnat_t *));
1257176Syx160601 static INLINE int nat_icmpquerytype6 __P((int));
1267176Syx160601
1277176Syx160601
1287176Syx160601 /* ------------------------------------------------------------------------ */
1297176Syx160601 /* Function: nat6_addrdr */
1307176Syx160601 /* Returns: Nil */
1317176Syx160601 /* Parameters: n(I) - pointer to NAT rule to add */
1327176Syx160601 /* */
1337176Syx160601 /* Adds a redirect rule to the hash table of redirect rules and the list of */
1347176Syx160601 /* loaded NAT rules. Updates the bitmask indicating which netmasks are in */
1357176Syx160601 /* use by redirect rules. */
1367176Syx160601 /* ------------------------------------------------------------------------ */
nat6_addrdr(n,ifs)1377176Syx160601 void nat6_addrdr(n, ifs)
1387176Syx160601 ipnat_t *n;
1397176Syx160601 ipf_stack_t *ifs;
1407176Syx160601 {
1417176Syx160601 ipnat_t **np;
1427176Syx160601 i6addr_t j;
1437176Syx160601 u_int hv;
1447176Syx160601 int k;
1457176Syx160601
1467176Syx160601 k = count6bits(n->in_out[1].i6);
1477176Syx160601 if ((k >= 0) && (k != 128))
1487176Syx160601 ifs->ifs_rdr6_masks[k >> 5] |= 1 << (k & 31);
1497176Syx160601 IP6_AND(&n->in_out[0], &n->in_out[1], &j);
1507176Syx160601 hv = NAT_HASH_FN6(&j, 0, ifs->ifs_ipf_rdrrules_sz);
1517176Syx160601 np = ifs->ifs_rdr_rules + hv;
1527176Syx160601 while (*np != NULL)
1537176Syx160601 np = &(*np)->in_rnext;
1547176Syx160601 n->in_rnext = NULL;
1557176Syx160601 n->in_prnext = np;
1567176Syx160601 n->in_hv = hv;
1577176Syx160601 *np = n;
1587176Syx160601 }
1597176Syx160601
1607176Syx160601
1617176Syx160601 /* ------------------------------------------------------------------------ */
1627176Syx160601 /* Function: nat6_addnat */
1637176Syx160601 /* Returns: Nil */
1647176Syx160601 /* Parameters: n(I) - pointer to NAT rule to add */
1657176Syx160601 /* */
1667176Syx160601 /* Adds a NAT map rule to the hash table of rules and the list of loaded */
1677176Syx160601 /* NAT rules. Updates the bitmask indicating which netmasks are in use by */
1687176Syx160601 /* redirect rules. */
1697176Syx160601 /* ------------------------------------------------------------------------ */
nat6_addnat(n,ifs)1707176Syx160601 void nat6_addnat(n, ifs)
1717176Syx160601 ipnat_t *n;
1727176Syx160601 ipf_stack_t *ifs;
1737176Syx160601 {
1747176Syx160601 ipnat_t **np;
1757176Syx160601 i6addr_t j;
1767176Syx160601 u_int hv;
1777176Syx160601 int k;
1787176Syx160601
1797176Syx160601 k = count6bits(n->in_in[1].i6);
1807176Syx160601 if ((k >= 0) && (k != 128))
1817176Syx160601 ifs->ifs_nat6_masks[k >> 5] |= 1 << (k & 31);
1827176Syx160601 IP6_AND(&n->in_in[0], &n->in_in[1], &j);
1837176Syx160601 hv = NAT_HASH_FN6(&j, 0, ifs->ifs_ipf_natrules_sz);
1847176Syx160601 np = ifs->ifs_nat_rules + hv;
1857176Syx160601 while (*np != NULL)
1867176Syx160601 np = &(*np)->in_mnext;
1877176Syx160601 n->in_mnext = NULL;
1887176Syx160601 n->in_pmnext = np;
1897176Syx160601 n->in_hv = hv;
1907176Syx160601 *np = n;
1917176Syx160601 }
1927176Syx160601
1937176Syx160601
1947176Syx160601 /* ------------------------------------------------------------------------ */
1957176Syx160601 /* Function: nat6_hostmap */
1967176Syx160601 /* Returns: struct hostmap* - NULL if no hostmap could be created, */
1977176Syx160601 /* else a pointer to the hostmapping to use */
1987176Syx160601 /* Parameters: np(I) - pointer to NAT rule */
1997176Syx160601 /* real(I) - real IP address */
2007176Syx160601 /* map(I) - mapped IP address */
2017176Syx160601 /* port(I) - destination port number */
2027176Syx160601 /* Write Locks: ipf_nat */
2037176Syx160601 /* */
2047176Syx160601 /* Check if an ip address has already been allocated for a given mapping */
2057176Syx160601 /* that is not doing port based translation. If is not yet allocated, then */
2067176Syx160601 /* create a new entry if a non-NULL NAT rule pointer has been supplied. */
2077176Syx160601 /* ------------------------------------------------------------------------ */
nat6_hostmap(np,src,dst,map,port,ifs)2087176Syx160601 static struct hostmap *nat6_hostmap(np, src, dst, map, port, ifs)
2097176Syx160601 ipnat_t *np;
2107176Syx160601 i6addr_t *src, *dst, *map;
2117176Syx160601 u_32_t port;
2127176Syx160601 ipf_stack_t *ifs;
2137176Syx160601 {
2147176Syx160601 hostmap_t *hm;
2157176Syx160601 u_int hv;
2167176Syx160601
2177176Syx160601 hv = (src->i6[3] ^ dst->i6[3]);
2187176Syx160601 hv += (src->i6[2] ^ dst->i6[2]);
2197176Syx160601 hv += (src->i6[1] ^ dst->i6[1]);
2207176Syx160601 hv += (src->i6[0] ^ dst->i6[0]);
2217176Syx160601 hv += src->i6[3];
2227176Syx160601 hv += src->i6[2];
2237176Syx160601 hv += src->i6[1];
2247176Syx160601 hv += src->i6[0];
2257176Syx160601 hv += dst->i6[3];
2267176Syx160601 hv += dst->i6[2];
2277176Syx160601 hv += dst->i6[1];
2287176Syx160601 hv += dst->i6[0];
2297176Syx160601 hv %= HOSTMAP_SIZE;
2307176Syx160601 for (hm = ifs->ifs_maptable[hv]; hm; hm = hm->hm_next)
2317176Syx160601 if (IP6_EQ(&hm->hm_srcip6, src) &&
2327176Syx160601 IP6_EQ(&hm->hm_dstip6, dst) &&
2337176Syx160601 ((np == NULL) || (np == hm->hm_ipnat)) &&
2347176Syx160601 ((port == 0) || (port == hm->hm_port))) {
2357176Syx160601 hm->hm_ref++;
2367176Syx160601 return hm;
2377176Syx160601 }
2387176Syx160601
2397176Syx160601 if (np == NULL)
2407176Syx160601 return NULL;
2417176Syx160601
2427176Syx160601 KMALLOC(hm, hostmap_t *);
2437176Syx160601 if (hm) {
2447176Syx160601 hm->hm_hnext = ifs->ifs_ipf_hm_maplist;
2457176Syx160601 hm->hm_phnext = &ifs->ifs_ipf_hm_maplist;
2467176Syx160601 if (ifs->ifs_ipf_hm_maplist != NULL)
2477176Syx160601 ifs->ifs_ipf_hm_maplist->hm_phnext = &hm->hm_hnext;
2487176Syx160601 ifs->ifs_ipf_hm_maplist = hm;
2497176Syx160601
2507176Syx160601 hm->hm_next = ifs->ifs_maptable[hv];
2517176Syx160601 hm->hm_pnext = ifs->ifs_maptable + hv;
2527176Syx160601 if (ifs->ifs_maptable[hv] != NULL)
2537176Syx160601 ifs->ifs_maptable[hv]->hm_pnext = &hm->hm_next;
2547176Syx160601 ifs->ifs_maptable[hv] = hm;
2557176Syx160601 hm->hm_ipnat = np;
2567176Syx160601 hm->hm_src = *src;
2577176Syx160601 hm->hm_dst = *dst;
2587176Syx160601 hm->hm_map = *map;
2597176Syx160601 hm->hm_ref = 1;
2607176Syx160601 hm->hm_port = port;
2617176Syx160601 hm->hm_v = 6;
2627176Syx160601 }
2637176Syx160601 return hm;
2647176Syx160601 }
2657176Syx160601
2667176Syx160601
2677176Syx160601 /* ------------------------------------------------------------------------ */
2687176Syx160601 /* Function: nat6_newmap */
2697176Syx160601 /* Returns: int - -1 == error, 0 == success */
2707176Syx160601 /* Parameters: fin(I) - pointer to packet information */
2717176Syx160601 /* nat(I) - pointer to NAT entry */
2727176Syx160601 /* ni(I) - pointer to structure with misc. information needed */
2737176Syx160601 /* to create new NAT entry. */
2747176Syx160601 /* */
2757176Syx160601 /* Given an empty NAT structure, populate it with new information about a */
2767176Syx160601 /* new NAT session, as defined by the matching NAT rule. */
2777176Syx160601 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
2787176Syx160601 /* to the new IP address for the translation. */
2797176Syx160601 /* ------------------------------------------------------------------------ */
nat6_newmap(fin,nat,ni)2807176Syx160601 static INLINE int nat6_newmap(fin, nat, ni)
2817176Syx160601 fr_info_t *fin;
2827176Syx160601 nat_t *nat;
2837176Syx160601 natinfo_t *ni;
2847176Syx160601 {
2857176Syx160601 u_short st_port, dport, sport, port, sp, dp;
2867176Syx160601 i6addr_t in, st_ip;
2877176Syx160601 hostmap_t *hm;
2887176Syx160601 u_32_t flags;
2897176Syx160601 ipnat_t *np;
2907176Syx160601 nat_t *natl;
2917176Syx160601 int l;
2927176Syx160601 ipf_stack_t *ifs = fin->fin_ifs;
2937176Syx160601
2947176Syx160601 /*
2957176Syx160601 * If it's an outbound packet which doesn't match any existing
2967176Syx160601 * record, then create a new port
2977176Syx160601 */
2987176Syx160601 l = 0;
2997176Syx160601 hm = NULL;
3007176Syx160601 np = ni->nai_np;
3017176Syx160601 st_ip = np->in_next6;
3027176Syx160601 st_port = np->in_pnext;
3037176Syx160601 flags = ni->nai_flags;
3047176Syx160601 sport = ni->nai_sport;
3057176Syx160601 dport = ni->nai_dport;
3067176Syx160601
3077176Syx160601 /*
3087176Syx160601 * Do a loop until we either run out of entries to try or we find
3097176Syx160601 * a NAT mapping that isn't currently being used. This is done
3107176Syx160601 * because the change to the source is not (usually) being fixed.
3117176Syx160601 */
3127176Syx160601 do {
3137176Syx160601 port = 0;
3147176Syx160601 in = np->in_next6;
3157176Syx160601 if (l == 0) {
3167176Syx160601 /*
3177176Syx160601 * Check to see if there is an existing NAT
3187176Syx160601 * setup for this IP address pair.
3197176Syx160601 */
3207176Syx160601 hm = nat6_hostmap(np, &fin->fin_src6, &fin->fin_dst6,
3217176Syx160601 &in, 0, ifs);
3227176Syx160601 if (hm != NULL)
3237176Syx160601 in = hm->hm_map;
3247176Syx160601 } else if ((l == 1) && (hm != NULL)) {
3257176Syx160601 fr_hostmapdel(&hm);
3267176Syx160601 }
3277176Syx160601
3287176Syx160601 nat->nat_hm = hm;
3297176Syx160601
3307176Syx160601 if (IP6_ISONES(&np->in_out[1]) && (np->in_pnext == 0)) {
3317176Syx160601 if (l > 0)
3327176Syx160601 return -1;
3337176Syx160601 }
3347176Syx160601
3357176Syx160601 if (np->in_redir == NAT_BIMAP &&
3367176Syx160601 IP6_EQ(&np->in_in[1], &np->in_out[1])) {
3377176Syx160601 i6addr_t temp;
3387176Syx160601 /*
3397176Syx160601 * map the address block in a 1:1 fashion
3407176Syx160601 */
3417176Syx160601 temp.i6[0] = fin->fin_src6.i6[0] &
3427176Syx160601 ~np->in_in[1].i6[0];
3437176Syx160601 temp.i6[1] = fin->fin_src6.i6[1] &
3447176Syx160601 ~np->in_in[1].i6[1];
3457176Syx160601 temp.i6[2] = fin->fin_src6.i6[2] &
3467176Syx160601 ~np->in_in[1].i6[2];
3477176Syx160601 temp.i6[3] = fin->fin_src6.i6[3] &
3487176Syx160601 ~np->in_in[1].i6[3];
3497176Syx160601 in = np->in_out[0];
3507176Syx160601 IP6_MERGE(&in, &temp, &np->in_in[0]);
3517176Syx160601
3527176Syx160601 #ifdef NEED_128BIT_MATH
3537176Syx160601 } else if (np->in_redir & NAT_MAPBLK) {
3547176Syx160601 if ((l >= np->in_ppip) || ((l > 0) &&
3557176Syx160601 !(flags & IPN_TCPUDP)))
3567176Syx160601 return -1;
3577176Syx160601 /*
3587176Syx160601 * map-block - Calculate destination address.
3597176Syx160601 */
3607176Syx160601 IP6_MASK(&in, &fin->fin_src6, &np->in_in[1]);
3617176Syx160601 in = ntol(in);
3627176Syx160601 inb = in;
3637176Syx160601 in /= np->in_ippip;
3647176Syx160601 in &= ntohl(~np->in_out[1]);
3657176Syx160601 in += ntohl(np->in_out[0]);
3667176Syx160601 /*
3677176Syx160601 * Calculate destination port.
3687176Syx160601 */
3697176Syx160601 if ((flags & IPN_TCPUDP) &&
3707176Syx160601 (np->in_ppip != 0)) {
3717176Syx160601 port = ntohs(sport) + l;
3727176Syx160601 port %= np->in_ppip;
3737176Syx160601 port += np->in_ppip *
3747176Syx160601 (inb.s_addr % np->in_ippip);
3757176Syx160601 port += MAPBLK_MINPORT;
3767176Syx160601 port = htons(port);
3777176Syx160601 }
3787176Syx160601 #endif
3797176Syx160601
3807176Syx160601 } else if (IP6_ISZERO(&np->in_out[0]) &&
3817176Syx160601 IP6_ISONES(&np->in_out[1])) {
3827176Syx160601 /*
3837176Syx160601 * 0/128 - use the interface's IP address.
3847176Syx160601 */
3857176Syx160601 if ((l > 0) ||
3867176Syx160601 fr_ifpaddr(6, FRI_NORMAL, fin->fin_ifp,
3877176Syx160601 (void *)&in, NULL, fin->fin_ifs) == -1)
3887176Syx160601 return -1;
3897176Syx160601
3907176Syx160601 } else if (IP6_ISZERO(&np->in_out[0]) &&
3917176Syx160601 IP6_ISZERO(&np->in_out[1])) {
3927176Syx160601 /*
3937176Syx160601 * 0/0 - use the original source address/port.
3947176Syx160601 */
3957176Syx160601 if (l > 0)
3967176Syx160601 return -1;
3977176Syx160601 in = fin->fin_src6;
3987176Syx160601
3997176Syx160601 } else if (!IP6_ISONES(&np->in_out[1]) &&
4007176Syx160601 (np->in_pnext == 0) && ((l > 0) || (hm == NULL))) {
4017176Syx160601 IP6_INC(&np->in_next6);
4027176Syx160601 }
4037176Syx160601
4047176Syx160601 natl = NULL;
4057176Syx160601
4067176Syx160601 if ((flags & IPN_TCPUDP) &&
4077176Syx160601 ((np->in_redir & NAT_MAPBLK) == 0) &&
4087176Syx160601 (np->in_flags & IPN_AUTOPORTMAP)) {
4097176Syx160601 /*EMPTY*/;
4107176Syx160601 #ifdef NEED_128BIT_MATH
4117176Syx160601 /*
4127176Syx160601 * XXX "ports auto" (without map-block)
4137176Syx160601 */
4147176Syx160601 if ((l > 0) && (l % np->in_ppip == 0)) {
4157176Syx160601 if (l > np->in_space) {
4167176Syx160601 return -1;
4177176Syx160601 } else if ((l > np->in_ppip) &&
4187176Syx160601 !IP6_ISONES(&np->in_out[1])) {
4197176Syx160601 IP6_INC(&np->in_next6);
4207176Syx160601 }
4217176Syx160601 }
4227176Syx160601 if (np->in_ppip != 0) {
4237176Syx160601 port = ntohs(sport);
4247176Syx160601 port += (l % np->in_ppip);
4257176Syx160601 port %= np->in_ppip;
4267176Syx160601 port += np->in_ppip *
4277176Syx160601 (ntohl(fin->fin_src6) %
4287176Syx160601 np->in_ippip);
4297176Syx160601 port += MAPBLK_MINPORT;
4307176Syx160601 port = htons(port);
4317176Syx160601 }
4327176Syx160601 #endif
4337176Syx160601
4347176Syx160601 } else if (((np->in_redir & NAT_MAPBLK) == 0) &&
4357176Syx160601 (flags & IPN_TCPUDPICMP) && (np->in_pnext != 0)) {
4367176Syx160601 /*
4377176Syx160601 * Standard port translation. Select next port.
4387176Syx160601 */
4397259Sdr146992 if (np->in_flags & IPN_SEQUENTIAL) {
4407259Sdr146992 port = np->in_pnext;
4417259Sdr146992 } else {
4427259Sdr146992 port = ipf_random() % (ntohs(np->in_pmax) -
4437259Sdr146992 ntohs(np->in_pmin));
4447259Sdr146992 port += ntohs(np->in_pmin);
4457259Sdr146992 }
4467259Sdr146992 port = htons(port);
4477259Sdr146992 np->in_pnext++;
4487176Syx160601
4497176Syx160601 if (np->in_pnext > ntohs(np->in_pmax)) {
4507176Syx160601 np->in_pnext = ntohs(np->in_pmin);
4517176Syx160601 if (!IP6_ISONES(&np->in_out[1])) {
4527176Syx160601 IP6_INC(&np->in_next6);
4537176Syx160601 }
4547176Syx160601 }
4557176Syx160601 }
4567176Syx160601
4577176Syx160601 if (np->in_flags & IPN_IPRANGE) {
4587176Syx160601 if (IP6_GT(&np->in_next6, &np->in_out[1]))
4597176Syx160601 np->in_next6 = np->in_out[0];
4607176Syx160601 } else {
4617176Syx160601 i6addr_t a1, a2;
4627176Syx160601
4637176Syx160601 a1 = np->in_next6;
4647176Syx160601 IP6_INC(&a1);
4657176Syx160601 IP6_AND(&a1, &np->in_out[1], &a2);
4667176Syx160601 if (!IP6_ISONES(&np->in_out[1]) &&
4677176Syx160601 IP6_GT(&a2, &np->in_out[0])) {
4687176Syx160601 IP6_ADD(&np->in_out[0], 1, &np->in_next6);
4697176Syx160601 }
4707176Syx160601 }
4717176Syx160601
4727176Syx160601 if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY)))
4737176Syx160601 port = sport;
4747176Syx160601
4757176Syx160601 /*
4767176Syx160601 * Here we do a lookup of the connection as seen from
4777176Syx160601 * the outside. If an IP# pair already exists, try
4787176Syx160601 * again. So if you have A->B becomes C->B, you can
4797176Syx160601 * also have D->E become C->E but not D->B causing
4807176Syx160601 * another C->B. Also take protocol and ports into
4817176Syx160601 * account when determining whether a pre-existing
4827176Syx160601 * NAT setup will cause an external conflict where
4837176Syx160601 * this is appropriate.
4847176Syx160601 */
4857176Syx160601 sp = fin->fin_data[0];
4867176Syx160601 dp = fin->fin_data[1];
4877176Syx160601 fin->fin_data[0] = fin->fin_data[1];
4887176Syx160601 fin->fin_data[1] = htons(port);
4897176Syx160601 natl = nat6_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
4907176Syx160601 (u_int)fin->fin_p, &fin->fin_dst6.in6, &in.in6);
4917176Syx160601 fin->fin_data[0] = sp;
4927176Syx160601 fin->fin_data[1] = dp;
4937176Syx160601
4947176Syx160601 /*
4957176Syx160601 * Has the search wrapped around and come back to the
4967176Syx160601 * start ?
4977176Syx160601 */
4987176Syx160601 if ((natl != NULL) &&
4997176Syx160601 (np->in_pnext != 0) && (st_port == np->in_pnext) &&
5007176Syx160601 !IP6_ISZERO(&np->in_next6) &&
5017176Syx160601 IP6_EQ(&st_ip, &np->in_next6))
5027176Syx160601 return -1;
5037176Syx160601 l++;
5047176Syx160601 } while (natl != NULL);
5057176Syx160601
5067176Syx160601 if (np->in_space > 0)
5077176Syx160601 np->in_space--;
5087176Syx160601
5097176Syx160601 /* Setup the NAT table */
5107176Syx160601 nat->nat_inip6 = fin->fin_src6;
5117176Syx160601 nat->nat_outip6 = in;
5127176Syx160601 nat->nat_oip6 = fin->fin_dst6;
5137176Syx160601 if (nat->nat_hm == NULL)
5147176Syx160601 nat->nat_hm = nat6_hostmap(np, &fin->fin_src6, &fin->fin_dst6,
5157176Syx160601 &nat->nat_outip6, 0, ifs);
5167176Syx160601
5177176Syx160601 if (flags & IPN_TCPUDP) {
5187176Syx160601 nat->nat_inport = sport;
5197176Syx160601 nat->nat_outport = port; /* sport */
5207176Syx160601 nat->nat_oport = dport;
5217176Syx160601 ((tcphdr_t *)fin->fin_dp)->th_sport = port;
5227176Syx160601 } else if (flags & IPN_ICMPQUERY) {
5237176Syx160601 ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = port;
5247176Syx160601 nat->nat_inport = port;
5257176Syx160601 nat->nat_outport = port;
5267176Syx160601 }
5277176Syx160601
5287176Syx160601 ni->nai_port = port;
5297176Syx160601 ni->nai_nport = dport;
5307176Syx160601 return 0;
5317176Syx160601 }
5327176Syx160601
5337176Syx160601
5347176Syx160601 /* ------------------------------------------------------------------------ */
5357176Syx160601 /* Function: nat6_newrdr */
5367176Syx160601 /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */
5377176Syx160601 /* allow rule to be moved if IPN_ROUNDR is set. */
5387176Syx160601 /* Parameters: fin(I) - pointer to packet information */
5397176Syx160601 /* nat(I) - pointer to NAT entry */
5407176Syx160601 /* ni(I) - pointer to structure with misc. information needed */
5417176Syx160601 /* to create new NAT entry. */
5427176Syx160601 /* */
5437176Syx160601 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
5447176Syx160601 /* to the new IP address for the translation. */
5457176Syx160601 /* ------------------------------------------------------------------------ */
nat6_newrdr(fin,nat,ni)5467176Syx160601 static INLINE int nat6_newrdr(fin, nat, ni)
5477176Syx160601 fr_info_t *fin;
5487176Syx160601 nat_t *nat;
5497176Syx160601 natinfo_t *ni;
5507176Syx160601 {
5517176Syx160601 u_short nport, dport, sport;
5527176Syx160601 i6addr_t in;
5537176Syx160601 u_short sp, dp;
5547176Syx160601 hostmap_t *hm;
5557176Syx160601 u_32_t flags;
5567176Syx160601 ipnat_t *np;
5577176Syx160601 nat_t *natl;
5587176Syx160601 int move;
5597176Syx160601 ipf_stack_t *ifs = fin->fin_ifs;
5607176Syx160601
5617176Syx160601 move = 1;
5627176Syx160601 hm = NULL;
5637176Syx160601 in.i6[0] = 0;
5647176Syx160601 in.i6[1] = 0;
5657176Syx160601 in.i6[2] = 0;
5667176Syx160601 in.i6[3] = 0;
5677176Syx160601 np = ni->nai_np;
5687176Syx160601 flags = ni->nai_flags;
5697176Syx160601 sport = ni->nai_sport;
5707176Syx160601 dport = ni->nai_dport;
5717176Syx160601
5727176Syx160601 /*
5737176Syx160601 * If the matching rule has IPN_STICKY set, then we want to have the
5747176Syx160601 * same rule kick in as before. Why would this happen? If you have
5757176Syx160601 * a collection of rdr rules with "round-robin sticky", the current
5767176Syx160601 * packet might match a different one to the previous connection but
5777176Syx160601 * we want the same destination to be used.
5787176Syx160601 */
5797176Syx160601 if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) ==
5807176Syx160601 (IPN_ROUNDR|IPN_STICKY)) {
5817176Syx160601 hm = nat6_hostmap(NULL, &fin->fin_src6, &fin->fin_dst6, &in,
5827176Syx160601 (u_32_t)dport, ifs);
5837176Syx160601 if (hm != NULL) {
5847176Syx160601 in = hm->hm_map;
5857176Syx160601 np = hm->hm_ipnat;
5867176Syx160601 ni->nai_np = np;
5877176Syx160601 move = 0;
5887176Syx160601 }
5897176Syx160601 }
5907176Syx160601
5917176Syx160601 /*
5927176Syx160601 * Otherwise, it's an inbound packet. Most likely, we don't
5937176Syx160601 * want to rewrite source ports and source addresses. Instead,
5947176Syx160601 * we want to rewrite to a fixed internal address and fixed
5957176Syx160601 * internal port.
5967176Syx160601 */
5977176Syx160601 if (np->in_flags & IPN_SPLIT) {
5987176Syx160601 in = np->in_next6;
5997176Syx160601
6007176Syx160601 if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) {
6017176Syx160601 hm = nat6_hostmap(np, &fin->fin_src6, &fin->fin_dst6,
6027176Syx160601 &in, (u_32_t)dport, ifs);
6037176Syx160601 if (hm != NULL) {
6047176Syx160601 in = hm->hm_map;
6057176Syx160601 move = 0;
6067176Syx160601 }
6077176Syx160601 }
6087176Syx160601
6097176Syx160601 if (hm == NULL || hm->hm_ref == 1) {
6107176Syx160601 if (IP6_EQ(&np->in_in[0], &in)) {
6117176Syx160601 np->in_next6 = np->in_in[1];
6127176Syx160601 move = 0;
6137176Syx160601 } else {
6147176Syx160601 np->in_next6 = np->in_in[0];
6157176Syx160601 }
6167176Syx160601 }
6177176Syx160601
6187176Syx160601 } else if (IP6_ISZERO(&np->in_in[0]) &&
6197176Syx160601 IP6_ISONES(&np->in_in[1])) {
6207176Syx160601 /*
6217176Syx160601 * 0/128 - use the interface's IP address.
6227176Syx160601 */
6237176Syx160601 if (fr_ifpaddr(6, FRI_NORMAL, fin->fin_ifp, (void *)&in, NULL,
6247176Syx160601 fin->fin_ifs) == -1)
6257176Syx160601 return -1;
6267176Syx160601
6277176Syx160601 } else if (IP6_ISZERO(&np->in_in[0]) &&
6287176Syx160601 IP6_ISZERO(&np->in_in[1])) {
6297176Syx160601 /*
6307176Syx160601 * 0/0 - use the original destination address/port.
6317176Syx160601 */
6327176Syx160601 in = fin->fin_dst6;
6337176Syx160601
6347176Syx160601 } else if (np->in_redir == NAT_BIMAP &&
6357176Syx160601 IP6_EQ(&np->in_in[1], &np->in_out[1])) {
6367176Syx160601 i6addr_t temp;
6377176Syx160601 /*
6387176Syx160601 * map the address block in a 1:1 fashion
6397176Syx160601 */
6407176Syx160601 temp.i6[0] = fin->fin_dst6.i6[0] & ~np->in_in[1].i6[0];
6417176Syx160601 temp.i6[1] = fin->fin_dst6.i6[1] & ~np->in_in[1].i6[1];
6427176Syx160601 temp.i6[2] = fin->fin_dst6.i6[2] & ~np->in_in[1].i6[2];
6437176Syx160601 temp.i6[3] = fin->fin_dst6.i6[3] & ~np->in_in[1].i6[3];
6447176Syx160601 in = np->in_in[0];
6457176Syx160601 IP6_MERGE(&in, &temp, &np->in_in[1]);
6467176Syx160601 } else {
6477176Syx160601 in = np->in_in[0];
6487176Syx160601 }
6497176Syx160601
6507176Syx160601 if ((np->in_pnext == 0) || ((flags & NAT_NOTRULEPORT) != 0))
6517176Syx160601 nport = dport;
6527176Syx160601 else {
6537176Syx160601 /*
6547176Syx160601 * Whilst not optimized for the case where
6557176Syx160601 * pmin == pmax, the gain is not significant.
6567176Syx160601 */
6577176Syx160601 if (((np->in_flags & IPN_FIXEDDPORT) == 0) &&
6587176Syx160601 (np->in_pmin != np->in_pmax)) {
6597176Syx160601 nport = ntohs(dport) - ntohs(np->in_pmin) +
6607176Syx160601 ntohs(np->in_pnext);
6617176Syx160601 nport = htons(nport);
6627176Syx160601 } else
6637176Syx160601 nport = np->in_pnext;
6647176Syx160601 }
6657176Syx160601
6667176Syx160601 /*
6677176Syx160601 * When the redirect-to address is set to 0.0.0.0, just
6687176Syx160601 * assume a blank `forwarding' of the packet. We don't
6697176Syx160601 * setup any translation for this either.
6707176Syx160601 */
6717176Syx160601 if (IP6_ISZERO(&in)) {
6727176Syx160601 if (nport == dport)
6737176Syx160601 return -1;
6747176Syx160601 in = fin->fin_dst6;
6757176Syx160601 }
6767176Syx160601
6777176Syx160601 /*
6787176Syx160601 * Check to see if this redirect mapping already exists and if
6797176Syx160601 * it does, return "failure" (allowing it to be created will just
6807176Syx160601 * cause one or both of these "connections" to stop working.)
6817176Syx160601 */
6827176Syx160601 sp = fin->fin_data[0];
6837176Syx160601 dp = fin->fin_data[1];
6847176Syx160601 fin->fin_data[1] = fin->fin_data[0];
6857176Syx160601 fin->fin_data[0] = ntohs(nport);
6867176Syx160601 natl = nat6_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
6877176Syx160601 (u_int)fin->fin_p, &in.in6, &fin->fin_src6.in6);
6887176Syx160601 fin->fin_data[0] = sp;
6897176Syx160601 fin->fin_data[1] = dp;
6907176Syx160601 if (natl != NULL)
6917176Syx160601 return -1;
6927176Syx160601
6937176Syx160601 nat->nat_inip6 = in;
6947176Syx160601 nat->nat_outip6 = fin->fin_dst6;
6957176Syx160601 nat->nat_oip6 = fin->fin_src6;
6967176Syx160601 if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0))
6977176Syx160601 nat->nat_hm = nat6_hostmap(np, &fin->fin_src6,
6987176Syx160601 &fin->fin_dst6, &in, (u_32_t)dport, ifs);
6997176Syx160601
7007176Syx160601 ni->nai_nport = nport;
7017176Syx160601 ni->nai_port = sport;
7027176Syx160601
7037176Syx160601 if (flags & IPN_TCPUDP) {
7047176Syx160601 nat->nat_inport = nport;
7057176Syx160601 nat->nat_outport = dport;
7067176Syx160601 nat->nat_oport = sport;
7077176Syx160601 ((tcphdr_t *)fin->fin_dp)->th_dport = nport;
7087176Syx160601 } else if (flags & IPN_ICMPQUERY) {
7097176Syx160601 ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = nport;
7107176Syx160601 nat->nat_inport = nport;
7117176Syx160601 nat->nat_outport = nport;
7127176Syx160601 }
7137176Syx160601
7147176Syx160601 return move;
7157176Syx160601 }
7167176Syx160601
7177176Syx160601 /* ------------------------------------------------------------------------ */
7187176Syx160601 /* Function: nat6_new */
7197176Syx160601 /* Returns: nat_t* - NULL == failure to create new NAT structure, */
7207176Syx160601 /* else pointer to new NAT structure */
7217176Syx160601 /* Parameters: fin(I) - pointer to packet information */
7227176Syx160601 /* np(I) - pointer to NAT rule */
7237176Syx160601 /* natsave(I) - pointer to where to store NAT struct pointer */
7247176Syx160601 /* flags(I) - flags describing the current packet */
7257176Syx160601 /* direction(I) - direction of packet (in/out) */
7267176Syx160601 /* Write Lock: ipf_nat */
7277176Syx160601 /* */
7287176Syx160601 /* Attempts to create a new NAT entry. Does not actually change the packet */
7297176Syx160601 /* in any way. */
7307176Syx160601 /* */
7317176Syx160601 /* This fucntion is in three main parts: (1) deal with creating a new NAT */
7327176Syx160601 /* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */
7337176Syx160601 /* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */
7347176Syx160601 /* and (3) building that structure and putting it into the NAT table(s). */
7357176Syx160601 /* ------------------------------------------------------------------------ */
nat6_new(fin,np,natsave,flags,direction)7367176Syx160601 nat_t *nat6_new(fin, np, natsave, flags, direction)
7377176Syx160601 fr_info_t *fin;
7387176Syx160601 ipnat_t *np;
7397176Syx160601 nat_t **natsave;
7407176Syx160601 u_int flags;
7417176Syx160601 int direction;
7427176Syx160601 {
7437176Syx160601 tcphdr_t *tcp = NULL;
7447176Syx160601 hostmap_t *hm = NULL;
7457176Syx160601 nat_t *nat, *natl;
7467176Syx160601 u_int nflags;
7477176Syx160601 natinfo_t ni;
7487176Syx160601 int move;
7497176Syx160601 ipf_stack_t *ifs = fin->fin_ifs;
7507176Syx160601
7517176Syx160601 /*
7528170SJohn.Ojemann@Sun.COM * Trigger automatic call to ipf_extraflush() if the
7537176Syx160601 * table has reached capcity specified by hi watermark.
7547176Syx160601 */
7558170SJohn.Ojemann@Sun.COM if (NAT_TAB_WATER_LEVEL(ifs) > ifs->ifs_nat_flush_level_hi)
7567176Syx160601 ifs->ifs_nat_doflush = 1;
7577176Syx160601
7587176Syx160601 if (ifs->ifs_nat_stats.ns_inuse >= ifs->ifs_ipf_nattable_max) {
7597176Syx160601 ifs->ifs_nat_stats.ns_memfail++;
7607176Syx160601 return NULL;
7617176Syx160601 }
7627176Syx160601
7637176Syx160601 move = 1;
7647176Syx160601 nflags = np->in_flags & flags;
7657176Syx160601 nflags &= NAT_FROMRULE;
7667176Syx160601
7677176Syx160601 ni.nai_np = np;
7687176Syx160601 ni.nai_nflags = nflags;
7697176Syx160601 ni.nai_flags = flags;
7707176Syx160601
7717176Syx160601 /* Give me a new nat */
7727176Syx160601 KMALLOC(nat, nat_t *);
7737176Syx160601 if (nat == NULL) {
7747176Syx160601 ifs->ifs_nat_stats.ns_memfail++;
7757176Syx160601 /*
7767176Syx160601 * Try to automatically tune the max # of entries in the
7777176Syx160601 * table allowed to be less than what will cause kmem_alloc()
7787176Syx160601 * to fail and try to eliminate panics due to out of memory
7797176Syx160601 * conditions arising.
7807176Syx160601 */
7817176Syx160601 if (ifs->ifs_ipf_nattable_max > ifs->ifs_ipf_nattable_sz) {
7827176Syx160601 ifs->ifs_ipf_nattable_max =
7837176Syx160601 ifs->ifs_nat_stats.ns_inuse - 100;
7847176Syx160601 printf("ipf_nattable_max reduced to %d\n",
7857176Syx160601 ifs->ifs_ipf_nattable_max);
7867176Syx160601 }
7877176Syx160601 return NULL;
7887176Syx160601 }
7897176Syx160601
7907176Syx160601 if (flags & IPN_TCPUDP) {
7917176Syx160601 tcp = fin->fin_dp;
7927176Syx160601 ni.nai_sport = htons(fin->fin_sport);
7937176Syx160601 ni.nai_dport = htons(fin->fin_dport);
7947176Syx160601 } else if (flags & IPN_ICMPQUERY) {
7957176Syx160601 /*
7967176Syx160601 * In the ICMP query NAT code, we translate the ICMP id fields
7977176Syx160601 * to make them unique. This is indepedent of the ICMP type
7987176Syx160601 * (e.g. in the unlikely event that a host sends an echo and
7997176Syx160601 * an tstamp request with the same id, both packets will have
8007176Syx160601 * their ip address/id field changed in the same way).
8017176Syx160601 *
8027176Syx160601 * The icmp_id field is used by the sender to identify the
8037176Syx160601 * process making the icmp request. (the receiver justs
8047176Syx160601 * copies it back in its response). So, it closely matches
8057176Syx160601 * the concept of source port. We overlay sport, so we can
8067176Syx160601 * maximally reuse the existing code.
8077176Syx160601 */
8087176Syx160601 ni.nai_sport = ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id;
8097176Syx160601 ni.nai_dport = ni.nai_sport;
8107176Syx160601 }
8117176Syx160601
8127176Syx160601 bzero((char *)nat, sizeof (*nat));
8137176Syx160601 nat->nat_flags = flags;
8147176Syx160601 nat->nat_redir = np->in_redir;
8157176Syx160601
8167176Syx160601 if ((flags & NAT_SLAVE) == 0) {
8177176Syx160601 MUTEX_ENTER(&ifs->ifs_ipf_nat_new);
8187176Syx160601 }
8197176Syx160601
8207176Syx160601 /*
8217176Syx160601 * Search the current table for a match.
8227176Syx160601 */
8237176Syx160601 if (direction == NAT_OUTBOUND) {
8247176Syx160601 /*
8257176Syx160601 * We can now arrange to call this for the same connection
8267176Syx160601 * because ipf_nat_new doesn't protect the code path into
8277176Syx160601 * this function.
8287176Syx160601 */
8297176Syx160601 natl = nat6_outlookup(fin, nflags, (u_int)fin->fin_p,
8307176Syx160601 &fin->fin_src6.in6, &fin->fin_dst6.in6);
8317176Syx160601 if (natl != NULL) {
8327176Syx160601 KFREE(nat);
8337176Syx160601 nat = natl;
8347176Syx160601 goto done;
8357176Syx160601 }
8367176Syx160601
8377176Syx160601 move = nat6_newmap(fin, nat, &ni);
8387176Syx160601 if (move == -1)
8397176Syx160601 goto badnat;
8407176Syx160601
8417176Syx160601 np = ni.nai_np;
8427176Syx160601 } else {
8437176Syx160601 /*
8447176Syx160601 * NAT_INBOUND is used only for redirects rules
8457176Syx160601 */
8467176Syx160601 natl = nat6_inlookup(fin, nflags, (u_int)fin->fin_p,
8477176Syx160601 &fin->fin_src6.in6, &fin->fin_dst6.in6);
8487176Syx160601 if (natl != NULL) {
8497176Syx160601 KFREE(nat);
8507176Syx160601 nat = natl;
8517176Syx160601 goto done;
8527176Syx160601 }
8537176Syx160601
8547176Syx160601 move = nat6_newrdr(fin, nat, &ni);
8557176Syx160601 if (move == -1)
8567176Syx160601 goto badnat;
8577176Syx160601
8587176Syx160601 np = ni.nai_np;
8597176Syx160601 }
8607176Syx160601
8617176Syx160601 if ((move == 1) && (np->in_flags & IPN_ROUNDR)) {
8627176Syx160601 if (np->in_redir == NAT_REDIRECT) {
8637176Syx160601 nat_delrdr(np);
8647176Syx160601 nat6_addrdr(np, ifs);
8657176Syx160601 } else if (np->in_redir == NAT_MAP) {
8667176Syx160601 nat_delnat(np);
8677176Syx160601 nat6_addnat(np, ifs);
8687176Syx160601 }
8697176Syx160601 }
8707176Syx160601
8717176Syx160601 if (nat6_finalise(fin, nat, &ni, tcp, natsave, direction) == -1) {
8727176Syx160601 goto badnat;
8737176Syx160601 }
8747176Syx160601
8757176Syx160601 nat_calc_chksum_diffs(nat);
8767176Syx160601
8777176Syx160601 if (flags & SI_WILDP)
8787176Syx160601 ifs->ifs_nat_stats.ns_wilds++;
8797176Syx160601 goto done;
8807176Syx160601 badnat:
8817176Syx160601 ifs->ifs_nat_stats.ns_badnat++;
8827176Syx160601 if ((hm = nat->nat_hm) != NULL)
8837176Syx160601 fr_hostmapdel(&hm);
8847176Syx160601 KFREE(nat);
8857176Syx160601 nat = NULL;
8867176Syx160601 done:
8877176Syx160601 if ((flags & NAT_SLAVE) == 0) {
8887176Syx160601 MUTEX_EXIT(&ifs->ifs_ipf_nat_new);
8897176Syx160601 }
8907176Syx160601 return nat;
8917176Syx160601 }
8927176Syx160601
8937176Syx160601
8947176Syx160601 /* ------------------------------------------------------------------------ */
8957176Syx160601 /* Function: nat6_finalise */
8967176Syx160601 /* Returns: int - 0 == sucess, -1 == failure */
8977176Syx160601 /* Parameters: fin(I) - pointer to packet information */
8987176Syx160601 /* nat(I) - pointer to NAT entry */
8997176Syx160601 /* ni(I) - pointer to structure with misc. information needed */
9007176Syx160601 /* to create new NAT entry. */
9017176Syx160601 /* Write Lock: ipf_nat */
9027176Syx160601 /* */
9037176Syx160601 /* This is the tail end of constructing a new NAT entry and is the same */
9047176Syx160601 /* for both IPv4 and IPv6. */
9057176Syx160601 /* ------------------------------------------------------------------------ */
9067176Syx160601 /*ARGSUSED*/
nat6_finalise(fin,nat,ni,tcp,natsave,direction)9077176Syx160601 static INLINE int nat6_finalise(fin, nat, ni, tcp, natsave, direction)
9087176Syx160601 fr_info_t *fin;
9097176Syx160601 nat_t *nat;
9107176Syx160601 natinfo_t *ni;
9117176Syx160601 tcphdr_t *tcp;
9127176Syx160601 nat_t **natsave;
9137176Syx160601 int direction;
9147176Syx160601 {
9157176Syx160601 frentry_t *fr;
9167176Syx160601 ipnat_t *np;
9177176Syx160601 ipf_stack_t *ifs = fin->fin_ifs;
9187176Syx160601
9197176Syx160601 np = ni->nai_np;
9207176Syx160601
9217176Syx160601 COPYIFNAME(fin->fin_ifp, nat->nat_ifnames[0], fin->fin_v);
9227176Syx160601
9237176Syx160601 #ifdef IPFILTER_SYNC
9247176Syx160601 if ((nat->nat_flags & SI_CLONE) == 0)
9257176Syx160601 nat->nat_sync = ipfsync_new(SMC_NAT, fin, nat);
9267176Syx160601 #endif
9277176Syx160601
9287176Syx160601 nat->nat_me = natsave;
9297176Syx160601 nat->nat_dir = direction;
9307176Syx160601 nat->nat_ifps[0] = np->in_ifps[0];
9317176Syx160601 nat->nat_ifps[1] = np->in_ifps[1];
9327176Syx160601 nat->nat_ptr = np;
9337176Syx160601 nat->nat_p = fin->fin_p;
9347176Syx160601 nat->nat_v = fin->fin_v;
9357176Syx160601 nat->nat_mssclamp = np->in_mssclamp;
9367176Syx160601 fr = fin->fin_fr;
9377176Syx160601 nat->nat_fr = fr;
9387176Syx160601 nat->nat_v = 6;
9397176Syx160601
9407176Syx160601 #ifdef IPF_V6_PROXIES
9417176Syx160601 if ((np->in_apr != NULL) && ((ni->nai_flags & NAT_SLAVE) == 0))
9427176Syx160601 if (appr_new(fin, nat) == -1)
9437176Syx160601 return -1;
9447176Syx160601 #endif
9457176Syx160601
9467176Syx160601 if (nat6_insert(nat, fin->fin_rev, ifs) == 0) {
9477176Syx160601 if (ifs->ifs_nat_logging)
9487176Syx160601 nat_log(nat, (u_int)np->in_redir, ifs);
9497176Syx160601 np->in_use++;
9507176Syx160601 if (fr != NULL) {
9517176Syx160601 MUTEX_ENTER(&fr->fr_lock);
9527176Syx160601 fr->fr_ref++;
9537176Syx160601 MUTEX_EXIT(&fr->fr_lock);
9547176Syx160601 }
9557176Syx160601 return 0;
9567176Syx160601 }
9577176Syx160601
9587176Syx160601 /*
9597176Syx160601 * nat6_insert failed, so cleanup time...
9607176Syx160601 */
9617176Syx160601 return -1;
9627176Syx160601 }
9637176Syx160601
9647176Syx160601
9657176Syx160601 /* ------------------------------------------------------------------------ */
9667176Syx160601 /* Function: nat6_insert */
9677176Syx160601 /* Returns: int - 0 == sucess, -1 == failure */
9687176Syx160601 /* Parameters: nat(I) - pointer to NAT structure */
9697176Syx160601 /* rev(I) - flag indicating forward/reverse direction of packet */
9707176Syx160601 /* Write Lock: ipf_nat */
9717176Syx160601 /* */
9727176Syx160601 /* Insert a NAT entry into the hash tables for searching and add it to the */
9737176Syx160601 /* list of active NAT entries. Adjust global counters when complete. */
9747176Syx160601 /* ------------------------------------------------------------------------ */
nat6_insert(nat,rev,ifs)9757176Syx160601 int nat6_insert(nat, rev, ifs)
9767176Syx160601 nat_t *nat;
9777176Syx160601 int rev;
9787176Syx160601 ipf_stack_t *ifs;
9797176Syx160601 {
9807176Syx160601 u_int hv1, hv2;
9817176Syx160601 nat_t **natp;
9827176Syx160601
9837176Syx160601 /*
9847176Syx160601 * Try and return an error as early as possible, so calculate the hash
9857176Syx160601 * entry numbers first and then proceed.
9867176Syx160601 */
9877176Syx160601 if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) {
9887176Syx160601 hv1 = NAT_HASH_FN6(&nat->nat_inip6, nat->nat_inport,
9897176Syx160601 0xffffffff);
9907176Syx160601 hv1 = NAT_HASH_FN6(&nat->nat_oip6, hv1 + nat->nat_oport,
9917176Syx160601 ifs->ifs_ipf_nattable_sz);
9927176Syx160601 hv2 = NAT_HASH_FN6(&nat->nat_outip6, nat->nat_outport,
9937176Syx160601 0xffffffff);
9947176Syx160601 hv2 = NAT_HASH_FN6(&nat->nat_oip6, hv2 + nat->nat_oport,
9957176Syx160601 ifs->ifs_ipf_nattable_sz);
9967176Syx160601 } else {
9977176Syx160601 hv1 = NAT_HASH_FN6(&nat->nat_inip6, 0, 0xffffffff);
9987176Syx160601 hv1 = NAT_HASH_FN6(&nat->nat_oip6, hv1,
9997176Syx160601 ifs->ifs_ipf_nattable_sz);
10007176Syx160601 hv2 = NAT_HASH_FN6(&nat->nat_outip6, 0, 0xffffffff);
10017176Syx160601 hv2 = NAT_HASH_FN6(&nat->nat_oip6, hv2,
10027176Syx160601 ifs->ifs_ipf_nattable_sz);
10037176Syx160601 }
10047176Syx160601
10057176Syx160601 if ((ifs->ifs_nat_stats.ns_bucketlen[0][hv1] >=
10067176Syx160601 ifs->ifs_fr_nat_maxbucket) ||
10077176Syx160601 (ifs->ifs_nat_stats.ns_bucketlen[1][hv2] >=
10087176Syx160601 ifs->ifs_fr_nat_maxbucket)) {
10097176Syx160601 return -1;
10107176Syx160601 }
10117176Syx160601
10127176Syx160601 nat->nat_hv[0] = hv1;
10137176Syx160601 nat->nat_hv[1] = hv2;
10147176Syx160601
10157176Syx160601 MUTEX_INIT(&nat->nat_lock, "nat entry lock");
10167176Syx160601
10177176Syx160601 nat->nat_rev = rev;
10187176Syx160601 nat->nat_ref = 1;
10197176Syx160601 nat->nat_bytes[0] = 0;
10207176Syx160601 nat->nat_pkts[0] = 0;
10217176Syx160601 nat->nat_bytes[1] = 0;
10227176Syx160601 nat->nat_pkts[1] = 0;
10237176Syx160601
10247176Syx160601 nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0';
10257176Syx160601 nat->nat_ifps[0] = fr_resolvenic(nat->nat_ifnames[0], 6, ifs);
10267176Syx160601
10277176Syx160601 if (nat->nat_ifnames[1][0] !='\0') {
10287176Syx160601 nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
10297176Syx160601 nat->nat_ifps[1] = fr_resolvenic(nat->nat_ifnames[1], 6, ifs);
10307176Syx160601 } else {
10317176Syx160601 (void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0],
10327176Syx160601 LIFNAMSIZ);
10337176Syx160601 nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
10347176Syx160601 nat->nat_ifps[1] = nat->nat_ifps[0];
10357176Syx160601 }
10367176Syx160601
10377176Syx160601 nat->nat_next = ifs->ifs_nat_instances;
10387176Syx160601 nat->nat_pnext = &ifs->ifs_nat_instances;
10397176Syx160601 if (ifs->ifs_nat_instances)
10407176Syx160601 ifs->ifs_nat_instances->nat_pnext = &nat->nat_next;
10417176Syx160601 ifs->ifs_nat_instances = nat;
10427176Syx160601
10437176Syx160601 natp = &ifs->ifs_nat_table[0][hv1];
10447176Syx160601 if (*natp)
10457176Syx160601 (*natp)->nat_phnext[0] = &nat->nat_hnext[0];
10467176Syx160601 nat->nat_phnext[0] = natp;
10477176Syx160601 nat->nat_hnext[0] = *natp;
10487176Syx160601 *natp = nat;
10497176Syx160601 ifs->ifs_nat_stats.ns_bucketlen[0][hv1]++;
10507176Syx160601
10517176Syx160601 natp = &ifs->ifs_nat_table[1][hv2];
10527176Syx160601 if (*natp)
10537176Syx160601 (*natp)->nat_phnext[1] = &nat->nat_hnext[1];
10547176Syx160601 nat->nat_phnext[1] = natp;
10557176Syx160601 nat->nat_hnext[1] = *natp;
10567176Syx160601 *natp = nat;
10577176Syx160601 ifs->ifs_nat_stats.ns_bucketlen[1][hv2]++;
10587176Syx160601
10597176Syx160601 fr_setnatqueue(nat, rev, ifs);
10607176Syx160601
10617176Syx160601 ifs->ifs_nat_stats.ns_added++;
10627176Syx160601 ifs->ifs_nat_stats.ns_inuse++;
10637176Syx160601 return 0;
10647176Syx160601 }
10657176Syx160601
10667176Syx160601
10677176Syx160601 /* ------------------------------------------------------------------------ */
10687176Syx160601 /* Function: nat6_icmperrorlookup */
10697176Syx160601 /* Returns: nat_t* - point to matching NAT structure */
10707176Syx160601 /* Parameters: fin(I) - pointer to packet information */
10717176Syx160601 /* dir(I) - direction of packet (in/out) */
10727176Syx160601 /* */
10737176Syx160601 /* Check if the ICMP error message is related to an existing TCP, UDP or */
10747176Syx160601 /* ICMP query nat entry. It is assumed that the packet is already of the */
10757176Syx160601 /* the required length. */
10767176Syx160601 /* ------------------------------------------------------------------------ */
nat6_icmperrorlookup(fin,dir)10777176Syx160601 nat_t *nat6_icmperrorlookup(fin, dir)
10787176Syx160601 fr_info_t *fin;
10797176Syx160601 int dir;
10807176Syx160601 {
10817176Syx160601 int flags = 0, minlen;
10827176Syx160601 struct icmp6_hdr *orgicmp;
10837176Syx160601 tcphdr_t *tcp = NULL;
10847176Syx160601 u_short data[2];
10857176Syx160601 nat_t *nat;
10867176Syx160601 ip6_t *oip6;
10877176Syx160601 u_int p;
10887176Syx160601
10897176Syx160601 minlen = 40;
10907176Syx160601 /*
10917176Syx160601 * Does it at least have the return (basic) IP header ?
10927176Syx160601 * Only a basic IP header (no options) should be with an ICMP error
10937176Syx160601 * header. Also, if it's not an error type, then return.
10947176Syx160601 */
10957176Syx160601 if (!(fin->fin_flx & FI_ICMPERR))
10967176Syx160601 return NULL;
10977176Syx160601
10987176Syx160601 /*
10997176Syx160601 * Check packet size
11007176Syx160601 */
11017176Syx160601 if (fin->fin_plen < ICMP6ERR_IPICMPHLEN)
11027176Syx160601 return NULL;
11037176Syx160601 oip6 = (ip6_t *)((char *)fin->fin_dp + 8);
11047176Syx160601
11057176Syx160601 /*
11067176Syx160601 * Is the buffer big enough for all of it ? It's the size of the IP
11077176Syx160601 * header claimed in the encapsulated part which is of concern. It
11087176Syx160601 * may be too big to be in this buffer but not so big that it's
11097176Syx160601 * outside the ICMP packet, leading to TCP deref's causing problems.
11107176Syx160601 * This is possible because we don't know how big oip_hl is when we
11117176Syx160601 * do the pullup early in fr_check() and thus can't gaurantee it is
11127176Syx160601 * all here now.
11137176Syx160601 */
11147176Syx160601 #ifdef _KERNEL
11157176Syx160601 {
11167176Syx160601 mb_t *m;
11177176Syx160601
11187176Syx160601 m = fin->fin_m;
11197176Syx160601 # if defined(MENTAT)
11207176Syx160601 if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr)
11217176Syx160601 return NULL;
11227176Syx160601 # else
11237176Syx160601 if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN >
11247176Syx160601 (char *)fin->fin_ip + M_LEN(m))
11257176Syx160601 return NULL;
11267176Syx160601 # endif
11277176Syx160601 }
11287176Syx160601 #endif
11297176Syx160601
11307176Syx160601 if (IP6_NEQ(&fin->fin_dst6, &oip6->ip6_src))
11317176Syx160601 return NULL;
11327176Syx160601
11337176Syx160601 p = oip6->ip6_nxt;
11347176Syx160601 if (p == IPPROTO_TCP)
11357176Syx160601 flags = IPN_TCP;
11367176Syx160601 else if (p == IPPROTO_UDP)
11377176Syx160601 flags = IPN_UDP;
11387176Syx160601 else if (p == IPPROTO_ICMPV6) {
11397176Syx160601 orgicmp = (struct icmp6_hdr *)(oip6 + 1);
11407176Syx160601
11417176Syx160601 /* see if this is related to an ICMP query */
11427176Syx160601 if (nat_icmpquerytype6(orgicmp->icmp6_type)) {
11437176Syx160601 data[0] = fin->fin_data[0];
11447176Syx160601 data[1] = fin->fin_data[1];
11457176Syx160601 fin->fin_data[0] = 0;
11467176Syx160601 fin->fin_data[1] = orgicmp->icmp6_id;
11477176Syx160601
11487176Syx160601 flags = IPN_ICMPERR|IPN_ICMPQUERY;
11497176Syx160601 /*
11507176Syx160601 * NOTE : dir refers to the direction of the original
11517176Syx160601 * ip packet. By definition the icmp error
11527176Syx160601 * message flows in the opposite direction.
11537176Syx160601 */
11547176Syx160601 if (dir == NAT_INBOUND)
11557176Syx160601 nat = nat6_inlookup(fin, flags, p,
11567176Syx160601 &oip6->ip6_dst, &oip6->ip6_src);
11577176Syx160601 else
11587176Syx160601 nat = nat6_outlookup(fin, flags, p,
11597176Syx160601 &oip6->ip6_dst, &oip6->ip6_src);
11607176Syx160601 fin->fin_data[0] = data[0];
11617176Syx160601 fin->fin_data[1] = data[1];
11627176Syx160601 return nat;
11637176Syx160601 }
11647176Syx160601 }
11657176Syx160601
11667176Syx160601 if (flags & IPN_TCPUDP) {
11677176Syx160601 minlen += 8; /* + 64bits of data to get ports */
11687176Syx160601 if (fin->fin_plen < ICMPERR_ICMPHLEN + minlen)
11697176Syx160601 return NULL;
11707176Syx160601
11717176Syx160601 data[0] = fin->fin_data[0];
11727176Syx160601 data[1] = fin->fin_data[1];
11737176Syx160601 tcp = (tcphdr_t *)(oip6 + 1);
11747176Syx160601 fin->fin_data[0] = ntohs(tcp->th_dport);
11757176Syx160601 fin->fin_data[1] = ntohs(tcp->th_sport);
11767176Syx160601
11777176Syx160601 if (dir == NAT_INBOUND) {
11787176Syx160601 nat = nat6_inlookup(fin, flags, p,
11797176Syx160601 &oip6->ip6_dst, &oip6->ip6_src);
11807176Syx160601 } else {
11817176Syx160601 nat = nat6_outlookup(fin, flags, p,
11827176Syx160601 &oip6->ip6_dst, &oip6->ip6_src);
11837176Syx160601 }
11847176Syx160601 fin->fin_data[0] = data[0];
11857176Syx160601 fin->fin_data[1] = data[1];
11867176Syx160601 return nat;
11877176Syx160601 }
11887176Syx160601 if (dir == NAT_INBOUND)
11897176Syx160601 return nat6_inlookup(fin, 0, p, &oip6->ip6_dst, &oip6->ip6_src);
11907176Syx160601 else
11917176Syx160601 return nat6_outlookup(fin, 0, p, &oip6->ip6_dst,
11927176Syx160601 &oip6->ip6_src);
11937176Syx160601 }
11947176Syx160601
11957176Syx160601
11967176Syx160601 /* ------------------------------------------------------------------------ */
11977176Syx160601 /* Function: nat6_icmperror */
11987176Syx160601 /* Returns: nat_t* - point to matching NAT structure */
11997176Syx160601 /* Parameters: fin(I) - pointer to packet information */
12007176Syx160601 /* nflags(I) - NAT flags for this packet */
12017176Syx160601 /* dir(I) - direction of packet (in/out) */
12027176Syx160601 /* */
12037176Syx160601 /* Fix up an ICMP packet which is an error message for an existing NAT */
12047176Syx160601 /* session. This will correct both packet header data and checksums. */
12057176Syx160601 /* */
12067176Syx160601 /* This should *ONLY* be used for incoming ICMP error packets to make sure */
12077176Syx160601 /* a NAT'd ICMP packet gets correctly recognised. */
12087176Syx160601 /* ------------------------------------------------------------------------ */
nat6_icmperror(fin,nflags,dir)12097176Syx160601 nat_t *nat6_icmperror(fin, nflags, dir)
12107176Syx160601 fr_info_t *fin;
12117176Syx160601 u_int *nflags;
12127176Syx160601 int dir;
12137176Syx160601 {
12147176Syx160601 u_32_t sum1, sum2, sumd, psum1, psum2, psumd, sumd1;
12157176Syx160601 i6addr_t in;
12167176Syx160601 struct icmp6_hdr *icmp6, *orgicmp;
12177176Syx160601 int dlen;
12187176Syx160601 udphdr_t *udp;
12197176Syx160601 tcphdr_t *tcp;
12207176Syx160601 nat_t *nat;
12217176Syx160601 ip6_t *oip6;
12227176Syx160601 if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY)))
12237176Syx160601 return NULL;
12247176Syx160601
12257176Syx160601 /*
12267176Syx160601 * nat6_icmperrorlookup() looks up nat entry associated with the
12277176Syx160601 * offending IP packet and returns pointer to the entry, or NULL
12287176Syx160601 * if packet wasn't natted or for `defective' packets.
12297176Syx160601 */
12307176Syx160601
12317176Syx160601 if ((fin->fin_v != 6) || !(nat = nat6_icmperrorlookup(fin, dir)))
12327176Syx160601 return NULL;
12337176Syx160601
12347176Syx160601 sumd1 = 0;
12357176Syx160601 *nflags = IPN_ICMPERR;
12367176Syx160601 icmp6 = fin->fin_dp;
12377176Syx160601 oip6 = (ip6_t *)((char *)icmp6 + sizeof (*icmp6));
12387176Syx160601 udp = (udphdr_t *)(((char *)oip6) + sizeof (*oip6));
12397176Syx160601 tcp = (tcphdr_t *)udp;
12407176Syx160601 dlen = fin->fin_plen - ((char *)udp - (char *)fin->fin_ip);
12417176Syx160601
12427176Syx160601 /*
12437176Syx160601 * Need to adjust ICMP header to include the real IP#'s and
12447176Syx160601 * port #'s. There are three steps required.
12457176Syx160601 *
12467176Syx160601 * Step 1
12477176Syx160601 * No update needed for ip6 header checksum.
12487176Syx160601 *
12497176Syx160601 * Unlike IPv4, we need to update icmp_cksum for IPv6 address
12507176Syx160601 * changes because there's no ip_sum change to cancel it.
12517176Syx160601 */
12527176Syx160601
12537176Syx160601 if (IP6_EQ((i6addr_t *)&oip6->ip6_dst, &nat->nat_oip6)) {
12547176Syx160601 sum1 = LONG_SUM6((i6addr_t *)&oip6->ip6_src);
12557176Syx160601 in = nat->nat_inip6;
12567176Syx160601 oip6->ip6_src = in.in6;
12577176Syx160601 } else {
12587176Syx160601 sum1 = LONG_SUM6((i6addr_t *)&oip6->ip6_dst);
12597176Syx160601 in = nat->nat_outip6;
12607176Syx160601 oip6->ip6_dst = in.in6;
12617176Syx160601 }
12627176Syx160601
12637176Syx160601 sum2 = LONG_SUM6(&in);
12647176Syx160601 CALC_SUMD(sum1, sum2, sumd);
12657176Syx160601
12667176Syx160601 /*
12677176Syx160601 * Step 2
12687176Syx160601 * Perform other adjustments based on protocol of offending packet.
12697176Syx160601 */
12707176Syx160601
12717176Syx160601 switch (oip6->ip6_nxt) {
12727176Syx160601 case IPPROTO_TCP :
12737176Syx160601 case IPPROTO_UDP :
12747176Syx160601
12757176Syx160601 /*
12767176Syx160601 * For offending TCP/UDP IP packets, translate the ports
12777176Syx160601 * based on the NAT specification.
12787176Syx160601 *
12797176Syx160601 * Advance notice : Now it becomes complicated :-)
12807176Syx160601 *
12817176Syx160601 * Since the port and IP addresse fields are both part
12827176Syx160601 * of the TCP/UDP checksum of the offending IP packet,
12837176Syx160601 * we need to adjust that checksum as well.
12847176Syx160601 *
12857176Syx160601 * To further complicate things, the TCP/UDP checksum
12867176Syx160601 * may not be present. We must check to see if the
12877176Syx160601 * length of the data portion is big enough to hold
12887176Syx160601 * the checksum. In the UDP case, a test to determine
12897176Syx160601 * if the checksum is even set is also required.
12907176Syx160601 *
12917176Syx160601 * Any changes to an IP address, port or checksum within
12927176Syx160601 * the ICMP packet requires a change to icmp_cksum.
12937176Syx160601 *
12947176Syx160601 * Be extremely careful here ... The change is dependent
12957176Syx160601 * upon whether or not the TCP/UPD checksum is present.
12967176Syx160601 *
12977176Syx160601 * If TCP/UPD checksum is present, the icmp_cksum must
12987176Syx160601 * compensate for checksum modification resulting from
12997176Syx160601 * IP address change only. Port change and resulting
13007176Syx160601 * data checksum adjustments cancel each other out.
13017176Syx160601 *
13027176Syx160601 * If TCP/UDP checksum is not present, icmp_cksum must
13037176Syx160601 * compensate for port change only. The IP address
13047176Syx160601 * change does not modify anything else in this case.
13057176Syx160601 */
13067176Syx160601
13077176Syx160601 psum1 = 0;
13087176Syx160601 psum2 = 0;
13097176Syx160601 psumd = 0;
13107176Syx160601
13117176Syx160601 if ((tcp->th_dport == nat->nat_oport) &&
13127176Syx160601 (tcp->th_sport != nat->nat_inport)) {
13137176Syx160601
13147176Syx160601 /*
13157176Syx160601 * Translate the source port.
13167176Syx160601 */
13177176Syx160601
13187176Syx160601 psum1 = ntohs(tcp->th_sport);
13197176Syx160601 psum2 = ntohs(nat->nat_inport);
13207176Syx160601 tcp->th_sport = nat->nat_inport;
13217176Syx160601
13227176Syx160601 } else if ((tcp->th_sport == nat->nat_oport) &&
13237176Syx160601 (tcp->th_dport != nat->nat_outport)) {
13247176Syx160601
13257176Syx160601 /*
13267176Syx160601 * Translate the destination port.
13277176Syx160601 */
13287176Syx160601
13297176Syx160601 psum1 = ntohs(tcp->th_dport);
13307176Syx160601 psum2 = ntohs(nat->nat_outport);
13317176Syx160601 tcp->th_dport = nat->nat_outport;
13327176Syx160601 }
13337176Syx160601
13347176Syx160601 if ((oip6->ip6_nxt == IPPROTO_TCP) && (dlen >= 18)) {
13357176Syx160601
13367176Syx160601 /*
13377176Syx160601 * TCP checksum present.
13387176Syx160601 *
13397176Syx160601 * Adjust data checksum and icmp checksum to
13407176Syx160601 * compensate for any IP address change.
13417176Syx160601 */
13427176Syx160601
13437176Syx160601 sum1 = ntohs(tcp->th_sum);
13447176Syx160601 fix_datacksum(&tcp->th_sum, sumd);
13457176Syx160601 sum2 = ntohs(tcp->th_sum);
13467176Syx160601 CALC_SUMD(sum1, sum2, sumd);
13477176Syx160601 sumd1 += sumd;
13487176Syx160601
13497176Syx160601 /*
13507176Syx160601 * Also make data checksum adjustment to
13517176Syx160601 * compensate for any port change.
13527176Syx160601 */
13537176Syx160601
13547176Syx160601 if (psum1 != psum2) {
13557176Syx160601 CALC_SUMD(psum1, psum2, psumd);
13567176Syx160601 fix_datacksum(&tcp->th_sum, psumd);
13577176Syx160601 }
13587176Syx160601
13597176Syx160601 } else if ((oip6->ip6_nxt == IPPROTO_UDP) &&
13607176Syx160601 (dlen >= 8) && (udp->uh_sum != 0)) {
13617176Syx160601
13627176Syx160601 /*
13637176Syx160601 * The UDP checksum is present and set.
13647176Syx160601 *
13657176Syx160601 * Adjust data checksum and icmp checksum to
13667176Syx160601 * compensate for any IP address change.
13677176Syx160601 */
13687176Syx160601
13697176Syx160601 sum1 = ntohs(udp->uh_sum);
13707176Syx160601 fix_datacksum(&udp->uh_sum, sumd);
13717176Syx160601 sum2 = ntohs(udp->uh_sum);
13727176Syx160601 CALC_SUMD(sum1, sum2, sumd);
13737176Syx160601 sumd1 += sumd;
13747176Syx160601
13757176Syx160601 /*
13767176Syx160601 * Also make data checksum adjustment to
13777176Syx160601 * compensate for any port change.
13787176Syx160601 */
13797176Syx160601
13807176Syx160601 if (psum1 != psum2) {
13817176Syx160601 CALC_SUMD(psum1, psum2, psumd);
13827176Syx160601 fix_datacksum(&udp->uh_sum, psumd);
13837176Syx160601 }
13847176Syx160601
13857176Syx160601 } else {
13867176Syx160601
13877176Syx160601 /*
13887176Syx160601 * Data checksum was not present.
13897176Syx160601 *
13907176Syx160601 * Compensate for any port change.
13917176Syx160601 */
13927176Syx160601
13937176Syx160601 CALC_SUMD(psum2, psum1, psumd);
13947176Syx160601 sumd1 += psumd;
13957176Syx160601 }
13967176Syx160601 break;
13977176Syx160601
13987176Syx160601 case IPPROTO_ICMPV6 :
13997176Syx160601
14007176Syx160601 orgicmp = (struct icmp6_hdr *)udp;
14017176Syx160601
14027176Syx160601 if ((nat->nat_dir == NAT_OUTBOUND) &&
14037176Syx160601 (orgicmp->icmp6_id != nat->nat_inport) &&
14047176Syx160601 (dlen >= 8)) {
14057176Syx160601
14067176Syx160601 /*
14077176Syx160601 * Fix ICMP checksum (of the offening ICMP
14087176Syx160601 * query packet) to compensate the change
14097176Syx160601 * in the ICMP id of the offending ICMP
14107176Syx160601 * packet.
14117176Syx160601 *
14127176Syx160601 * Since you modify orgicmp->icmp_id with
14137176Syx160601 * a delta (say x) and you compensate that
14147176Syx160601 * in origicmp->icmp_cksum with a delta
14157176Syx160601 * minus x, you don't have to adjust the
14167176Syx160601 * overall icmp->icmp_cksum
14177176Syx160601 */
14187176Syx160601
14197176Syx160601 sum1 = ntohs(orgicmp->icmp6_id);
14207176Syx160601 sum2 = ntohs(nat->nat_inport);
14217176Syx160601 CALC_SUMD(sum1, sum2, sumd);
14227176Syx160601 orgicmp->icmp6_id = nat->nat_inport;
14237176Syx160601 fix_datacksum(&orgicmp->icmp6_cksum, sumd);
14247176Syx160601
14257176Syx160601 } /* nat_dir can't be NAT_INBOUND for icmp queries */
14267176Syx160601
14277176Syx160601 break;
14287176Syx160601
14297176Syx160601 default :
14307176Syx160601
14317176Syx160601 break;
14327176Syx160601
14337176Syx160601 } /* switch (oip6->ip6_nxt) */
14347176Syx160601
14357176Syx160601 /*
14367176Syx160601 * Step 3
14377176Syx160601 * Make the adjustments to icmp checksum.
14387176Syx160601 */
14397176Syx160601
14407176Syx160601 if (sumd1 != 0) {
14417176Syx160601 sumd1 = (sumd1 & 0xffff) + (sumd1 >> 16);
14427176Syx160601 sumd1 = (sumd1 & 0xffff) + (sumd1 >> 16);
14437176Syx160601 fix_incksum(&icmp6->icmp6_cksum, sumd1);
14447176Syx160601 }
14457176Syx160601 return nat;
14467176Syx160601 }
14477176Syx160601
14487176Syx160601
14497176Syx160601 /*
14507176Syx160601 * NB: these lookups don't lock access to the list, it assumed that it has
14517176Syx160601 * already been done!
14527176Syx160601 */
14537176Syx160601
14547176Syx160601 /* ------------------------------------------------------------------------ */
14557176Syx160601 /* Function: nat6_inlookup */
14567176Syx160601 /* Returns: nat_t* - NULL == no match, */
14577176Syx160601 /* else pointer to matching NAT entry */
14587176Syx160601 /* Parameters: fin(I) - pointer to packet information */
14597176Syx160601 /* flags(I) - NAT flags for this packet */
14607176Syx160601 /* p(I) - protocol for this packet */
14617176Syx160601 /* src(I) - source IP address */
14627176Syx160601 /* mapdst(I) - destination IP address */
14637176Syx160601 /* */
14647176Syx160601 /* Lookup a nat entry based on the mapped destination ip address/port and */
14657176Syx160601 /* real source address/port. We use this lookup when receiving a packet, */
14667176Syx160601 /* we're looking for a table entry, based on the destination address. */
14677176Syx160601 /* */
14687176Syx160601 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */
14697176Syx160601 /* */
14707176Syx160601 /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */
14717176Syx160601 /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */
14727176Syx160601 /* */
14737176Syx160601 /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */
14747176Syx160601 /* the packet is of said protocol */
14757176Syx160601 /* ------------------------------------------------------------------------ */
nat6_inlookup(fin,flags,p,src,mapdst)14767176Syx160601 nat_t *nat6_inlookup(fin, flags, p, src, mapdst)
14777176Syx160601 fr_info_t *fin;
14787176Syx160601 u_int flags, p;
14797176Syx160601 struct in6_addr *src, *mapdst;
14807176Syx160601 {
14817176Syx160601 u_short sport, dport;
14827176Syx160601 u_int sflags;
14837176Syx160601 nat_t *nat;
14847176Syx160601 int nflags;
14857176Syx160601 i6addr_t dst;
14867176Syx160601 void *ifp;
14877176Syx160601 u_int hv;
14887176Syx160601 ipf_stack_t *ifs = fin->fin_ifs;
14897176Syx160601
14907176Syx160601 if (fin != NULL)
14917176Syx160601 ifp = fin->fin_ifp;
14927176Syx160601 else
14937176Syx160601 ifp = NULL;
14947176Syx160601 sport = 0;
14957176Syx160601 dport = 0;
14967176Syx160601 dst.in6 = *mapdst;
14977176Syx160601 sflags = flags & NAT_TCPUDPICMP;
14987176Syx160601
14997176Syx160601 switch (p)
15007176Syx160601 {
15017176Syx160601 case IPPROTO_TCP :
15027176Syx160601 case IPPROTO_UDP :
15037176Syx160601 sport = htons(fin->fin_data[0]);
15047176Syx160601 dport = htons(fin->fin_data[1]);
15057176Syx160601 break;
15067176Syx160601 case IPPROTO_ICMPV6 :
15077176Syx160601 if (flags & IPN_ICMPERR)
15087176Syx160601 sport = fin->fin_data[1];
15097176Syx160601 else
15107176Syx160601 dport = fin->fin_data[1];
15117176Syx160601 break;
15127176Syx160601 default :
15137176Syx160601 break;
15147176Syx160601 }
15157176Syx160601
15167176Syx160601
15177176Syx160601 if ((flags & SI_WILDP) != 0)
15187176Syx160601 goto find_in_wild_ports;
15197176Syx160601
15207176Syx160601 hv = NAT_HASH_FN6(&dst, dport, 0xffffffff);
15217176Syx160601 hv = NAT_HASH_FN6(src, hv + sport, ifs->ifs_ipf_nattable_sz);
15227176Syx160601 nat = ifs->ifs_nat_table[1][hv];
15237176Syx160601 for (; nat; nat = nat->nat_hnext[1]) {
15247176Syx160601 if (nat->nat_v != 6)
15257176Syx160601 continue;
15267176Syx160601
15277176Syx160601 if (nat->nat_ifps[0] != NULL) {
15287176Syx160601 if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
15297176Syx160601 continue;
15307176Syx160601 } else if (ifp != NULL)
15317176Syx160601 nat->nat_ifps[0] = ifp;
15327176Syx160601
15337176Syx160601 nflags = nat->nat_flags;
15347176Syx160601
15357176Syx160601 if (IP6_EQ(&nat->nat_oip6, src) &&
15367176Syx160601 IP6_EQ(&nat->nat_outip6, &dst) &&
15377176Syx160601 (((p == 0) &&
15387176Syx160601 (sflags == (nat->nat_flags & IPN_TCPUDPICMP))) ||
15397176Syx160601 (p == nat->nat_p))) {
15407176Syx160601 switch (p)
15417176Syx160601 {
15427176Syx160601 #if 0
15437176Syx160601 case IPPROTO_GRE :
15447176Syx160601 if (nat->nat_call[1] != fin->fin_data[0])
15457176Syx160601 continue;
15467176Syx160601 break;
15477176Syx160601 #endif
15487176Syx160601 case IPPROTO_ICMPV6 :
15497176Syx160601 if ((flags & IPN_ICMPERR) != 0) {
15507176Syx160601 if (nat->nat_outport != sport)
15517176Syx160601 continue;
15527176Syx160601 } else {
15537176Syx160601 if (nat->nat_outport != dport)
15547176Syx160601 continue;
15557176Syx160601 }
15567176Syx160601 break;
15577176Syx160601 case IPPROTO_TCP :
15587176Syx160601 case IPPROTO_UDP :
15597176Syx160601 if (nat->nat_oport != sport)
15607176Syx160601 continue;
15617176Syx160601 if (nat->nat_outport != dport)
15627176Syx160601 continue;
15637176Syx160601 break;
15647176Syx160601 default :
15657176Syx160601 break;
15667176Syx160601 }
15677176Syx160601
15687176Syx160601 #ifdef IPF_V6_PROXIES
15697176Syx160601 ipn = nat->nat_ptr;
15707176Syx160601 if ((ipn != NULL) && (nat->nat_aps != NULL))
15717176Syx160601 if (appr_match(fin, nat) != 0)
15727176Syx160601 continue;
15737176Syx160601 #endif
15747176Syx160601 return nat;
15757176Syx160601 }
15767176Syx160601 }
15777176Syx160601
15787176Syx160601 /*
15797176Syx160601 * So if we didn't find it but there are wildcard members in the hash
15807176Syx160601 * table, go back and look for them. We do this search and update here
15817176Syx160601 * because it is modifying the NAT table and we want to do this only
15827176Syx160601 * for the first packet that matches. The exception, of course, is
15837176Syx160601 * for "dummy" (FI_IGNORE) lookups.
15847176Syx160601 */
15857176Syx160601 find_in_wild_ports:
15867176Syx160601 if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH))
15877176Syx160601 return NULL;
15887176Syx160601 if (ifs->ifs_nat_stats.ns_wilds == 0)
15897176Syx160601 return NULL;
15907176Syx160601
15917176Syx160601 RWLOCK_EXIT(&ifs->ifs_ipf_nat);
15927176Syx160601
15937176Syx160601 hv = NAT_HASH_FN6(&dst, 0, 0xffffffff);
15947176Syx160601 hv = NAT_HASH_FN6(src, hv, ifs->ifs_ipf_nattable_sz);
15957176Syx160601
15967176Syx160601 WRITE_ENTER(&ifs->ifs_ipf_nat);
15977176Syx160601
15987176Syx160601 nat = ifs->ifs_nat_table[1][hv];
15997176Syx160601 for (; nat; nat = nat->nat_hnext[1]) {
16007176Syx160601 if (nat->nat_v != 6)
16017176Syx160601 continue;
16027176Syx160601
16037176Syx160601 if (nat->nat_ifps[0] != NULL) {
16047176Syx160601 if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
16057176Syx160601 continue;
16067176Syx160601 } else if (ifp != NULL)
16077176Syx160601 nat->nat_ifps[0] = ifp;
16087176Syx160601
16097176Syx160601 if (nat->nat_p != fin->fin_p)
16107176Syx160601 continue;
16117176Syx160601 if (IP6_NEQ(&nat->nat_oip6, src) ||
16127176Syx160601 IP6_NEQ(&nat->nat_outip6, &dst))
16137176Syx160601 continue;
16147176Syx160601
16157176Syx160601 nflags = nat->nat_flags;
16167176Syx160601 if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
16177176Syx160601 continue;
16187176Syx160601
16197176Syx160601 if (nat_wildok(nat, (int)sport, (int)dport, nflags,
16207176Syx160601 NAT_INBOUND) == 1) {
16217176Syx160601 if ((fin->fin_flx & FI_IGNORE) != 0)
16227176Syx160601 break;
16237176Syx160601 if ((nflags & SI_CLONE) != 0) {
16247176Syx160601 nat = fr_natclone(fin, nat);
16257176Syx160601 if (nat == NULL)
16267176Syx160601 break;
16277176Syx160601 } else {
16287176Syx160601 MUTEX_ENTER(&ifs->ifs_ipf_nat_new);
16297176Syx160601 ifs->ifs_nat_stats.ns_wilds--;
16307176Syx160601 MUTEX_EXIT(&ifs->ifs_ipf_nat_new);
16317176Syx160601 }
16327176Syx160601 nat->nat_oport = sport;
16337176Syx160601 nat->nat_outport = dport;
16347176Syx160601 nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
16357176Syx160601 nat6_tabmove(nat, ifs);
16367176Syx160601 break;
16377176Syx160601 }
16387176Syx160601 }
16397176Syx160601
16407176Syx160601 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
16417176Syx160601
16427176Syx160601 return nat;
16437176Syx160601 }
16447176Syx160601
16457176Syx160601
16467176Syx160601 /* ------------------------------------------------------------------------ */
16477176Syx160601 /* Function: nat6_tabmove */
16487176Syx160601 /* Returns: Nil */
16497176Syx160601 /* Parameters: nat(I) - pointer to NAT structure */
16507176Syx160601 /* Write Lock: ipf_nat */
16517176Syx160601 /* */
16527176Syx160601 /* This function is only called for TCP/UDP NAT table entries where the */
16537176Syx160601 /* original was placed in the table without hashing on the ports and we now */
16547176Syx160601 /* want to include hashing on port numbers. */
16557176Syx160601 /* ------------------------------------------------------------------------ */
nat6_tabmove(nat,ifs)16567176Syx160601 static void nat6_tabmove(nat, ifs)
16577176Syx160601 nat_t *nat;
16587176Syx160601 ipf_stack_t *ifs;
16597176Syx160601 {
16607176Syx160601 nat_t **natp;
16617176Syx160601 u_int hv;
16627176Syx160601
16637176Syx160601 if (nat->nat_flags & SI_CLONE)
16647176Syx160601 return;
16657176Syx160601
16667176Syx160601 /*
16677176Syx160601 * Remove the NAT entry from the old location
16687176Syx160601 */
16697176Syx160601 if (nat->nat_hnext[0])
16707176Syx160601 nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
16717176Syx160601 *nat->nat_phnext[0] = nat->nat_hnext[0];
16727176Syx160601 ifs->ifs_nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--;
16737176Syx160601
16747176Syx160601 if (nat->nat_hnext[1])
16757176Syx160601 nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
16767176Syx160601 *nat->nat_phnext[1] = nat->nat_hnext[1];
16777176Syx160601 ifs->ifs_nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--;
16787176Syx160601
16797176Syx160601 /*
16807176Syx160601 * Add into the NAT table in the new position
16817176Syx160601 */
16827176Syx160601 hv = NAT_HASH_FN6(&nat->nat_inip6, nat->nat_inport, 0xffffffff);
16837176Syx160601 hv = NAT_HASH_FN6(&nat->nat_oip6, hv + nat->nat_oport,
16847176Syx160601 ifs->ifs_ipf_nattable_sz);
16857176Syx160601 nat->nat_hv[0] = hv;
16867176Syx160601 natp = &ifs->ifs_nat_table[0][hv];
16877176Syx160601 if (*natp)
16887176Syx160601 (*natp)->nat_phnext[0] = &nat->nat_hnext[0];
16897176Syx160601 nat->nat_phnext[0] = natp;
16907176Syx160601 nat->nat_hnext[0] = *natp;
16917176Syx160601 *natp = nat;
16927176Syx160601 ifs->ifs_nat_stats.ns_bucketlen[0][hv]++;
16937176Syx160601
16947176Syx160601 hv = NAT_HASH_FN6(&nat->nat_outip6, nat->nat_outport, 0xffffffff);
16957176Syx160601 hv = NAT_HASH_FN6(&nat->nat_oip6, hv + nat->nat_oport,
16967176Syx160601 ifs->ifs_ipf_nattable_sz);
16977176Syx160601 nat->nat_hv[1] = hv;
16987176Syx160601 natp = &ifs->ifs_nat_table[1][hv];
16997176Syx160601 if (*natp)
17007176Syx160601 (*natp)->nat_phnext[1] = &nat->nat_hnext[1];
17017176Syx160601 nat->nat_phnext[1] = natp;
17027176Syx160601 nat->nat_hnext[1] = *natp;
17037176Syx160601 *natp = nat;
17047176Syx160601 ifs->ifs_nat_stats.ns_bucketlen[1][hv]++;
17057176Syx160601 }
17067176Syx160601
17077176Syx160601
17087176Syx160601 /* ------------------------------------------------------------------------ */
17097176Syx160601 /* Function: nat6_outlookup */
17107176Syx160601 /* Returns: nat_t* - NULL == no match, */
17117176Syx160601 /* else pointer to matching NAT entry */
17127176Syx160601 /* Parameters: fin(I) - pointer to packet information */
17137176Syx160601 /* flags(I) - NAT flags for this packet */
17147176Syx160601 /* p(I) - protocol for this packet */
17157176Syx160601 /* src(I) - source IP address */
17167176Syx160601 /* dst(I) - destination IP address */
17177176Syx160601 /* rw(I) - 1 == write lock on ipf_nat held, 0 == read lock. */
17187176Syx160601 /* */
17197176Syx160601 /* Lookup a nat entry based on the source 'real' ip address/port and */
17207176Syx160601 /* destination address/port. We use this lookup when sending a packet out, */
17217176Syx160601 /* we're looking for a table entry, based on the source address. */
17227176Syx160601 /* */
17237176Syx160601 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */
17247176Syx160601 /* */
17257176Syx160601 /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */
17267176Syx160601 /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */
17277176Syx160601 /* */
17287176Syx160601 /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */
17297176Syx160601 /* the packet is of said protocol */
17307176Syx160601 /* ------------------------------------------------------------------------ */
nat6_outlookup(fin,flags,p,src,dst)17317176Syx160601 nat_t *nat6_outlookup(fin, flags, p, src, dst)
17327176Syx160601 fr_info_t *fin;
17337176Syx160601 u_int flags, p;
17347176Syx160601 struct in6_addr *src , *dst;
17357176Syx160601 {
17367176Syx160601 u_short sport, dport;
17377176Syx160601 u_int sflags;
17387176Syx160601 nat_t *nat;
17397176Syx160601 int nflags;
17407176Syx160601 void *ifp;
17417176Syx160601 u_int hv;
17427176Syx160601 ipf_stack_t *ifs = fin->fin_ifs;
17437176Syx160601
17447176Syx160601 ifp = fin->fin_ifp;
17457176Syx160601
17467176Syx160601 sflags = flags & IPN_TCPUDPICMP;
17477176Syx160601 sport = 0;
17487176Syx160601 dport = 0;
17497176Syx160601
17507176Syx160601 switch (p)
17517176Syx160601 {
17527176Syx160601 case IPPROTO_TCP :
17537176Syx160601 case IPPROTO_UDP :
17547176Syx160601 sport = htons(fin->fin_data[0]);
17557176Syx160601 dport = htons(fin->fin_data[1]);
17567176Syx160601 break;
17577176Syx160601 case IPPROTO_ICMPV6 :
17587176Syx160601 if (flags & IPN_ICMPERR)
17597176Syx160601 sport = fin->fin_data[1];
17607176Syx160601 else
17617176Syx160601 dport = fin->fin_data[1];
17627176Syx160601 break;
17637176Syx160601 default :
17647176Syx160601 break;
17657176Syx160601 }
17667176Syx160601
17677176Syx160601 if ((flags & SI_WILDP) != 0)
17687176Syx160601 goto find_out_wild_ports;
17697176Syx160601
17707176Syx160601 hv = NAT_HASH_FN6(src, sport, 0xffffffff);
17717176Syx160601 hv = NAT_HASH_FN6(dst, hv + dport, ifs->ifs_ipf_nattable_sz);
17727176Syx160601 nat = ifs->ifs_nat_table[0][hv];
17737176Syx160601 for (; nat; nat = nat->nat_hnext[0]) {
17747176Syx160601 if (nat->nat_v != 6)
17757176Syx160601 continue;
17767176Syx160601
17777176Syx160601 if (nat->nat_ifps[1] != NULL) {
17787176Syx160601 if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
17797176Syx160601 continue;
17807176Syx160601 } else if (ifp != NULL)
17817176Syx160601 nat->nat_ifps[1] = ifp;
17827176Syx160601
17837176Syx160601 nflags = nat->nat_flags;
17847176Syx160601
17857176Syx160601 if (IP6_EQ(&nat->nat_inip6, src) &&
17867176Syx160601 IP6_EQ(&nat->nat_oip6, dst) &&
17877176Syx160601 (((p == 0) && (sflags == (nflags & NAT_TCPUDPICMP))) ||
17887176Syx160601 (p == nat->nat_p))) {
17897176Syx160601 switch (p)
17907176Syx160601 {
17917176Syx160601 #if 0
17927176Syx160601 case IPPROTO_GRE :
17937176Syx160601 if (nat->nat_call[1] != fin->fin_data[0])
17947176Syx160601 continue;
17957176Syx160601 break;
17967176Syx160601 #endif
17977176Syx160601 case IPPROTO_TCP :
17987176Syx160601 case IPPROTO_UDP :
17997176Syx160601 if (nat->nat_oport != dport)
18007176Syx160601 continue;
18017176Syx160601 if (nat->nat_inport != sport)
18027176Syx160601 continue;
18037176Syx160601 break;
18047176Syx160601 default :
18057176Syx160601 break;
18067176Syx160601 }
18077176Syx160601
18087176Syx160601 #ifdef IPF_V6_PROXIES
18097176Syx160601 ipn = nat->nat_ptr;
18107176Syx160601 if ((ipn != NULL) && (nat->nat_aps != NULL))
18117176Syx160601 if (appr_match(fin, nat) != 0)
18127176Syx160601 continue;
18137176Syx160601 #endif
18147176Syx160601 return nat;
18157176Syx160601 }
18167176Syx160601 }
18177176Syx160601
18187176Syx160601 /*
18197176Syx160601 * So if we didn't find it but there are wildcard members in the hash
18207176Syx160601 * table, go back and look for them. We do this search and update here
18217176Syx160601 * because it is modifying the NAT table and we want to do this only
18227176Syx160601 * for the first packet that matches. The exception, of course, is
18237176Syx160601 * for "dummy" (FI_IGNORE) lookups.
18247176Syx160601 */
18257176Syx160601 find_out_wild_ports:
18267176Syx160601 if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH))
18277176Syx160601 return NULL;
18287176Syx160601 if (ifs->ifs_nat_stats.ns_wilds == 0)
18297176Syx160601 return NULL;
18307176Syx160601
18317176Syx160601 RWLOCK_EXIT(&ifs->ifs_ipf_nat);
18327176Syx160601
18337176Syx160601 hv = NAT_HASH_FN6(src, 0, 0xffffffff);
18347176Syx160601 hv = NAT_HASH_FN6(dst, hv, ifs->ifs_ipf_nattable_sz);
18357176Syx160601
18367176Syx160601 WRITE_ENTER(&ifs->ifs_ipf_nat);
18377176Syx160601
18387176Syx160601 nat = ifs->ifs_nat_table[0][hv];
18397176Syx160601 for (; nat; nat = nat->nat_hnext[0]) {
18407176Syx160601 if (nat->nat_v != 6)
18417176Syx160601 continue;
18427176Syx160601
18437176Syx160601 if (nat->nat_ifps[1] != NULL) {
18447176Syx160601 if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
18457176Syx160601 continue;
18467176Syx160601 } else if (ifp != NULL)
18477176Syx160601 nat->nat_ifps[1] = ifp;
18487176Syx160601
18497176Syx160601 if (nat->nat_p != fin->fin_p)
18507176Syx160601 continue;
18517176Syx160601 if (IP6_NEQ(&nat->nat_inip6, src) ||
18527176Syx160601 IP6_NEQ(&nat->nat_oip6, dst))
18537176Syx160601 continue;
18547176Syx160601
18557176Syx160601 nflags = nat->nat_flags;
18567176Syx160601 if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
18577176Syx160601 continue;
18587176Syx160601
18597176Syx160601 if (nat_wildok(nat, (int)sport, (int)dport, nflags,
18607176Syx160601 NAT_OUTBOUND) == 1) {
18617176Syx160601 if ((fin->fin_flx & FI_IGNORE) != 0)
18627176Syx160601 break;
18637176Syx160601 if ((nflags & SI_CLONE) != 0) {
18647176Syx160601 nat = fr_natclone(fin, nat);
18657176Syx160601 if (nat == NULL)
18667176Syx160601 break;
18677176Syx160601 } else {
18687176Syx160601 MUTEX_ENTER(&ifs->ifs_ipf_nat_new);
18697176Syx160601 ifs->ifs_nat_stats.ns_wilds--;
18707176Syx160601 MUTEX_EXIT(&ifs->ifs_ipf_nat_new);
18717176Syx160601 }
18727176Syx160601 nat->nat_inport = sport;
18737176Syx160601 nat->nat_oport = dport;
18747176Syx160601 if (nat->nat_outport == 0)
18757176Syx160601 nat->nat_outport = sport;
18767176Syx160601 nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
18777176Syx160601 nat6_tabmove(nat, ifs);
18787176Syx160601 break;
18797176Syx160601 }
18807176Syx160601 }
18817176Syx160601
18827176Syx160601 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
18837176Syx160601
18847176Syx160601 return nat;
18857176Syx160601 }
18867176Syx160601
18877176Syx160601
18887176Syx160601 /* ------------------------------------------------------------------------ */
18897176Syx160601 /* Function: nat6_lookupredir */
18907176Syx160601 /* Returns: nat_t* - NULL == no match, */
18917176Syx160601 /* else pointer to matching NAT entry */
18927176Syx160601 /* Parameters: np(I) - pointer to description of packet to find NAT table */
18937176Syx160601 /* entry for. */
18947176Syx160601 /* */
18957176Syx160601 /* Lookup the NAT tables to search for a matching redirect */
18967176Syx160601 /* ------------------------------------------------------------------------ */
nat6_lookupredir(np,ifs)18977176Syx160601 nat_t *nat6_lookupredir(np, ifs)
18987176Syx160601 natlookup_t *np;
18997176Syx160601 ipf_stack_t *ifs;
19007176Syx160601 {
19017176Syx160601 fr_info_t fi;
19027176Syx160601 nat_t *nat;
19037176Syx160601
19047176Syx160601 bzero((char *)&fi, sizeof (fi));
19057176Syx160601 if (np->nl_flags & IPN_IN) {
19067176Syx160601 fi.fin_data[0] = ntohs(np->nl_realport);
19077176Syx160601 fi.fin_data[1] = ntohs(np->nl_outport);
19087176Syx160601 } else {
19097176Syx160601 fi.fin_data[0] = ntohs(np->nl_inport);
19107176Syx160601 fi.fin_data[1] = ntohs(np->nl_outport);
19117176Syx160601 }
19127176Syx160601 if (np->nl_flags & IPN_TCP)
19137176Syx160601 fi.fin_p = IPPROTO_TCP;
19147176Syx160601 else if (np->nl_flags & IPN_UDP)
19157176Syx160601 fi.fin_p = IPPROTO_UDP;
19167176Syx160601 else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY))
19177176Syx160601 fi.fin_p = IPPROTO_ICMPV6;
19187176Syx160601
19197176Syx160601 fi.fin_ifs = ifs;
19207176Syx160601 /*
19217176Syx160601 * We can do two sorts of lookups:
19227176Syx160601 * - IPN_IN: we have the `real' and `out' address, look for `in'.
19237176Syx160601 * - default: we have the `in' and `out' address, look for `real'.
19247176Syx160601 */
19257176Syx160601 if (np->nl_flags & IPN_IN) {
19267176Syx160601 if ((nat = nat6_inlookup(&fi, np->nl_flags, fi.fin_p,
19277176Syx160601 &np->nl_realip6, &np->nl_outip6))) {
19287176Syx160601 np->nl_inipaddr = nat->nat_inip6;
19297176Syx160601 np->nl_inport = nat->nat_inport;
19307176Syx160601 }
19317176Syx160601 } else {
19327176Syx160601 /*
19337176Syx160601 * If nl_inip is non null, this is a lookup based on the real
19347176Syx160601 * ip address. Else, we use the fake.
19357176Syx160601 */
19367176Syx160601 if ((nat = nat6_outlookup(&fi, np->nl_flags, fi.fin_p,
19377176Syx160601 &np->nl_inip6, &np->nl_outip6))) {
19387176Syx160601 if ((np->nl_flags & IPN_FINDFORWARD) != 0) {
19397176Syx160601 fr_info_t fin;
19407176Syx160601 bzero((char *)&fin, sizeof (fin));
19417176Syx160601 fin.fin_p = nat->nat_p;
19427176Syx160601 fin.fin_data[0] = ntohs(nat->nat_outport);
19437176Syx160601 fin.fin_data[1] = ntohs(nat->nat_oport);
19447176Syx160601 fin.fin_ifs = ifs;
19457176Syx160601 if (nat6_inlookup(&fin, np->nl_flags, fin.fin_p,
19467176Syx160601 &nat->nat_outip6.in6,
19477176Syx160601 &nat->nat_oip6.in6) != NULL) {
19487176Syx160601 np->nl_flags &= ~IPN_FINDFORWARD;
19497176Syx160601 }
19507176Syx160601 }
19517176Syx160601
19527176Syx160601 np->nl_realip6 = nat->nat_outip6.in6;
19537176Syx160601 np->nl_realport = nat->nat_outport;
19547176Syx160601 }
19557176Syx160601 }
19567176Syx160601
19577176Syx160601 return nat;
19587176Syx160601 }
19597176Syx160601
19607176Syx160601
19617176Syx160601 /* ------------------------------------------------------------------------ */
19627176Syx160601 /* Function: nat6_match */
19637176Syx160601 /* Returns: int - 0 == no match, 1 == match */
19647176Syx160601 /* Parameters: fin(I) - pointer to packet information */
19657176Syx160601 /* np(I) - pointer to NAT rule */
19667176Syx160601 /* */
19677176Syx160601 /* Pull the matching of a packet against a NAT rule out of that complex */
19687176Syx160601 /* loop inside fr_checknat6in() and lay it out properly in its own function.*/
19697176Syx160601 /* ------------------------------------------------------------------------ */
nat6_match(fin,np)19707176Syx160601 static int nat6_match(fin, np)
19717176Syx160601 fr_info_t *fin;
19727176Syx160601 ipnat_t *np;
19737176Syx160601 {
19747176Syx160601 frtuc_t *ft;
19757176Syx160601
19767176Syx160601 if (fin->fin_v != 6)
19777176Syx160601 return 0;
19787176Syx160601
19797176Syx160601 if (np->in_p && fin->fin_p != np->in_p)
19807176Syx160601 return 0;
19817176Syx160601
19827176Syx160601 if (fin->fin_out) {
19837176Syx160601 if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK)))
19847176Syx160601 return 0;
19857176Syx160601 if (IP6_MASKNEQ(&fin->fin_src6, &np->in_in[1], &np->in_in[0])
19867176Syx160601 ^ ((np->in_flags & IPN_NOTSRC) != 0))
19877176Syx160601 return 0;
19887176Syx160601 if (IP6_MASKNEQ(&fin->fin_dst6, &np->in_src[1], &np->in_src[0])
19897176Syx160601 ^ ((np->in_flags & IPN_NOTDST) != 0))
19907176Syx160601 return 0;
19917176Syx160601 } else {
19927176Syx160601 if (!(np->in_redir & NAT_REDIRECT))
19937176Syx160601 return 0;
19947176Syx160601 if (IP6_MASKNEQ(&fin->fin_src6, &np->in_src[1], &np->in_src[0])
19957176Syx160601 ^ ((np->in_flags & IPN_NOTSRC) != 0))
19967176Syx160601 return 0;
19977176Syx160601 if (IP6_MASKNEQ(&fin->fin_dst6, &np->in_out[1], &np->in_out[0])
19987176Syx160601 ^ ((np->in_flags & IPN_NOTDST) != 0))
19997176Syx160601 return 0;
20007176Syx160601 }
20017176Syx160601
20027176Syx160601 ft = &np->in_tuc;
20037176Syx160601 if (!(fin->fin_flx & FI_TCPUDP) ||
20047176Syx160601 (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
20057176Syx160601 if (ft->ftu_scmp || ft->ftu_dcmp)
20067176Syx160601 return 0;
20077176Syx160601 return 1;
20087176Syx160601 }
20097176Syx160601
20107176Syx160601 return fr_tcpudpchk(fin, ft);
20117176Syx160601 }
20127176Syx160601
20137176Syx160601
20147176Syx160601 /* ------------------------------------------------------------------------ */
20157176Syx160601 /* Function: fr_checknat6out */
20167176Syx160601 /* Returns: int - -1 == packet failed NAT checks so block it, */
20177176Syx160601 /* 0 == no packet translation occurred, */
20187176Syx160601 /* 1 == packet was successfully translated. */
20197176Syx160601 /* Parameters: fin(I) - pointer to packet information */
20207176Syx160601 /* passp(I) - pointer to filtering result flags */
20217176Syx160601 /* */
20227176Syx160601 /* Check to see if an outcoming packet should be changed. ICMP packets are */
20237176Syx160601 /* first checked to see if they match an existing entry (if an error), */
20247176Syx160601 /* otherwise a search of the current NAT table is made. If neither results */
20257176Syx160601 /* in a match then a search for a matching NAT rule is made. Create a new */
20267176Syx160601 /* NAT entry if a we matched a NAT rule. Lastly, actually change the */
20277176Syx160601 /* packet header(s) as required. */
20287176Syx160601 /* ------------------------------------------------------------------------ */
fr_checknat6out(fin,passp)20297176Syx160601 int fr_checknat6out(fin, passp)
20307176Syx160601 fr_info_t *fin;
20317176Syx160601 u_32_t *passp;
20327176Syx160601 {
20337176Syx160601 struct ifnet *ifp, *sifp;
20347176Syx160601 int rval, natfailed;
20357176Syx160601 ipnat_t *np = NULL;
20367176Syx160601 u_int nflags = 0;
20377176Syx160601 i6addr_t ipa, iph;
20387176Syx160601 int natadd = 1;
20397176Syx160601 frentry_t *fr;
20407176Syx160601 nat_t *nat;
20417176Syx160601 ipf_stack_t *ifs = fin->fin_ifs;
20427176Syx160601
20437176Syx160601 if (ifs->ifs_nat_stats.ns_rules == 0 || ifs->ifs_fr_nat_lock != 0)
20447176Syx160601 return 0;
20457176Syx160601
20467176Syx160601 natfailed = 0;
20477176Syx160601 fr = fin->fin_fr;
20487176Syx160601 sifp = fin->fin_ifp;
20497176Syx160601 if ((fr != NULL) && !(fr->fr_flags & FR_DUP) &&
20507176Syx160601 fr->fr_tifs[fin->fin_rev].fd_ifp &&
20517176Syx160601 fr->fr_tifs[fin->fin_rev].fd_ifp != (void *)-1)
20527176Syx160601 fin->fin_ifp = fr->fr_tifs[fin->fin_rev].fd_ifp;
20537176Syx160601 ifp = fin->fin_ifp;
20547176Syx160601
20557176Syx160601 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
20567176Syx160601 switch (fin->fin_p)
20577176Syx160601 {
20587176Syx160601 case IPPROTO_TCP :
20597176Syx160601 nflags = IPN_TCP;
20607176Syx160601 break;
20617176Syx160601 case IPPROTO_UDP :
20627176Syx160601 nflags = IPN_UDP;
20637176Syx160601 break;
20647176Syx160601 case IPPROTO_ICMPV6 :
20657176Syx160601 /*
20667176Syx160601 * This is an incoming packet, so the destination is
20677176Syx160601 * the icmp6_id and the source port equals 0
20687176Syx160601 */
20697176Syx160601 if ((fin->fin_flx & FI_ICMPQUERY) != 0)
20707176Syx160601 nflags = IPN_ICMPQUERY;
20717176Syx160601 break;
20727176Syx160601 default :
20737176Syx160601 break;
20747176Syx160601 }
20757176Syx160601
20767176Syx160601 #ifdef IPF_V6_PROXIES
20777176Syx160601 if ((nflags & IPN_TCPUDP))
20787176Syx160601 tcp = fin->fin_dp;
20797176Syx160601 #endif
20807176Syx160601 }
20817176Syx160601
20827176Syx160601 ipa = fin->fin_src6;
20837176Syx160601
20847176Syx160601 READ_ENTER(&ifs->ifs_ipf_nat);
20857176Syx160601
20867176Syx160601 if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) &&
20877176Syx160601 (nat = nat6_icmperror(fin, &nflags, NAT_OUTBOUND)))
20887176Syx160601 /*EMPTY*/;
20897176Syx160601 else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin)))
20907176Syx160601 natadd = 0;
20917176Syx160601 else if ((nat = nat6_outlookup(fin, nflags|NAT_SEARCH,
20927176Syx160601 (u_int)fin->fin_p, &fin->fin_src6.in6,
20937176Syx160601 &fin->fin_dst6.in6))) {
20947176Syx160601 nflags = nat->nat_flags;
20957176Syx160601 } else {
20967176Syx160601 u_32_t hv, nmsk;
20977176Syx160601 i6addr_t msk;
20987176Syx160601 int i;
20997176Syx160601
21007176Syx160601 /*
21017176Syx160601 * If there is no current entry in the nat table for this IP#,
21027176Syx160601 * create one for it (if there is a matching rule).
21037176Syx160601 */
21047176Syx160601 RWLOCK_EXIT(&ifs->ifs_ipf_nat);
21057176Syx160601 i = 3;
21067176Syx160601 msk.i6[0] = 0xffffffff;
21077176Syx160601 msk.i6[1] = 0xffffffff;
21087176Syx160601 msk.i6[2] = 0xffffffff;
21097176Syx160601 msk.i6[3] = 0xffffffff;
21107176Syx160601 nmsk = ifs->ifs_nat6_masks[3];
21117176Syx160601 WRITE_ENTER(&ifs->ifs_ipf_nat);
21127176Syx160601 maskloop:
21137176Syx160601 IP6_AND(&ipa, &msk, &iph);
21147176Syx160601 hv = NAT_HASH_FN6(&iph, 0, ifs->ifs_ipf_natrules_sz);
21157176Syx160601 for (np = ifs->ifs_nat_rules[hv]; np; np = np->in_mnext)
21167176Syx160601 {
21177176Syx160601 if ((np->in_ifps[1] && (np->in_ifps[1] != ifp)))
21187176Syx160601 continue;
21197176Syx160601 if (np->in_v != 6)
21207176Syx160601 continue;
21217176Syx160601 if (np->in_p && (np->in_p != fin->fin_p))
21227176Syx160601 continue;
21237176Syx160601 if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
21247176Syx160601 continue;
21257176Syx160601 if (np->in_flags & IPN_FILTER) {
21267176Syx160601 if (!nat6_match(fin, np))
21277176Syx160601 continue;
21287176Syx160601 } else if (!IP6_MASKEQ(&ipa, &np->in_in[1],
21297176Syx160601 &np->in_in[0]))
21307176Syx160601 continue;
21317176Syx160601
21327176Syx160601 if ((fr != NULL) &&
21337176Syx160601 !fr_matchtag(&np->in_tag, &fr->fr_nattag))
21347176Syx160601 continue;
21357176Syx160601
21367176Syx160601 #ifdef IPF_V6_PROXIES
21377176Syx160601 if (*np->in_plabel != '\0') {
21387176Syx160601 if (((np->in_flags & IPN_FILTER) == 0) &&
21397176Syx160601 (np->in_dport != tcp->th_dport))
21407176Syx160601 continue;
21417176Syx160601 if (appr_ok(fin, tcp, np) == 0)
21427176Syx160601 continue;
21437176Syx160601 }
21447176Syx160601 #endif
21457176Syx160601
21467176Syx160601 if (nat = nat6_new(fin, np, NULL, nflags,
21477176Syx160601 NAT_OUTBOUND)) {
21487176Syx160601 np->in_hits++;
21497176Syx160601 break;
21507176Syx160601 } else
21517176Syx160601 natfailed = -1;
21527176Syx160601 }
21537176Syx160601 if ((np == NULL) && (i >= 0)) {
21547176Syx160601 while (i >= 0) {
21557176Syx160601 while (nmsk) {
21567176Syx160601 msk.i6[i] = htonl(ntohl(msk.i6[i])<<1);
21577176Syx160601 if ((nmsk & 0x80000000) != 0) {
21587176Syx160601 nmsk <<= 1;
21597176Syx160601 goto maskloop;
21607176Syx160601 }
21617176Syx160601 nmsk <<= 1;
21627176Syx160601 }
21637176Syx160601 msk.i6[i--] = 0;
21647176Syx160601 if (i >= 0) {
21657176Syx160601 nmsk = ifs->ifs_nat6_masks[i];
21667176Syx160601 if (nmsk != 0)
21677176Syx160601 goto maskloop;
21687176Syx160601 }
21697176Syx160601 }
21707176Syx160601 }
21717176Syx160601 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
21727176Syx160601 }
21737176Syx160601
21747176Syx160601 if (nat != NULL) {
21757176Syx160601 rval = fr_nat6out(fin, nat, natadd, nflags);
2176*8624SDarren.Reed@Sun.COM } else {
21777176Syx160601 rval = natfailed;
2178*8624SDarren.Reed@Sun.COM }
21797176Syx160601 RWLOCK_EXIT(&ifs->ifs_ipf_nat);
21807176Syx160601
21817176Syx160601 if (rval == -1) {
21827176Syx160601 if (passp != NULL)
21837176Syx160601 *passp = FR_BLOCK;
21847176Syx160601 fin->fin_flx |= FI_BADNAT;
21857176Syx160601 }
21867176Syx160601 fin->fin_ifp = sifp;
21877176Syx160601 return rval;
21887176Syx160601 }
21897176Syx160601
21907176Syx160601 /* ------------------------------------------------------------------------ */
21917176Syx160601 /* Function: fr_nat6out */
21927176Syx160601 /* Returns: int - -1 == packet failed NAT checks so block it, */
21937176Syx160601 /* 1 == packet was successfully translated. */
21947176Syx160601 /* Parameters: fin(I) - pointer to packet information */
21957176Syx160601 /* nat(I) - pointer to NAT structure */
21967176Syx160601 /* natadd(I) - flag indicating if it is safe to add frag cache */
21977176Syx160601 /* nflags(I) - NAT flags set for this packet */
21987176Syx160601 /* */
21997176Syx160601 /* Translate a packet coming "out" on an interface. */
22007176Syx160601 /* ------------------------------------------------------------------------ */
fr_nat6out(fin,nat,natadd,nflags)22017176Syx160601 int fr_nat6out(fin, nat, natadd, nflags)
22027176Syx160601 fr_info_t *fin;
22037176Syx160601 nat_t *nat;
22047176Syx160601 int natadd;
22057176Syx160601 u_32_t nflags;
22067176Syx160601 {
22077176Syx160601 struct icmp6_hdr *icmp6;
22087176Syx160601 u_short *csump;
22097176Syx160601 tcphdr_t *tcp;
22107176Syx160601 ipnat_t *np;
22117176Syx160601 int i;
22127176Syx160601 ipf_stack_t *ifs = fin->fin_ifs;
22137176Syx160601
22147176Syx160601 #if SOLARIS && defined(_KERNEL)
22157513SDarren.Reed@Sun.COM net_handle_t net_data_p = ifs->ifs_ipf_ipv6;
22167176Syx160601 #endif
22177176Syx160601
22187176Syx160601 tcp = NULL;
22197176Syx160601 icmp6 = NULL;
22207176Syx160601 csump = NULL;
22217176Syx160601 np = nat->nat_ptr;
22227176Syx160601
22237176Syx160601 if ((natadd != 0) && (fin->fin_flx & FI_FRAG))
22247176Syx160601 (void) fr_nat_newfrag(fin, 0, nat);
22257176Syx160601
22267176Syx160601 MUTEX_ENTER(&nat->nat_lock);
22277176Syx160601 nat->nat_bytes[1] += fin->fin_plen;
22287176Syx160601 nat->nat_pkts[1]++;
22297176Syx160601 MUTEX_EXIT(&nat->nat_lock);
22307176Syx160601
22317176Syx160601 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
22327176Syx160601 if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) {
22337176Syx160601 tcp = fin->fin_dp;
22347176Syx160601
22357176Syx160601 tcp->th_sport = nat->nat_outport;
22367176Syx160601 fin->fin_data[0] = ntohs(nat->nat_outport);
22377176Syx160601 }
22387176Syx160601
22397176Syx160601 if ((nat->nat_outport != 0) && (nflags & IPN_ICMPQUERY)) {
22407176Syx160601 icmp6 = fin->fin_dp;
22417176Syx160601 icmp6->icmp6_id = nat->nat_outport;
22427176Syx160601 }
22437176Syx160601
22447176Syx160601 csump = nat_proto(fin, nat, nflags);
22457176Syx160601 }
22467176Syx160601
22477176Syx160601 fin->fin_ip6->ip6_src = nat->nat_outip6.in6;
22487176Syx160601 fin->fin_src6 = nat->nat_outip6;
22497176Syx160601
22507176Syx160601 nat_update(fin, nat, np);
22517176Syx160601
22527176Syx160601 /*
22537176Syx160601 * TCP/UDP/ICMPv6 checksum needs to be adjusted.
22547176Syx160601 */
22557176Syx160601 if (csump != NULL && (!(nflags & IPN_TCPUDP) ||
22567176Syx160601 !NET_IS_HCK_L4_FULL(net_data_p, fin->fin_m))) {
22577176Syx160601 if (nflags & IPN_TCPUDP &&
22587176Syx160601 NET_IS_HCK_L4_PART(net_data_p, fin->fin_m)) {
22597176Syx160601 if (nat->nat_dir == NAT_OUTBOUND)
22607176Syx160601 fix_outcksum(csump, nat->nat_sumd[1]);
22617176Syx160601 else
22627176Syx160601 fix_incksum(csump, nat->nat_sumd[1]);
22637176Syx160601 } else {
22647176Syx160601 if (nat->nat_dir == NAT_OUTBOUND)
22657176Syx160601 fix_outcksum(csump, nat->nat_sumd[0]);
22667176Syx160601 else
22677176Syx160601 fix_incksum(csump, nat->nat_sumd[0]);
22687176Syx160601 }
22697176Syx160601 }
22707176Syx160601 #ifdef IPFILTER_SYNC
22717176Syx160601 ipfsync_update(SMC_NAT, fin, nat->nat_sync);
22727176Syx160601 #endif
22737176Syx160601 /* ------------------------------------------------------------- */
22747176Syx160601 /* A few quick notes: */
22757176Syx160601 /* Following are test conditions prior to calling the */
22767176Syx160601 /* appr_check routine. */
22777176Syx160601 /* */
22787176Syx160601 /* A NULL tcp indicates a non TCP/UDP packet. When dealing */
22797176Syx160601 /* with a redirect rule, we attempt to match the packet's */
22807176Syx160601 /* source port against in_dport, otherwise we'd compare the */
22817176Syx160601 /* packet's destination. */
22827176Syx160601 /* ------------------------------------------------------------- */
22837176Syx160601 if ((np != NULL) && (np->in_apr != NULL)) {
22847176Syx160601 i = appr_check(fin, nat);
22857176Syx160601 if (i == 0)
22867176Syx160601 i = 1;
22877176Syx160601 } else
22887176Syx160601 i = 1;
22897176Syx160601 ATOMIC_INCL(ifs->ifs_nat_stats.ns_mapped[1]);
22907176Syx160601 fin->fin_flx |= FI_NATED;
22917176Syx160601 return i;
22927176Syx160601 }
22937176Syx160601
22947176Syx160601
22957176Syx160601 /* ------------------------------------------------------------------------ */
22967176Syx160601 /* Function: fr_checknat6in */
22977176Syx160601 /* Returns: int - -1 == packet failed NAT checks so block it, */
22987176Syx160601 /* 0 == no packet translation occurred, */
22997176Syx160601 /* 1 == packet was successfully translated. */
23007176Syx160601 /* Parameters: fin(I) - pointer to packet information */
23017176Syx160601 /* passp(I) - pointer to filtering result flags */
23027176Syx160601 /* */
23037176Syx160601 /* Check to see if an incoming packet should be changed. ICMP packets are */
23047176Syx160601 /* first checked to see if they match an existing entry (if an error), */
23057176Syx160601 /* otherwise a search of the current NAT table is made. If neither results */
23067176Syx160601 /* in a match then a search for a matching NAT rule is made. Create a new */
23077176Syx160601 /* NAT entry if a we matched a NAT rule. Lastly, actually change the */
23087176Syx160601 /* packet header(s) as required. */
23097176Syx160601 /* ------------------------------------------------------------------------ */
fr_checknat6in(fin,passp)23107176Syx160601 int fr_checknat6in(fin, passp)
23117176Syx160601 fr_info_t *fin;
23127176Syx160601 u_32_t *passp;
23137176Syx160601 {
23147176Syx160601 u_int nflags, natadd;
23157176Syx160601 int rval, natfailed;
23167176Syx160601 struct ifnet *ifp;
23177176Syx160601 struct icmp6_hdr *icmp6;
23187176Syx160601 tcphdr_t *tcp;
23197176Syx160601 u_short dport;
23207176Syx160601 ipnat_t *np;
23217176Syx160601 nat_t *nat;
23227176Syx160601 i6addr_t ipa, iph;
23237176Syx160601 ipf_stack_t *ifs = fin->fin_ifs;
23247176Syx160601
23257176Syx160601 if (ifs->ifs_nat_stats.ns_rules == 0 || ifs->ifs_fr_nat_lock != 0)
23267176Syx160601 return 0;
23277176Syx160601
23287176Syx160601 tcp = NULL;
23297176Syx160601 icmp6 = NULL;
23307176Syx160601 dport = 0;
23317176Syx160601 natadd = 1;
23327176Syx160601 nflags = 0;
23337176Syx160601 natfailed = 0;
23347176Syx160601 ifp = fin->fin_ifp;
23357176Syx160601
23367176Syx160601 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
23377176Syx160601 switch (fin->fin_p)
23387176Syx160601 {
23397176Syx160601 case IPPROTO_TCP :
23407176Syx160601 nflags = IPN_TCP;
23417176Syx160601 break;
23427176Syx160601 case IPPROTO_UDP :
23437176Syx160601 nflags = IPN_UDP;
23447176Syx160601 break;
23457176Syx160601 case IPPROTO_ICMPV6 :
23467176Syx160601 icmp6 = fin->fin_dp;
23477176Syx160601
23487176Syx160601 /*
23497176Syx160601 * This is an incoming packet, so the destination is
23507176Syx160601 * the icmp_id and the source port equals 0
23517176Syx160601 */
23527176Syx160601 if ((fin->fin_flx & FI_ICMPQUERY) != 0) {
23537176Syx160601 nflags = IPN_ICMPQUERY;
23547176Syx160601 dport = icmp6->icmp6_id;
23557176Syx160601 } break;
23567176Syx160601 default :
23577176Syx160601 break;
23587176Syx160601 }
23597176Syx160601
23607176Syx160601 if ((nflags & IPN_TCPUDP)) {
23617176Syx160601 tcp = fin->fin_dp;
23627176Syx160601 dport = tcp->th_dport;
23637176Syx160601 }
23647176Syx160601 }
23657176Syx160601
23667176Syx160601 ipa = fin->fin_dst6;
23677176Syx160601
23687176Syx160601 READ_ENTER(&ifs->ifs_ipf_nat);
23697176Syx160601
23707176Syx160601 if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) &&
23717176Syx160601 (nat = nat6_icmperror(fin, &nflags, NAT_INBOUND)))
23727176Syx160601 /*EMPTY*/;
23737176Syx160601 else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin)))
23747176Syx160601 natadd = 0;
23757176Syx160601 else if ((nat = nat6_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p,
23767176Syx160601 &fin->fin_src6.in6, &ipa.in6))) {
23777176Syx160601 nflags = nat->nat_flags;
23787176Syx160601 } else {
23797176Syx160601 u_32_t hv, rmsk;
23807176Syx160601 i6addr_t msk;
23817176Syx160601 int i;
23827176Syx160601
23837176Syx160601 RWLOCK_EXIT(&ifs->ifs_ipf_nat);
23847176Syx160601 i = 3;
23857176Syx160601 msk.i6[0] = 0xffffffff;
23867176Syx160601 msk.i6[1] = 0xffffffff;
23877176Syx160601 msk.i6[2] = 0xffffffff;
23887176Syx160601 msk.i6[3] = 0xffffffff;
23897176Syx160601 rmsk = ifs->ifs_rdr6_masks[3];
23907176Syx160601 WRITE_ENTER(&ifs->ifs_ipf_nat);
23917176Syx160601 /*
23927176Syx160601 * If there is no current entry in the nat table for this IP#,
23937176Syx160601 * create one for it (if there is a matching rule).
23947176Syx160601 */
23957176Syx160601 maskloop:
23967176Syx160601 IP6_AND(&ipa, &msk, &iph);
23977176Syx160601 hv = NAT_HASH_FN6(&iph, 0, ifs->ifs_ipf_rdrrules_sz);
23987176Syx160601 for (np = ifs->ifs_rdr_rules[hv]; np; np = np->in_rnext) {
23997176Syx160601 if (np->in_ifps[0] && (np->in_ifps[0] != ifp))
24007176Syx160601 continue;
24017176Syx160601 if (np->in_v != fin->fin_v)
24027176Syx160601 continue;
24037176Syx160601 if (np->in_p && (np->in_p != fin->fin_p))
24047176Syx160601 continue;
24057176Syx160601 if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
24067176Syx160601 continue;
24077176Syx160601 if (np->in_flags & IPN_FILTER) {
24087176Syx160601 if (!nat6_match(fin, np))
24097176Syx160601 continue;
24107176Syx160601 } else {
24117176Syx160601 if (!IP6_MASKEQ(&ipa, &np->in_out[1],
24127176Syx160601 &np->in_out[0]))
24137176Syx160601 continue;
24147176Syx160601 if (np->in_pmin &&
24157176Syx160601 ((ntohs(np->in_pmax) < ntohs(dport)) ||
24167176Syx160601 (ntohs(dport) < ntohs(np->in_pmin))))
24177176Syx160601 continue;
24187176Syx160601 }
24197176Syx160601
24207176Syx160601 #ifdef IPF_V6_PROXIES
24217176Syx160601 if (*np->in_plabel != '\0') {
24227176Syx160601 if (!appr_ok(fin, tcp, np)) {
24237176Syx160601 continue;
24247176Syx160601 }
24257176Syx160601 }
24267176Syx160601 #endif
24277176Syx160601
24287176Syx160601 nat = nat6_new(fin, np, NULL, nflags, NAT_INBOUND);
24297176Syx160601 if (nat != NULL) {
24307176Syx160601 np->in_hits++;
24317176Syx160601 break;
24327176Syx160601 } else
24337176Syx160601 natfailed = -1;
24347176Syx160601 }
24357176Syx160601
24367176Syx160601 if ((np == NULL) && (i >= 0)) {
24377176Syx160601 while (i >= 0) {
24387176Syx160601 while (rmsk) {
24397176Syx160601 msk.i6[i] = htonl(ntohl(msk.i6[i])<<1);
24407176Syx160601 if ((rmsk & 0x80000000) != 0) {
24417176Syx160601 rmsk <<= 1;
24427176Syx160601 goto maskloop;
24437176Syx160601 }
24447176Syx160601 rmsk <<= 1;
24457176Syx160601 }
24467176Syx160601 msk.i6[i--] = 0;
24477176Syx160601 if (i >= 0) {
24487176Syx160601 rmsk = ifs->ifs_rdr6_masks[i];
24497176Syx160601 if (rmsk != 0)
24507176Syx160601 goto maskloop;
24517176Syx160601 }
24527176Syx160601 }
24537176Syx160601 }
24547176Syx160601 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
24557176Syx160601 }
24567176Syx160601 if (nat != NULL) {
24577176Syx160601 rval = fr_nat6in(fin, nat, natadd, nflags);
2458*8624SDarren.Reed@Sun.COM } else {
24597176Syx160601 rval = natfailed;
2460*8624SDarren.Reed@Sun.COM }
24617176Syx160601 RWLOCK_EXIT(&ifs->ifs_ipf_nat);
24627176Syx160601
24637176Syx160601 if (rval == -1) {
24647176Syx160601 if (passp != NULL)
24657176Syx160601 *passp = FR_BLOCK;
24667176Syx160601 fin->fin_flx |= FI_BADNAT;
24677176Syx160601 }
24687176Syx160601 return rval;
24697176Syx160601 }
24707176Syx160601
24717176Syx160601
24727176Syx160601 /* ------------------------------------------------------------------------ */
24737176Syx160601 /* Function: fr_nat6in */
24747176Syx160601 /* Returns: int - -1 == packet failed NAT checks so block it, */
24757176Syx160601 /* 1 == packet was successfully translated. */
24767176Syx160601 /* Parameters: fin(I) - pointer to packet information */
24777176Syx160601 /* nat(I) - pointer to NAT structure */
24787176Syx160601 /* natadd(I) - flag indicating if it is safe to add frag cache */
24797176Syx160601 /* nflags(I) - NAT flags set for this packet */
24807176Syx160601 /* Locks Held: ipf_nat (READ) */
24817176Syx160601 /* */
24827176Syx160601 /* Translate a packet coming "in" on an interface. */
24837176Syx160601 /* ------------------------------------------------------------------------ */
fr_nat6in(fin,nat,natadd,nflags)24847176Syx160601 int fr_nat6in(fin, nat, natadd, nflags)
24857176Syx160601 fr_info_t *fin;
24867176Syx160601 nat_t *nat;
24877176Syx160601 int natadd;
24887176Syx160601 u_32_t nflags;
24897176Syx160601 {
24907176Syx160601 struct icmp6_hdr *icmp6;
24917176Syx160601 u_short *csump;
24927176Syx160601 tcphdr_t *tcp;
24937176Syx160601 ipnat_t *np;
24947176Syx160601 ipf_stack_t *ifs = fin->fin_ifs;
24957176Syx160601
24967176Syx160601 #if SOLARIS && defined(_KERNEL)
24977513SDarren.Reed@Sun.COM net_handle_t net_data_p = ifs->ifs_ipf_ipv6;
24987176Syx160601 #endif
24997176Syx160601
25007176Syx160601 tcp = NULL;
25017176Syx160601 csump = NULL;
25027176Syx160601 np = nat->nat_ptr;
25037176Syx160601 fin->fin_fr = nat->nat_fr;
25047176Syx160601
25057176Syx160601 if ((natadd != 0) && (fin->fin_flx & FI_FRAG))
25067176Syx160601 (void) fr_nat_newfrag(fin, 0, nat);
25077176Syx160601
25087176Syx160601 #ifdef IPF_V6_PROXIES
25097176Syx160601 if (np != NULL) {
25107176Syx160601
25117176Syx160601 /* ------------------------------------------------------------- */
25127176Syx160601 /* A few quick notes: */
25137176Syx160601 /* Following are test conditions prior to calling the */
25147176Syx160601 /* appr_check routine. */
25157176Syx160601 /* */
25167176Syx160601 /* A NULL tcp indicates a non TCP/UDP packet. When dealing */
25177176Syx160601 /* with a map rule, we attempt to match the packet's */
25187176Syx160601 /* source port against in_dport, otherwise we'd compare the */
25197176Syx160601 /* packet's destination. */
25207176Syx160601 /* ------------------------------------------------------------- */
25217176Syx160601 if (np->in_apr != NULL) {
25227176Syx160601 i = appr_check(fin, nat);
25237176Syx160601 if (i == -1) {
25247176Syx160601 return -1;
25257176Syx160601 }
25267176Syx160601 }
25277176Syx160601 }
25287176Syx160601 #endif
25297176Syx160601
25307176Syx160601 #ifdef IPFILTER_SYNC
25317176Syx160601 ipfsync_update(SMC_NAT, fin, nat->nat_sync);
25327176Syx160601 #endif
25337176Syx160601
25347176Syx160601 MUTEX_ENTER(&nat->nat_lock);
25357176Syx160601 nat->nat_bytes[0] += fin->fin_plen;
25367176Syx160601 nat->nat_pkts[0]++;
25377176Syx160601 MUTEX_EXIT(&nat->nat_lock);
25387176Syx160601
25397176Syx160601 fin->fin_ip6->ip6_dst = nat->nat_inip6.in6;
25407176Syx160601 fin->fin_dst6 = nat->nat_inip6;
25417176Syx160601
25427176Syx160601 if (nflags & IPN_TCPUDP)
25437176Syx160601 tcp = fin->fin_dp;
25447176Syx160601
25457176Syx160601 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
25467176Syx160601 if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) {
25477176Syx160601 tcp->th_dport = nat->nat_inport;
25487176Syx160601 fin->fin_data[1] = ntohs(nat->nat_inport);
25497176Syx160601 }
25507176Syx160601
25517176Syx160601
25527176Syx160601 if ((nat->nat_inport != 0) && (nflags & IPN_ICMPQUERY)) {
25537176Syx160601 icmp6 = fin->fin_dp;
25547176Syx160601
25557176Syx160601 icmp6->icmp6_id = nat->nat_inport;
25567176Syx160601 }
25577176Syx160601
25587176Syx160601 csump = nat_proto(fin, nat, nflags);
25597176Syx160601 }
25607176Syx160601
25617176Syx160601 nat_update(fin, nat, np);
25627176Syx160601
25637176Syx160601 /*
25647176Syx160601 * In case they are being forwarded, inbound packets always need to have
25657176Syx160601 * their checksum adjusted even if hardware checksum validation said OK.
25667176Syx160601 */
25677176Syx160601 if (csump != NULL) {
25687176Syx160601 if (nat->nat_dir == NAT_OUTBOUND)
25697176Syx160601 fix_incksum(csump, nat->nat_sumd[0]);
25707176Syx160601 else
25717176Syx160601 fix_outcksum(csump, nat->nat_sumd[0]);
25727176Syx160601 }
25737176Syx160601
25747176Syx160601 #if SOLARIS && defined(_KERNEL)
25757176Syx160601 if (nflags & IPN_TCPUDP &&
25767176Syx160601 NET_IS_HCK_L4_PART(net_data_p, fin->fin_m)) {
25777176Syx160601 /*
25787176Syx160601 * Need to adjust the partial checksum result stored in
25797176Syx160601 * db_cksum16, which will be used for validation in IP.
25807176Syx160601 * See IP_CKSUM_RECV().
25817176Syx160601 * Adjustment data should be the inverse of the IP address
25827176Syx160601 * changes, because db_cksum16 is supposed to be the complement
25837176Syx160601 * of the pesudo header.
25847176Syx160601 */
25857176Syx160601 csump = &fin->fin_m->b_datap->db_cksum16;
25867176Syx160601 if (nat->nat_dir == NAT_OUTBOUND)
25877176Syx160601 fix_outcksum(csump, nat->nat_sumd[1]);
25887176Syx160601 else
25897176Syx160601 fix_incksum(csump, nat->nat_sumd[1]);
25907176Syx160601 }
25917176Syx160601 #endif
25927176Syx160601
25937176Syx160601 ATOMIC_INCL(ifs->ifs_nat_stats.ns_mapped[0]);
25947176Syx160601 fin->fin_flx |= FI_NATED;
25957176Syx160601 if (np != NULL && np->in_tag.ipt_num[0] != 0)
25967176Syx160601 fin->fin_nattag = &np->in_tag;
25977176Syx160601 return 1;
25987176Syx160601 }
25997176Syx160601
26007176Syx160601
26017176Syx160601 /* ------------------------------------------------------------------------ */
26027176Syx160601 /* Function: nat_icmpquerytype6 */
26037176Syx160601 /* Returns: int - 1 == success, 0 == failure */
26047176Syx160601 /* Parameters: icmptype(I) - ICMP type number */
26057176Syx160601 /* */
26067176Syx160601 /* Tests to see if the ICMP type number passed is a query/response type or */
26077176Syx160601 /* not. */
26087176Syx160601 /* ------------------------------------------------------------------------ */
nat_icmpquerytype6(icmptype)26097176Syx160601 static INLINE int nat_icmpquerytype6(icmptype)
26107176Syx160601 int icmptype;
26117176Syx160601 {
26127176Syx160601
26137176Syx160601 /*
26147176Syx160601 * For the ICMP query NAT code, it is essential that both the query
26157176Syx160601 * and the reply match on the NAT rule. Because the NAT structure
26167176Syx160601 * does not keep track of the icmptype, and a single NAT structure
26177176Syx160601 * is used for all icmp types with the same src, dest and id, we
26187176Syx160601 * simply define the replies as queries as well. The funny thing is,
26197176Syx160601 * altough it seems silly to call a reply a query, this is exactly
26207176Syx160601 * as it is defined in the IPv4 specification
26217176Syx160601 */
26227176Syx160601
26237176Syx160601 switch (icmptype)
26247176Syx160601 {
26257176Syx160601
26267176Syx160601 case ICMP6_ECHO_REPLY:
26277176Syx160601 case ICMP6_ECHO_REQUEST:
26287176Syx160601 /* route aedvertisement/solliciation is currently unsupported: */
26297176Syx160601 /* it would require rewriting the ICMP data section */
26307176Syx160601 case ICMP6_MEMBERSHIP_QUERY:
26317176Syx160601 case ICMP6_MEMBERSHIP_REPORT:
26327176Syx160601 case ICMP6_MEMBERSHIP_REDUCTION:
26337176Syx160601 case ICMP6_WRUREQUEST:
26347176Syx160601 case ICMP6_WRUREPLY:
26357176Syx160601 case MLD6_MTRACE_RESP:
26367176Syx160601 case MLD6_MTRACE:
26377176Syx160601 return 1;
26387176Syx160601 default:
26397176Syx160601 return 0;
26407176Syx160601 }
26417176Syx160601 }
2642