1d6c23f6fSyx160601 /* 2d6c23f6fSyx160601 * Copyright (C) 1995-2003 by Darren Reed. 3d6c23f6fSyx160601 * 4d6c23f6fSyx160601 * See the IPFILTER.LICENCE file for details on licencing. 5d6c23f6fSyx160601 * 633f2fefdSDarren Reed * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 7d6c23f6fSyx160601 * Use is subject to license terms. 8d6c23f6fSyx160601 */ 9d6c23f6fSyx160601 10d6c23f6fSyx160601 #if defined(KERNEL) || defined(_KERNEL) 11d6c23f6fSyx160601 # undef KERNEL 12d6c23f6fSyx160601 # undef _KERNEL 13d6c23f6fSyx160601 # define KERNEL 1 14d6c23f6fSyx160601 # define _KERNEL 1 15d6c23f6fSyx160601 #endif 16d6c23f6fSyx160601 #include <sys/errno.h> 17d6c23f6fSyx160601 #include <sys/types.h> 18d6c23f6fSyx160601 #include <sys/param.h> 19d6c23f6fSyx160601 #include <sys/time.h> 20d6c23f6fSyx160601 #include <sys/file.h> 21d6c23f6fSyx160601 #if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \ 22d6c23f6fSyx160601 defined(_KERNEL) 23d6c23f6fSyx160601 # include "opt_ipfilter_log.h" 24d6c23f6fSyx160601 #endif 25d6c23f6fSyx160601 #if !defined(_KERNEL) 26d6c23f6fSyx160601 # include <stdio.h> 27d6c23f6fSyx160601 # include <string.h> 28d6c23f6fSyx160601 # include <stdlib.h> 29d6c23f6fSyx160601 # define _KERNEL 30d6c23f6fSyx160601 # ifdef __OpenBSD__ 31d6c23f6fSyx160601 struct file; 32d6c23f6fSyx160601 # endif 33d6c23f6fSyx160601 # include <sys/uio.h> 34d6c23f6fSyx160601 # undef _KERNEL 35d6c23f6fSyx160601 #endif 36d6c23f6fSyx160601 #if defined(_KERNEL) && (__FreeBSD_version >= 220000) 37d6c23f6fSyx160601 # include <sys/filio.h> 38d6c23f6fSyx160601 # include <sys/fcntl.h> 39d6c23f6fSyx160601 #else 40d6c23f6fSyx160601 # include <sys/ioctl.h> 41d6c23f6fSyx160601 #endif 42d6c23f6fSyx160601 #if !defined(AIX) 43d6c23f6fSyx160601 # include <sys/fcntl.h> 44d6c23f6fSyx160601 #endif 45d6c23f6fSyx160601 #if !defined(linux) 46d6c23f6fSyx160601 # include <sys/protosw.h> 47d6c23f6fSyx160601 #endif 48d6c23f6fSyx160601 #include <sys/socket.h> 49d6c23f6fSyx160601 #if defined(_KERNEL) 50d6c23f6fSyx160601 # include <sys/systm.h> 51d6c23f6fSyx160601 # if !defined(__SVR4) && !defined(__svr4__) 52d6c23f6fSyx160601 # include <sys/mbuf.h> 53d6c23f6fSyx160601 # endif 54d6c23f6fSyx160601 #endif 55d6c23f6fSyx160601 #if defined(__SVR4) || defined(__svr4__) 56d6c23f6fSyx160601 # include <sys/filio.h> 57d6c23f6fSyx160601 # include <sys/byteorder.h> 58d6c23f6fSyx160601 # ifdef _KERNEL 59d6c23f6fSyx160601 # include <sys/dditypes.h> 60d6c23f6fSyx160601 # endif 61d6c23f6fSyx160601 # include <sys/stream.h> 62d6c23f6fSyx160601 # include <sys/kmem.h> 63d6c23f6fSyx160601 #endif 64d6c23f6fSyx160601 #if __FreeBSD_version >= 300000 65d6c23f6fSyx160601 # include <sys/queue.h> 66d6c23f6fSyx160601 #endif 67d6c23f6fSyx160601 #include <net/if.h> 68d6c23f6fSyx160601 #if __FreeBSD_version >= 300000 69d6c23f6fSyx160601 # include <net/if_var.h> 70d6c23f6fSyx160601 # if defined(_KERNEL) && !defined(IPFILTER_LKM) 71d6c23f6fSyx160601 # include "opt_ipfilter.h" 72d6c23f6fSyx160601 # endif 73d6c23f6fSyx160601 #endif 74d6c23f6fSyx160601 #ifdef sun 75d6c23f6fSyx160601 # include <net/af.h> 76d6c23f6fSyx160601 #endif 77d6c23f6fSyx160601 #include <net/route.h> 78d6c23f6fSyx160601 #include <netinet/in.h> 79d6c23f6fSyx160601 #include <netinet/in_systm.h> 80d6c23f6fSyx160601 #include <netinet/ip.h> 81d6c23f6fSyx160601 82d6c23f6fSyx160601 #ifdef RFC1825 83d6c23f6fSyx160601 # include <vpn/md5.h> 84d6c23f6fSyx160601 # include <vpn/ipsec.h> 85d6c23f6fSyx160601 extern struct ifnet vpnif; 86d6c23f6fSyx160601 #endif 87d6c23f6fSyx160601 88d6c23f6fSyx160601 #if !defined(linux) 89d6c23f6fSyx160601 # include <netinet/ip_var.h> 90d6c23f6fSyx160601 #endif 91d6c23f6fSyx160601 #include <netinet/tcp.h> 92d6c23f6fSyx160601 #include <netinet/udp.h> 93d6c23f6fSyx160601 #include <netinet/ip_icmp.h> 94d6c23f6fSyx160601 #include "netinet/ip_compat.h" 95d6c23f6fSyx160601 #include <netinet/tcpip.h> 96d6c23f6fSyx160601 #include "netinet/ip_fil.h" 97d6c23f6fSyx160601 #include "netinet/ip_nat.h" 98d6c23f6fSyx160601 #include "netinet/ip_frag.h" 99d6c23f6fSyx160601 #include "netinet/ip_state.h" 100d6c23f6fSyx160601 #include "netinet/ip_proxy.h" 101d6c23f6fSyx160601 #include "netinet/ipf_stack.h" 102d6c23f6fSyx160601 #ifdef IPFILTER_SYNC 103d6c23f6fSyx160601 #include "netinet/ip_sync.h" 104d6c23f6fSyx160601 #endif 105d6c23f6fSyx160601 #if (__FreeBSD_version >= 300000) 106d6c23f6fSyx160601 # include <sys/malloc.h> 107d6c23f6fSyx160601 #endif 108d6c23f6fSyx160601 /* END OF INCLUDES */ 109d6c23f6fSyx160601 110d6c23f6fSyx160601 #undef SOCKADDR_IN 111d6c23f6fSyx160601 #define SOCKADDR_IN struct sockaddr_in 112d6c23f6fSyx160601 113d6c23f6fSyx160601 #if !defined(lint) 114d6c23f6fSyx160601 static const char rcsid[] = "@(#)$Id: ip_nat6.c,v 1.2 2008/02/14 21:05:50 darrenr Exp $"; 115d6c23f6fSyx160601 #endif 116d6c23f6fSyx160601 117d6c23f6fSyx160601 static hostmap_t *nat6_hostmap __P((ipnat_t *, i6addr_t *, i6addr_t *, 118d6c23f6fSyx160601 i6addr_t *, u_32_t, ipf_stack_t *)); 119d6c23f6fSyx160601 static INLINE int nat6_newmap __P((fr_info_t *, nat_t *, natinfo_t *)); 120d6c23f6fSyx160601 static INLINE int nat6_newrdr __P((fr_info_t *, nat_t *, natinfo_t *)); 121d6c23f6fSyx160601 static INLINE int nat6_finalise __P((fr_info_t *, nat_t *, natinfo_t *, 122d6c23f6fSyx160601 tcphdr_t *, nat_t **, int)); 123d6c23f6fSyx160601 static void nat6_tabmove __P((nat_t *, ipf_stack_t *)); 124d6c23f6fSyx160601 static int nat6_match __P((fr_info_t *, ipnat_t *)); 125d6c23f6fSyx160601 static INLINE int nat_icmpquerytype6 __P((int)); 126d6c23f6fSyx160601 127d6c23f6fSyx160601 128d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 129d6c23f6fSyx160601 /* Function: nat6_addrdr */ 130d6c23f6fSyx160601 /* Returns: Nil */ 131d6c23f6fSyx160601 /* Parameters: n(I) - pointer to NAT rule to add */ 132d6c23f6fSyx160601 /* */ 133d6c23f6fSyx160601 /* Adds a redirect rule to the hash table of redirect rules and the list of */ 134d6c23f6fSyx160601 /* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ 135d6c23f6fSyx160601 /* use by redirect rules. */ 136d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 137d6c23f6fSyx160601 void nat6_addrdr(n, ifs) 138d6c23f6fSyx160601 ipnat_t *n; 139d6c23f6fSyx160601 ipf_stack_t *ifs; 140d6c23f6fSyx160601 { 141d6c23f6fSyx160601 ipnat_t **np; 142d6c23f6fSyx160601 i6addr_t j; 143d6c23f6fSyx160601 u_int hv; 144d6c23f6fSyx160601 int k; 145d6c23f6fSyx160601 146d6c23f6fSyx160601 k = count6bits(n->in_out[1].i6); 147d6c23f6fSyx160601 if ((k >= 0) && (k != 128)) 148d6c23f6fSyx160601 ifs->ifs_rdr6_masks[k >> 5] |= 1 << (k & 31); 149d6c23f6fSyx160601 IP6_AND(&n->in_out[0], &n->in_out[1], &j); 150d6c23f6fSyx160601 hv = NAT_HASH_FN6(&j, 0, ifs->ifs_ipf_rdrrules_sz); 151d6c23f6fSyx160601 np = ifs->ifs_rdr_rules + hv; 152d6c23f6fSyx160601 while (*np != NULL) 153d6c23f6fSyx160601 np = &(*np)->in_rnext; 154d6c23f6fSyx160601 n->in_rnext = NULL; 155d6c23f6fSyx160601 n->in_prnext = np; 156d6c23f6fSyx160601 n->in_hv = hv; 157d6c23f6fSyx160601 *np = n; 158d6c23f6fSyx160601 } 159d6c23f6fSyx160601 160d6c23f6fSyx160601 161d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 162d6c23f6fSyx160601 /* Function: nat6_addnat */ 163d6c23f6fSyx160601 /* Returns: Nil */ 164d6c23f6fSyx160601 /* Parameters: n(I) - pointer to NAT rule to add */ 165d6c23f6fSyx160601 /* */ 166d6c23f6fSyx160601 /* Adds a NAT map rule to the hash table of rules and the list of loaded */ 167d6c23f6fSyx160601 /* NAT rules. Updates the bitmask indicating which netmasks are in use by */ 168d6c23f6fSyx160601 /* redirect rules. */ 169d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 170d6c23f6fSyx160601 void nat6_addnat(n, ifs) 171d6c23f6fSyx160601 ipnat_t *n; 172d6c23f6fSyx160601 ipf_stack_t *ifs; 173d6c23f6fSyx160601 { 174d6c23f6fSyx160601 ipnat_t **np; 175d6c23f6fSyx160601 i6addr_t j; 176d6c23f6fSyx160601 u_int hv; 177d6c23f6fSyx160601 int k; 178d6c23f6fSyx160601 179d6c23f6fSyx160601 k = count6bits(n->in_in[1].i6); 180d6c23f6fSyx160601 if ((k >= 0) && (k != 128)) 181d6c23f6fSyx160601 ifs->ifs_nat6_masks[k >> 5] |= 1 << (k & 31); 182d6c23f6fSyx160601 IP6_AND(&n->in_in[0], &n->in_in[1], &j); 183d6c23f6fSyx160601 hv = NAT_HASH_FN6(&j, 0, ifs->ifs_ipf_natrules_sz); 184d6c23f6fSyx160601 np = ifs->ifs_nat_rules + hv; 185d6c23f6fSyx160601 while (*np != NULL) 186d6c23f6fSyx160601 np = &(*np)->in_mnext; 187d6c23f6fSyx160601 n->in_mnext = NULL; 188d6c23f6fSyx160601 n->in_pmnext = np; 189d6c23f6fSyx160601 n->in_hv = hv; 190d6c23f6fSyx160601 *np = n; 191d6c23f6fSyx160601 } 192d6c23f6fSyx160601 193d6c23f6fSyx160601 194d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 195d6c23f6fSyx160601 /* Function: nat6_hostmap */ 196d6c23f6fSyx160601 /* Returns: struct hostmap* - NULL if no hostmap could be created, */ 197d6c23f6fSyx160601 /* else a pointer to the hostmapping to use */ 198d6c23f6fSyx160601 /* Parameters: np(I) - pointer to NAT rule */ 199d6c23f6fSyx160601 /* real(I) - real IP address */ 200d6c23f6fSyx160601 /* map(I) - mapped IP address */ 201d6c23f6fSyx160601 /* port(I) - destination port number */ 202d6c23f6fSyx160601 /* Write Locks: ipf_nat */ 203d6c23f6fSyx160601 /* */ 204d6c23f6fSyx160601 /* Check if an ip address has already been allocated for a given mapping */ 205d6c23f6fSyx160601 /* that is not doing port based translation. If is not yet allocated, then */ 206d6c23f6fSyx160601 /* create a new entry if a non-NULL NAT rule pointer has been supplied. */ 207d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 208d6c23f6fSyx160601 static struct hostmap *nat6_hostmap(np, src, dst, map, port, ifs) 209d6c23f6fSyx160601 ipnat_t *np; 210d6c23f6fSyx160601 i6addr_t *src, *dst, *map; 211d6c23f6fSyx160601 u_32_t port; 212d6c23f6fSyx160601 ipf_stack_t *ifs; 213d6c23f6fSyx160601 { 214d6c23f6fSyx160601 hostmap_t *hm; 215d6c23f6fSyx160601 u_int hv; 216d6c23f6fSyx160601 217d6c23f6fSyx160601 hv = (src->i6[3] ^ dst->i6[3]); 218d6c23f6fSyx160601 hv += (src->i6[2] ^ dst->i6[2]); 219d6c23f6fSyx160601 hv += (src->i6[1] ^ dst->i6[1]); 220d6c23f6fSyx160601 hv += (src->i6[0] ^ dst->i6[0]); 221d6c23f6fSyx160601 hv += src->i6[3]; 222d6c23f6fSyx160601 hv += src->i6[2]; 223d6c23f6fSyx160601 hv += src->i6[1]; 224d6c23f6fSyx160601 hv += src->i6[0]; 225d6c23f6fSyx160601 hv += dst->i6[3]; 226d6c23f6fSyx160601 hv += dst->i6[2]; 227d6c23f6fSyx160601 hv += dst->i6[1]; 228d6c23f6fSyx160601 hv += dst->i6[0]; 229d6c23f6fSyx160601 hv %= HOSTMAP_SIZE; 230d6c23f6fSyx160601 for (hm = ifs->ifs_maptable[hv]; hm; hm = hm->hm_next) 231d6c23f6fSyx160601 if (IP6_EQ(&hm->hm_srcip6, src) && 232d6c23f6fSyx160601 IP6_EQ(&hm->hm_dstip6, dst) && 233d6c23f6fSyx160601 ((np == NULL) || (np == hm->hm_ipnat)) && 234d6c23f6fSyx160601 ((port == 0) || (port == hm->hm_port))) { 235d6c23f6fSyx160601 hm->hm_ref++; 236d6c23f6fSyx160601 return hm; 237d6c23f6fSyx160601 } 238d6c23f6fSyx160601 239d6c23f6fSyx160601 if (np == NULL) 240d6c23f6fSyx160601 return NULL; 241d6c23f6fSyx160601 242d6c23f6fSyx160601 KMALLOC(hm, hostmap_t *); 243d6c23f6fSyx160601 if (hm) { 244d6c23f6fSyx160601 hm->hm_hnext = ifs->ifs_ipf_hm_maplist; 245d6c23f6fSyx160601 hm->hm_phnext = &ifs->ifs_ipf_hm_maplist; 246d6c23f6fSyx160601 if (ifs->ifs_ipf_hm_maplist != NULL) 247d6c23f6fSyx160601 ifs->ifs_ipf_hm_maplist->hm_phnext = &hm->hm_hnext; 248d6c23f6fSyx160601 ifs->ifs_ipf_hm_maplist = hm; 249d6c23f6fSyx160601 250d6c23f6fSyx160601 hm->hm_next = ifs->ifs_maptable[hv]; 251d6c23f6fSyx160601 hm->hm_pnext = ifs->ifs_maptable + hv; 252d6c23f6fSyx160601 if (ifs->ifs_maptable[hv] != NULL) 253d6c23f6fSyx160601 ifs->ifs_maptable[hv]->hm_pnext = &hm->hm_next; 254d6c23f6fSyx160601 ifs->ifs_maptable[hv] = hm; 255d6c23f6fSyx160601 hm->hm_ipnat = np; 256d6c23f6fSyx160601 hm->hm_src = *src; 257d6c23f6fSyx160601 hm->hm_dst = *dst; 258d6c23f6fSyx160601 hm->hm_map = *map; 259d6c23f6fSyx160601 hm->hm_ref = 1; 260d6c23f6fSyx160601 hm->hm_port = port; 261d6c23f6fSyx160601 hm->hm_v = 6; 262d6c23f6fSyx160601 } 263d6c23f6fSyx160601 return hm; 264d6c23f6fSyx160601 } 265d6c23f6fSyx160601 266d6c23f6fSyx160601 267d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 268d6c23f6fSyx160601 /* Function: nat6_newmap */ 269d6c23f6fSyx160601 /* Returns: int - -1 == error, 0 == success */ 270d6c23f6fSyx160601 /* Parameters: fin(I) - pointer to packet information */ 271d6c23f6fSyx160601 /* nat(I) - pointer to NAT entry */ 272d6c23f6fSyx160601 /* ni(I) - pointer to structure with misc. information needed */ 273d6c23f6fSyx160601 /* to create new NAT entry. */ 274d6c23f6fSyx160601 /* */ 275d6c23f6fSyx160601 /* Given an empty NAT structure, populate it with new information about a */ 276d6c23f6fSyx160601 /* new NAT session, as defined by the matching NAT rule. */ 277d6c23f6fSyx160601 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 278d6c23f6fSyx160601 /* to the new IP address for the translation. */ 279d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 280d6c23f6fSyx160601 static INLINE int nat6_newmap(fin, nat, ni) 281d6c23f6fSyx160601 fr_info_t *fin; 282d6c23f6fSyx160601 nat_t *nat; 283d6c23f6fSyx160601 natinfo_t *ni; 284d6c23f6fSyx160601 { 285d6c23f6fSyx160601 u_short st_port, dport, sport, port, sp, dp; 286d6c23f6fSyx160601 i6addr_t in, st_ip; 287d6c23f6fSyx160601 hostmap_t *hm; 288d6c23f6fSyx160601 u_32_t flags; 289d6c23f6fSyx160601 ipnat_t *np; 290d6c23f6fSyx160601 nat_t *natl; 291d6c23f6fSyx160601 int l; 292d6c23f6fSyx160601 ipf_stack_t *ifs = fin->fin_ifs; 293d6c23f6fSyx160601 294d6c23f6fSyx160601 /* 295d6c23f6fSyx160601 * If it's an outbound packet which doesn't match any existing 296d6c23f6fSyx160601 * record, then create a new port 297d6c23f6fSyx160601 */ 298d6c23f6fSyx160601 l = 0; 299d6c23f6fSyx160601 hm = NULL; 300d6c23f6fSyx160601 np = ni->nai_np; 301d6c23f6fSyx160601 st_ip = np->in_next6; 302d6c23f6fSyx160601 st_port = np->in_pnext; 303d6c23f6fSyx160601 flags = ni->nai_flags; 304d6c23f6fSyx160601 sport = ni->nai_sport; 305d6c23f6fSyx160601 dport = ni->nai_dport; 306d6c23f6fSyx160601 307d6c23f6fSyx160601 /* 308d6c23f6fSyx160601 * Do a loop until we either run out of entries to try or we find 309d6c23f6fSyx160601 * a NAT mapping that isn't currently being used. This is done 310d6c23f6fSyx160601 * because the change to the source is not (usually) being fixed. 311d6c23f6fSyx160601 */ 312d6c23f6fSyx160601 do { 313d6c23f6fSyx160601 port = 0; 314d6c23f6fSyx160601 in = np->in_next6; 315d6c23f6fSyx160601 if (l == 0) { 316d6c23f6fSyx160601 /* 317d6c23f6fSyx160601 * Check to see if there is an existing NAT 318d6c23f6fSyx160601 * setup for this IP address pair. 319d6c23f6fSyx160601 */ 320d6c23f6fSyx160601 hm = nat6_hostmap(np, &fin->fin_src6, &fin->fin_dst6, 321d6c23f6fSyx160601 &in, 0, ifs); 322d6c23f6fSyx160601 if (hm != NULL) 323d6c23f6fSyx160601 in = hm->hm_map; 324d6c23f6fSyx160601 } else if ((l == 1) && (hm != NULL)) { 325d6c23f6fSyx160601 fr_hostmapdel(&hm); 326d6c23f6fSyx160601 } 327d6c23f6fSyx160601 328d6c23f6fSyx160601 nat->nat_hm = hm; 329d6c23f6fSyx160601 330d6c23f6fSyx160601 if (IP6_ISONES(&np->in_out[1]) && (np->in_pnext == 0)) { 331d6c23f6fSyx160601 if (l > 0) 332d6c23f6fSyx160601 return -1; 333d6c23f6fSyx160601 } 334d6c23f6fSyx160601 335d6c23f6fSyx160601 if (np->in_redir == NAT_BIMAP && 336d6c23f6fSyx160601 IP6_EQ(&np->in_in[1], &np->in_out[1])) { 337d6c23f6fSyx160601 i6addr_t temp; 338d6c23f6fSyx160601 /* 339d6c23f6fSyx160601 * map the address block in a 1:1 fashion 340d6c23f6fSyx160601 */ 341d6c23f6fSyx160601 temp.i6[0] = fin->fin_src6.i6[0] & 342d6c23f6fSyx160601 ~np->in_in[1].i6[0]; 343d6c23f6fSyx160601 temp.i6[1] = fin->fin_src6.i6[1] & 344d6c23f6fSyx160601 ~np->in_in[1].i6[1]; 345d6c23f6fSyx160601 temp.i6[2] = fin->fin_src6.i6[2] & 346d6c23f6fSyx160601 ~np->in_in[1].i6[2]; 347d6c23f6fSyx160601 temp.i6[3] = fin->fin_src6.i6[3] & 348d6c23f6fSyx160601 ~np->in_in[1].i6[3]; 349d6c23f6fSyx160601 in = np->in_out[0]; 350d6c23f6fSyx160601 IP6_MERGE(&in, &temp, &np->in_in[0]); 351d6c23f6fSyx160601 352d6c23f6fSyx160601 #ifdef NEED_128BIT_MATH 353d6c23f6fSyx160601 } else if (np->in_redir & NAT_MAPBLK) { 354d6c23f6fSyx160601 if ((l >= np->in_ppip) || ((l > 0) && 355d6c23f6fSyx160601 !(flags & IPN_TCPUDP))) 356d6c23f6fSyx160601 return -1; 357d6c23f6fSyx160601 /* 358d6c23f6fSyx160601 * map-block - Calculate destination address. 359d6c23f6fSyx160601 */ 360d6c23f6fSyx160601 IP6_MASK(&in, &fin->fin_src6, &np->in_in[1]); 361d6c23f6fSyx160601 in = ntol(in); 362d6c23f6fSyx160601 inb = in; 363d6c23f6fSyx160601 in /= np->in_ippip; 364d6c23f6fSyx160601 in &= ntohl(~np->in_out[1]); 365d6c23f6fSyx160601 in += ntohl(np->in_out[0]); 366d6c23f6fSyx160601 /* 367d6c23f6fSyx160601 * Calculate destination port. 368d6c23f6fSyx160601 */ 369d6c23f6fSyx160601 if ((flags & IPN_TCPUDP) && 370d6c23f6fSyx160601 (np->in_ppip != 0)) { 371d6c23f6fSyx160601 port = ntohs(sport) + l; 372d6c23f6fSyx160601 port %= np->in_ppip; 373d6c23f6fSyx160601 port += np->in_ppip * 374d6c23f6fSyx160601 (inb.s_addr % np->in_ippip); 375d6c23f6fSyx160601 port += MAPBLK_MINPORT; 376d6c23f6fSyx160601 port = htons(port); 377d6c23f6fSyx160601 } 378d6c23f6fSyx160601 #endif 379d6c23f6fSyx160601 380d6c23f6fSyx160601 } else if (IP6_ISZERO(&np->in_out[0]) && 381d6c23f6fSyx160601 IP6_ISONES(&np->in_out[1])) { 382d6c23f6fSyx160601 /* 383d6c23f6fSyx160601 * 0/128 - use the interface's IP address. 384d6c23f6fSyx160601 */ 385d6c23f6fSyx160601 if ((l > 0) || 386d6c23f6fSyx160601 fr_ifpaddr(6, FRI_NORMAL, fin->fin_ifp, 387d6c23f6fSyx160601 (void *)&in, NULL, fin->fin_ifs) == -1) 388d6c23f6fSyx160601 return -1; 389d6c23f6fSyx160601 390d6c23f6fSyx160601 } else if (IP6_ISZERO(&np->in_out[0]) && 391d6c23f6fSyx160601 IP6_ISZERO(&np->in_out[1])) { 392d6c23f6fSyx160601 /* 393d6c23f6fSyx160601 * 0/0 - use the original source address/port. 394d6c23f6fSyx160601 */ 395d6c23f6fSyx160601 if (l > 0) 396d6c23f6fSyx160601 return -1; 397d6c23f6fSyx160601 in = fin->fin_src6; 398d6c23f6fSyx160601 399d6c23f6fSyx160601 } else if (!IP6_ISONES(&np->in_out[1]) && 400d6c23f6fSyx160601 (np->in_pnext == 0) && ((l > 0) || (hm == NULL))) { 401d6c23f6fSyx160601 IP6_INC(&np->in_next6); 402d6c23f6fSyx160601 } 403d6c23f6fSyx160601 404d6c23f6fSyx160601 natl = NULL; 405d6c23f6fSyx160601 406d6c23f6fSyx160601 if ((flags & IPN_TCPUDP) && 407d6c23f6fSyx160601 ((np->in_redir & NAT_MAPBLK) == 0) && 408d6c23f6fSyx160601 (np->in_flags & IPN_AUTOPORTMAP)) { 409d6c23f6fSyx160601 /*EMPTY*/; 410d6c23f6fSyx160601 #ifdef NEED_128BIT_MATH 411d6c23f6fSyx160601 /* 412d6c23f6fSyx160601 * XXX "ports auto" (without map-block) 413d6c23f6fSyx160601 */ 414d6c23f6fSyx160601 if ((l > 0) && (l % np->in_ppip == 0)) { 415d6c23f6fSyx160601 if (l > np->in_space) { 416d6c23f6fSyx160601 return -1; 417d6c23f6fSyx160601 } else if ((l > np->in_ppip) && 418d6c23f6fSyx160601 !IP6_ISONES(&np->in_out[1])) { 419d6c23f6fSyx160601 IP6_INC(&np->in_next6); 420d6c23f6fSyx160601 } 421d6c23f6fSyx160601 } 422d6c23f6fSyx160601 if (np->in_ppip != 0) { 423d6c23f6fSyx160601 port = ntohs(sport); 424d6c23f6fSyx160601 port += (l % np->in_ppip); 425d6c23f6fSyx160601 port %= np->in_ppip; 426d6c23f6fSyx160601 port += np->in_ppip * 427d6c23f6fSyx160601 (ntohl(fin->fin_src6) % 428d6c23f6fSyx160601 np->in_ippip); 429d6c23f6fSyx160601 port += MAPBLK_MINPORT; 430d6c23f6fSyx160601 port = htons(port); 431d6c23f6fSyx160601 } 432d6c23f6fSyx160601 #endif 433d6c23f6fSyx160601 434d6c23f6fSyx160601 } else if (((np->in_redir & NAT_MAPBLK) == 0) && 435d6c23f6fSyx160601 (flags & IPN_TCPUDPICMP) && (np->in_pnext != 0)) { 436d6c23f6fSyx160601 /* 437d6c23f6fSyx160601 * Standard port translation. Select next port. 438d6c23f6fSyx160601 */ 439ab073b32Sdr146992 if (np->in_flags & IPN_SEQUENTIAL) { 440ab073b32Sdr146992 port = np->in_pnext; 441ab073b32Sdr146992 } else { 442ab073b32Sdr146992 port = ipf_random() % (ntohs(np->in_pmax) - 443ab073b32Sdr146992 ntohs(np->in_pmin)); 444ab073b32Sdr146992 port += ntohs(np->in_pmin); 445ab073b32Sdr146992 } 446ab073b32Sdr146992 port = htons(port); 447ab073b32Sdr146992 np->in_pnext++; 448d6c23f6fSyx160601 449d6c23f6fSyx160601 if (np->in_pnext > ntohs(np->in_pmax)) { 450d6c23f6fSyx160601 np->in_pnext = ntohs(np->in_pmin); 451d6c23f6fSyx160601 if (!IP6_ISONES(&np->in_out[1])) { 452d6c23f6fSyx160601 IP6_INC(&np->in_next6); 453d6c23f6fSyx160601 } 454d6c23f6fSyx160601 } 455d6c23f6fSyx160601 } 456d6c23f6fSyx160601 457d6c23f6fSyx160601 if (np->in_flags & IPN_IPRANGE) { 458d6c23f6fSyx160601 if (IP6_GT(&np->in_next6, &np->in_out[1])) 459d6c23f6fSyx160601 np->in_next6 = np->in_out[0]; 460d6c23f6fSyx160601 } else { 461d6c23f6fSyx160601 i6addr_t a1, a2; 462d6c23f6fSyx160601 463d6c23f6fSyx160601 a1 = np->in_next6; 464d6c23f6fSyx160601 IP6_INC(&a1); 465d6c23f6fSyx160601 IP6_AND(&a1, &np->in_out[1], &a2); 466d6c23f6fSyx160601 if (!IP6_ISONES(&np->in_out[1]) && 467d6c23f6fSyx160601 IP6_GT(&a2, &np->in_out[0])) { 468d6c23f6fSyx160601 IP6_ADD(&np->in_out[0], 1, &np->in_next6); 469d6c23f6fSyx160601 } 470d6c23f6fSyx160601 } 471d6c23f6fSyx160601 472d6c23f6fSyx160601 if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) 473d6c23f6fSyx160601 port = sport; 474d6c23f6fSyx160601 475d6c23f6fSyx160601 /* 476d6c23f6fSyx160601 * Here we do a lookup of the connection as seen from 477d6c23f6fSyx160601 * the outside. If an IP# pair already exists, try 478d6c23f6fSyx160601 * again. So if you have A->B becomes C->B, you can 479d6c23f6fSyx160601 * also have D->E become C->E but not D->B causing 480d6c23f6fSyx160601 * another C->B. Also take protocol and ports into 481d6c23f6fSyx160601 * account when determining whether a pre-existing 482d6c23f6fSyx160601 * NAT setup will cause an external conflict where 483d6c23f6fSyx160601 * this is appropriate. 484d6c23f6fSyx160601 */ 485d6c23f6fSyx160601 sp = fin->fin_data[0]; 486d6c23f6fSyx160601 dp = fin->fin_data[1]; 487d6c23f6fSyx160601 fin->fin_data[0] = fin->fin_data[1]; 488d6c23f6fSyx160601 fin->fin_data[1] = htons(port); 489d6c23f6fSyx160601 natl = nat6_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 490d6c23f6fSyx160601 (u_int)fin->fin_p, &fin->fin_dst6.in6, &in.in6); 491d6c23f6fSyx160601 fin->fin_data[0] = sp; 492d6c23f6fSyx160601 fin->fin_data[1] = dp; 493d6c23f6fSyx160601 494d6c23f6fSyx160601 /* 495d6c23f6fSyx160601 * Has the search wrapped around and come back to the 496d6c23f6fSyx160601 * start ? 497d6c23f6fSyx160601 */ 498d6c23f6fSyx160601 if ((natl != NULL) && 499d6c23f6fSyx160601 (np->in_pnext != 0) && (st_port == np->in_pnext) && 500d6c23f6fSyx160601 !IP6_ISZERO(&np->in_next6) && 501d6c23f6fSyx160601 IP6_EQ(&st_ip, &np->in_next6)) 502d6c23f6fSyx160601 return -1; 503d6c23f6fSyx160601 l++; 504d6c23f6fSyx160601 } while (natl != NULL); 505d6c23f6fSyx160601 506d6c23f6fSyx160601 if (np->in_space > 0) 507d6c23f6fSyx160601 np->in_space--; 508d6c23f6fSyx160601 509d6c23f6fSyx160601 /* Setup the NAT table */ 510d6c23f6fSyx160601 nat->nat_inip6 = fin->fin_src6; 511d6c23f6fSyx160601 nat->nat_outip6 = in; 512d6c23f6fSyx160601 nat->nat_oip6 = fin->fin_dst6; 513d6c23f6fSyx160601 if (nat->nat_hm == NULL) 514d6c23f6fSyx160601 nat->nat_hm = nat6_hostmap(np, &fin->fin_src6, &fin->fin_dst6, 515d6c23f6fSyx160601 &nat->nat_outip6, 0, ifs); 516d6c23f6fSyx160601 517d6c23f6fSyx160601 if (flags & IPN_TCPUDP) { 518d6c23f6fSyx160601 nat->nat_inport = sport; 519d6c23f6fSyx160601 nat->nat_outport = port; /* sport */ 520d6c23f6fSyx160601 nat->nat_oport = dport; 521d6c23f6fSyx160601 ((tcphdr_t *)fin->fin_dp)->th_sport = port; 522d6c23f6fSyx160601 } else if (flags & IPN_ICMPQUERY) { 523d6c23f6fSyx160601 ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = port; 524d6c23f6fSyx160601 nat->nat_inport = port; 525d6c23f6fSyx160601 nat->nat_outport = port; 526d6c23f6fSyx160601 } 527d6c23f6fSyx160601 528d6c23f6fSyx160601 ni->nai_port = port; 529d6c23f6fSyx160601 ni->nai_nport = dport; 530d6c23f6fSyx160601 return 0; 531d6c23f6fSyx160601 } 532d6c23f6fSyx160601 533d6c23f6fSyx160601 534d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 535d6c23f6fSyx160601 /* Function: nat6_newrdr */ 536d6c23f6fSyx160601 /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ 537d6c23f6fSyx160601 /* allow rule to be moved if IPN_ROUNDR is set. */ 538d6c23f6fSyx160601 /* Parameters: fin(I) - pointer to packet information */ 539d6c23f6fSyx160601 /* nat(I) - pointer to NAT entry */ 540d6c23f6fSyx160601 /* ni(I) - pointer to structure with misc. information needed */ 541d6c23f6fSyx160601 /* to create new NAT entry. */ 542d6c23f6fSyx160601 /* */ 543d6c23f6fSyx160601 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 544d6c23f6fSyx160601 /* to the new IP address for the translation. */ 545d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 546d6c23f6fSyx160601 static INLINE int nat6_newrdr(fin, nat, ni) 547d6c23f6fSyx160601 fr_info_t *fin; 548d6c23f6fSyx160601 nat_t *nat; 549d6c23f6fSyx160601 natinfo_t *ni; 550d6c23f6fSyx160601 { 551d6c23f6fSyx160601 u_short nport, dport, sport; 552d6c23f6fSyx160601 i6addr_t in; 553d6c23f6fSyx160601 u_short sp, dp; 554d6c23f6fSyx160601 hostmap_t *hm; 555d6c23f6fSyx160601 u_32_t flags; 556d6c23f6fSyx160601 ipnat_t *np; 557d6c23f6fSyx160601 nat_t *natl; 558d6c23f6fSyx160601 int move; 559d6c23f6fSyx160601 ipf_stack_t *ifs = fin->fin_ifs; 560d6c23f6fSyx160601 561d6c23f6fSyx160601 move = 1; 562d6c23f6fSyx160601 hm = NULL; 563d6c23f6fSyx160601 in.i6[0] = 0; 564d6c23f6fSyx160601 in.i6[1] = 0; 565d6c23f6fSyx160601 in.i6[2] = 0; 566d6c23f6fSyx160601 in.i6[3] = 0; 567d6c23f6fSyx160601 np = ni->nai_np; 568d6c23f6fSyx160601 flags = ni->nai_flags; 569d6c23f6fSyx160601 sport = ni->nai_sport; 570d6c23f6fSyx160601 dport = ni->nai_dport; 571d6c23f6fSyx160601 572d6c23f6fSyx160601 /* 573d6c23f6fSyx160601 * If the matching rule has IPN_STICKY set, then we want to have the 574d6c23f6fSyx160601 * same rule kick in as before. Why would this happen? If you have 575d6c23f6fSyx160601 * a collection of rdr rules with "round-robin sticky", the current 576d6c23f6fSyx160601 * packet might match a different one to the previous connection but 577d6c23f6fSyx160601 * we want the same destination to be used. 578d6c23f6fSyx160601 */ 579d6c23f6fSyx160601 if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == 580d6c23f6fSyx160601 (IPN_ROUNDR|IPN_STICKY)) { 581d6c23f6fSyx160601 hm = nat6_hostmap(NULL, &fin->fin_src6, &fin->fin_dst6, &in, 582d6c23f6fSyx160601 (u_32_t)dport, ifs); 583d6c23f6fSyx160601 if (hm != NULL) { 584d6c23f6fSyx160601 in = hm->hm_map; 585d6c23f6fSyx160601 np = hm->hm_ipnat; 586d6c23f6fSyx160601 ni->nai_np = np; 587d6c23f6fSyx160601 move = 0; 588d6c23f6fSyx160601 } 589d6c23f6fSyx160601 } 590d6c23f6fSyx160601 591d6c23f6fSyx160601 /* 592d6c23f6fSyx160601 * Otherwise, it's an inbound packet. Most likely, we don't 593d6c23f6fSyx160601 * want to rewrite source ports and source addresses. Instead, 594d6c23f6fSyx160601 * we want to rewrite to a fixed internal address and fixed 595d6c23f6fSyx160601 * internal port. 596d6c23f6fSyx160601 */ 597d6c23f6fSyx160601 if (np->in_flags & IPN_SPLIT) { 598d6c23f6fSyx160601 in = np->in_next6; 599d6c23f6fSyx160601 600d6c23f6fSyx160601 if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { 601d6c23f6fSyx160601 hm = nat6_hostmap(np, &fin->fin_src6, &fin->fin_dst6, 602d6c23f6fSyx160601 &in, (u_32_t)dport, ifs); 603d6c23f6fSyx160601 if (hm != NULL) { 604d6c23f6fSyx160601 in = hm->hm_map; 605d6c23f6fSyx160601 move = 0; 606d6c23f6fSyx160601 } 607d6c23f6fSyx160601 } 608d6c23f6fSyx160601 609d6c23f6fSyx160601 if (hm == NULL || hm->hm_ref == 1) { 610d6c23f6fSyx160601 if (IP6_EQ(&np->in_in[0], &in)) { 611d6c23f6fSyx160601 np->in_next6 = np->in_in[1]; 612d6c23f6fSyx160601 move = 0; 613d6c23f6fSyx160601 } else { 614d6c23f6fSyx160601 np->in_next6 = np->in_in[0]; 615d6c23f6fSyx160601 } 616d6c23f6fSyx160601 } 617d6c23f6fSyx160601 618d6c23f6fSyx160601 } else if (IP6_ISZERO(&np->in_in[0]) && 619d6c23f6fSyx160601 IP6_ISONES(&np->in_in[1])) { 620d6c23f6fSyx160601 /* 621d6c23f6fSyx160601 * 0/128 - use the interface's IP address. 622d6c23f6fSyx160601 */ 623d6c23f6fSyx160601 if (fr_ifpaddr(6, FRI_NORMAL, fin->fin_ifp, (void *)&in, NULL, 624d6c23f6fSyx160601 fin->fin_ifs) == -1) 625d6c23f6fSyx160601 return -1; 626d6c23f6fSyx160601 627d6c23f6fSyx160601 } else if (IP6_ISZERO(&np->in_in[0]) && 628d6c23f6fSyx160601 IP6_ISZERO(&np->in_in[1])) { 629d6c23f6fSyx160601 /* 630d6c23f6fSyx160601 * 0/0 - use the original destination address/port. 631d6c23f6fSyx160601 */ 632d6c23f6fSyx160601 in = fin->fin_dst6; 633d6c23f6fSyx160601 634d6c23f6fSyx160601 } else if (np->in_redir == NAT_BIMAP && 635d6c23f6fSyx160601 IP6_EQ(&np->in_in[1], &np->in_out[1])) { 636d6c23f6fSyx160601 i6addr_t temp; 637d6c23f6fSyx160601 /* 638d6c23f6fSyx160601 * map the address block in a 1:1 fashion 639d6c23f6fSyx160601 */ 640d6c23f6fSyx160601 temp.i6[0] = fin->fin_dst6.i6[0] & ~np->in_in[1].i6[0]; 641d6c23f6fSyx160601 temp.i6[1] = fin->fin_dst6.i6[1] & ~np->in_in[1].i6[1]; 642d6c23f6fSyx160601 temp.i6[2] = fin->fin_dst6.i6[2] & ~np->in_in[1].i6[2]; 643d6c23f6fSyx160601 temp.i6[3] = fin->fin_dst6.i6[3] & ~np->in_in[1].i6[3]; 644d6c23f6fSyx160601 in = np->in_in[0]; 645d6c23f6fSyx160601 IP6_MERGE(&in, &temp, &np->in_in[1]); 646d6c23f6fSyx160601 } else { 647d6c23f6fSyx160601 in = np->in_in[0]; 648d6c23f6fSyx160601 } 649d6c23f6fSyx160601 650d6c23f6fSyx160601 if ((np->in_pnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) 651d6c23f6fSyx160601 nport = dport; 652d6c23f6fSyx160601 else { 653d6c23f6fSyx160601 /* 654d6c23f6fSyx160601 * Whilst not optimized for the case where 655d6c23f6fSyx160601 * pmin == pmax, the gain is not significant. 656d6c23f6fSyx160601 */ 657d6c23f6fSyx160601 if (((np->in_flags & IPN_FIXEDDPORT) == 0) && 658d6c23f6fSyx160601 (np->in_pmin != np->in_pmax)) { 659d6c23f6fSyx160601 nport = ntohs(dport) - ntohs(np->in_pmin) + 660d6c23f6fSyx160601 ntohs(np->in_pnext); 661d6c23f6fSyx160601 nport = htons(nport); 662d6c23f6fSyx160601 } else 663d6c23f6fSyx160601 nport = np->in_pnext; 664d6c23f6fSyx160601 } 665d6c23f6fSyx160601 666d6c23f6fSyx160601 /* 667d6c23f6fSyx160601 * When the redirect-to address is set to 0.0.0.0, just 668d6c23f6fSyx160601 * assume a blank `forwarding' of the packet. We don't 669d6c23f6fSyx160601 * setup any translation for this either. 670d6c23f6fSyx160601 */ 671d6c23f6fSyx160601 if (IP6_ISZERO(&in)) { 672d6c23f6fSyx160601 if (nport == dport) 673d6c23f6fSyx160601 return -1; 674d6c23f6fSyx160601 in = fin->fin_dst6; 675d6c23f6fSyx160601 } 676d6c23f6fSyx160601 677d6c23f6fSyx160601 /* 678d6c23f6fSyx160601 * Check to see if this redirect mapping already exists and if 679d6c23f6fSyx160601 * it does, return "failure" (allowing it to be created will just 680d6c23f6fSyx160601 * cause one or both of these "connections" to stop working.) 681d6c23f6fSyx160601 */ 682d6c23f6fSyx160601 sp = fin->fin_data[0]; 683d6c23f6fSyx160601 dp = fin->fin_data[1]; 684d6c23f6fSyx160601 fin->fin_data[1] = fin->fin_data[0]; 685d6c23f6fSyx160601 fin->fin_data[0] = ntohs(nport); 686d6c23f6fSyx160601 natl = nat6_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 687d6c23f6fSyx160601 (u_int)fin->fin_p, &in.in6, &fin->fin_src6.in6); 688d6c23f6fSyx160601 fin->fin_data[0] = sp; 689d6c23f6fSyx160601 fin->fin_data[1] = dp; 690d6c23f6fSyx160601 if (natl != NULL) 691d6c23f6fSyx160601 return -1; 692d6c23f6fSyx160601 693d6c23f6fSyx160601 nat->nat_inip6 = in; 694d6c23f6fSyx160601 nat->nat_outip6 = fin->fin_dst6; 695d6c23f6fSyx160601 nat->nat_oip6 = fin->fin_src6; 696d6c23f6fSyx160601 if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0)) 697d6c23f6fSyx160601 nat->nat_hm = nat6_hostmap(np, &fin->fin_src6, 698d6c23f6fSyx160601 &fin->fin_dst6, &in, (u_32_t)dport, ifs); 699d6c23f6fSyx160601 700d6c23f6fSyx160601 ni->nai_nport = nport; 701d6c23f6fSyx160601 ni->nai_port = sport; 702d6c23f6fSyx160601 703d6c23f6fSyx160601 if (flags & IPN_TCPUDP) { 704d6c23f6fSyx160601 nat->nat_inport = nport; 705d6c23f6fSyx160601 nat->nat_outport = dport; 706d6c23f6fSyx160601 nat->nat_oport = sport; 707d6c23f6fSyx160601 ((tcphdr_t *)fin->fin_dp)->th_dport = nport; 708d6c23f6fSyx160601 } else if (flags & IPN_ICMPQUERY) { 709d6c23f6fSyx160601 ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = nport; 710d6c23f6fSyx160601 nat->nat_inport = nport; 711d6c23f6fSyx160601 nat->nat_outport = nport; 712d6c23f6fSyx160601 } 713d6c23f6fSyx160601 714d6c23f6fSyx160601 return move; 715d6c23f6fSyx160601 } 716d6c23f6fSyx160601 717d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 718d6c23f6fSyx160601 /* Function: nat6_new */ 719d6c23f6fSyx160601 /* Returns: nat_t* - NULL == failure to create new NAT structure, */ 720d6c23f6fSyx160601 /* else pointer to new NAT structure */ 721d6c23f6fSyx160601 /* Parameters: fin(I) - pointer to packet information */ 722d6c23f6fSyx160601 /* np(I) - pointer to NAT rule */ 723d6c23f6fSyx160601 /* natsave(I) - pointer to where to store NAT struct pointer */ 724d6c23f6fSyx160601 /* flags(I) - flags describing the current packet */ 725d6c23f6fSyx160601 /* direction(I) - direction of packet (in/out) */ 726d6c23f6fSyx160601 /* Write Lock: ipf_nat */ 727d6c23f6fSyx160601 /* */ 728d6c23f6fSyx160601 /* Attempts to create a new NAT entry. Does not actually change the packet */ 729d6c23f6fSyx160601 /* in any way. */ 730d6c23f6fSyx160601 /* */ 731d6c23f6fSyx160601 /* This fucntion is in three main parts: (1) deal with creating a new NAT */ 732d6c23f6fSyx160601 /* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */ 733d6c23f6fSyx160601 /* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */ 734d6c23f6fSyx160601 /* and (3) building that structure and putting it into the NAT table(s). */ 735d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 736d6c23f6fSyx160601 nat_t *nat6_new(fin, np, natsave, flags, direction) 737d6c23f6fSyx160601 fr_info_t *fin; 738d6c23f6fSyx160601 ipnat_t *np; 739d6c23f6fSyx160601 nat_t **natsave; 740d6c23f6fSyx160601 u_int flags; 741d6c23f6fSyx160601 int direction; 742d6c23f6fSyx160601 { 743d6c23f6fSyx160601 tcphdr_t *tcp = NULL; 744d6c23f6fSyx160601 hostmap_t *hm = NULL; 745d6c23f6fSyx160601 nat_t *nat, *natl; 746d6c23f6fSyx160601 u_int nflags; 747d6c23f6fSyx160601 natinfo_t ni; 748d6c23f6fSyx160601 int move; 749d6c23f6fSyx160601 ipf_stack_t *ifs = fin->fin_ifs; 750d6c23f6fSyx160601 751d6c23f6fSyx160601 /* 752ea8244dcSJohn Ojemann * Trigger automatic call to ipf_extraflush() if the 753d6c23f6fSyx160601 * table has reached capcity specified by hi watermark. 754d6c23f6fSyx160601 */ 755ea8244dcSJohn Ojemann if (NAT_TAB_WATER_LEVEL(ifs) > ifs->ifs_nat_flush_level_hi) 756d6c23f6fSyx160601 ifs->ifs_nat_doflush = 1; 757d6c23f6fSyx160601 758d6c23f6fSyx160601 if (ifs->ifs_nat_stats.ns_inuse >= ifs->ifs_ipf_nattable_max) { 759d6c23f6fSyx160601 ifs->ifs_nat_stats.ns_memfail++; 760d6c23f6fSyx160601 return NULL; 761d6c23f6fSyx160601 } 762d6c23f6fSyx160601 763d6c23f6fSyx160601 move = 1; 764d6c23f6fSyx160601 nflags = np->in_flags & flags; 765d6c23f6fSyx160601 nflags &= NAT_FROMRULE; 766d6c23f6fSyx160601 767d6c23f6fSyx160601 ni.nai_np = np; 768d6c23f6fSyx160601 ni.nai_nflags = nflags; 769d6c23f6fSyx160601 ni.nai_flags = flags; 770d6c23f6fSyx160601 771d6c23f6fSyx160601 /* Give me a new nat */ 772d6c23f6fSyx160601 KMALLOC(nat, nat_t *); 773d6c23f6fSyx160601 if (nat == NULL) { 774d6c23f6fSyx160601 ifs->ifs_nat_stats.ns_memfail++; 775d6c23f6fSyx160601 /* 776d6c23f6fSyx160601 * Try to automatically tune the max # of entries in the 777d6c23f6fSyx160601 * table allowed to be less than what will cause kmem_alloc() 778d6c23f6fSyx160601 * to fail and try to eliminate panics due to out of memory 779d6c23f6fSyx160601 * conditions arising. 780d6c23f6fSyx160601 */ 781d6c23f6fSyx160601 if (ifs->ifs_ipf_nattable_max > ifs->ifs_ipf_nattable_sz) { 782d6c23f6fSyx160601 ifs->ifs_ipf_nattable_max = 783d6c23f6fSyx160601 ifs->ifs_nat_stats.ns_inuse - 100; 784d6c23f6fSyx160601 printf("ipf_nattable_max reduced to %d\n", 785d6c23f6fSyx160601 ifs->ifs_ipf_nattable_max); 786d6c23f6fSyx160601 } 787d6c23f6fSyx160601 return NULL; 788d6c23f6fSyx160601 } 789d6c23f6fSyx160601 790d6c23f6fSyx160601 if (flags & IPN_TCPUDP) { 791d6c23f6fSyx160601 tcp = fin->fin_dp; 792d6c23f6fSyx160601 ni.nai_sport = htons(fin->fin_sport); 793d6c23f6fSyx160601 ni.nai_dport = htons(fin->fin_dport); 794d6c23f6fSyx160601 } else if (flags & IPN_ICMPQUERY) { 795d6c23f6fSyx160601 /* 796d6c23f6fSyx160601 * In the ICMP query NAT code, we translate the ICMP id fields 797d6c23f6fSyx160601 * to make them unique. This is indepedent of the ICMP type 798d6c23f6fSyx160601 * (e.g. in the unlikely event that a host sends an echo and 799d6c23f6fSyx160601 * an tstamp request with the same id, both packets will have 800d6c23f6fSyx160601 * their ip address/id field changed in the same way). 801d6c23f6fSyx160601 * 802d6c23f6fSyx160601 * The icmp_id field is used by the sender to identify the 803d6c23f6fSyx160601 * process making the icmp request. (the receiver justs 804d6c23f6fSyx160601 * copies it back in its response). So, it closely matches 805d6c23f6fSyx160601 * the concept of source port. We overlay sport, so we can 806d6c23f6fSyx160601 * maximally reuse the existing code. 807d6c23f6fSyx160601 */ 808d6c23f6fSyx160601 ni.nai_sport = ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id; 809d6c23f6fSyx160601 ni.nai_dport = ni.nai_sport; 810d6c23f6fSyx160601 } 811d6c23f6fSyx160601 812d6c23f6fSyx160601 bzero((char *)nat, sizeof (*nat)); 813d6c23f6fSyx160601 nat->nat_flags = flags; 814d6c23f6fSyx160601 nat->nat_redir = np->in_redir; 815d6c23f6fSyx160601 816d6c23f6fSyx160601 if ((flags & NAT_SLAVE) == 0) { 817d6c23f6fSyx160601 MUTEX_ENTER(&ifs->ifs_ipf_nat_new); 818d6c23f6fSyx160601 } 819d6c23f6fSyx160601 820d6c23f6fSyx160601 /* 821d6c23f6fSyx160601 * Search the current table for a match. 822d6c23f6fSyx160601 */ 823d6c23f6fSyx160601 if (direction == NAT_OUTBOUND) { 824d6c23f6fSyx160601 /* 825d6c23f6fSyx160601 * We can now arrange to call this for the same connection 826d6c23f6fSyx160601 * because ipf_nat_new doesn't protect the code path into 827d6c23f6fSyx160601 * this function. 828d6c23f6fSyx160601 */ 829d6c23f6fSyx160601 natl = nat6_outlookup(fin, nflags, (u_int)fin->fin_p, 830d6c23f6fSyx160601 &fin->fin_src6.in6, &fin->fin_dst6.in6); 831d6c23f6fSyx160601 if (natl != NULL) { 832d6c23f6fSyx160601 KFREE(nat); 833d6c23f6fSyx160601 nat = natl; 834d6c23f6fSyx160601 goto done; 835d6c23f6fSyx160601 } 836d6c23f6fSyx160601 837d6c23f6fSyx160601 move = nat6_newmap(fin, nat, &ni); 838d6c23f6fSyx160601 if (move == -1) 839d6c23f6fSyx160601 goto badnat; 840d6c23f6fSyx160601 841d6c23f6fSyx160601 np = ni.nai_np; 842d6c23f6fSyx160601 } else { 843d6c23f6fSyx160601 /* 844d6c23f6fSyx160601 * NAT_INBOUND is used only for redirects rules 845d6c23f6fSyx160601 */ 846d6c23f6fSyx160601 natl = nat6_inlookup(fin, nflags, (u_int)fin->fin_p, 847d6c23f6fSyx160601 &fin->fin_src6.in6, &fin->fin_dst6.in6); 848d6c23f6fSyx160601 if (natl != NULL) { 849d6c23f6fSyx160601 KFREE(nat); 850d6c23f6fSyx160601 nat = natl; 851d6c23f6fSyx160601 goto done; 852d6c23f6fSyx160601 } 853d6c23f6fSyx160601 854d6c23f6fSyx160601 move = nat6_newrdr(fin, nat, &ni); 855d6c23f6fSyx160601 if (move == -1) 856d6c23f6fSyx160601 goto badnat; 857d6c23f6fSyx160601 858d6c23f6fSyx160601 np = ni.nai_np; 859d6c23f6fSyx160601 } 860d6c23f6fSyx160601 861d6c23f6fSyx160601 if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { 862d6c23f6fSyx160601 if (np->in_redir == NAT_REDIRECT) { 863d6c23f6fSyx160601 nat_delrdr(np); 864d6c23f6fSyx160601 nat6_addrdr(np, ifs); 865d6c23f6fSyx160601 } else if (np->in_redir == NAT_MAP) { 866d6c23f6fSyx160601 nat_delnat(np); 867d6c23f6fSyx160601 nat6_addnat(np, ifs); 868d6c23f6fSyx160601 } 869d6c23f6fSyx160601 } 870d6c23f6fSyx160601 871d6c23f6fSyx160601 if (nat6_finalise(fin, nat, &ni, tcp, natsave, direction) == -1) { 872d6c23f6fSyx160601 goto badnat; 873d6c23f6fSyx160601 } 874d6c23f6fSyx160601 875d6c23f6fSyx160601 nat_calc_chksum_diffs(nat); 876d6c23f6fSyx160601 877d6c23f6fSyx160601 if (flags & SI_WILDP) 878d6c23f6fSyx160601 ifs->ifs_nat_stats.ns_wilds++; 879d6c23f6fSyx160601 goto done; 880d6c23f6fSyx160601 badnat: 881d6c23f6fSyx160601 ifs->ifs_nat_stats.ns_badnat++; 882d6c23f6fSyx160601 if ((hm = nat->nat_hm) != NULL) 883d6c23f6fSyx160601 fr_hostmapdel(&hm); 884d6c23f6fSyx160601 KFREE(nat); 885d6c23f6fSyx160601 nat = NULL; 886d6c23f6fSyx160601 done: 887d6c23f6fSyx160601 if ((flags & NAT_SLAVE) == 0) { 888d6c23f6fSyx160601 MUTEX_EXIT(&ifs->ifs_ipf_nat_new); 889d6c23f6fSyx160601 } 890d6c23f6fSyx160601 return nat; 891d6c23f6fSyx160601 } 892d6c23f6fSyx160601 893d6c23f6fSyx160601 894d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 895d6c23f6fSyx160601 /* Function: nat6_finalise */ 896d6c23f6fSyx160601 /* Returns: int - 0 == sucess, -1 == failure */ 897d6c23f6fSyx160601 /* Parameters: fin(I) - pointer to packet information */ 898d6c23f6fSyx160601 /* nat(I) - pointer to NAT entry */ 899d6c23f6fSyx160601 /* ni(I) - pointer to structure with misc. information needed */ 900d6c23f6fSyx160601 /* to create new NAT entry. */ 901d6c23f6fSyx160601 /* Write Lock: ipf_nat */ 902d6c23f6fSyx160601 /* */ 903d6c23f6fSyx160601 /* This is the tail end of constructing a new NAT entry and is the same */ 904d6c23f6fSyx160601 /* for both IPv4 and IPv6. */ 905d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 906d6c23f6fSyx160601 /*ARGSUSED*/ 907d6c23f6fSyx160601 static INLINE int nat6_finalise(fin, nat, ni, tcp, natsave, direction) 908d6c23f6fSyx160601 fr_info_t *fin; 909d6c23f6fSyx160601 nat_t *nat; 910d6c23f6fSyx160601 natinfo_t *ni; 911d6c23f6fSyx160601 tcphdr_t *tcp; 912d6c23f6fSyx160601 nat_t **natsave; 913d6c23f6fSyx160601 int direction; 914d6c23f6fSyx160601 { 915d6c23f6fSyx160601 frentry_t *fr; 916d6c23f6fSyx160601 ipnat_t *np; 917d6c23f6fSyx160601 ipf_stack_t *ifs = fin->fin_ifs; 918d6c23f6fSyx160601 919d6c23f6fSyx160601 np = ni->nai_np; 920d6c23f6fSyx160601 921d6c23f6fSyx160601 COPYIFNAME(fin->fin_ifp, nat->nat_ifnames[0], fin->fin_v); 922d6c23f6fSyx160601 923d6c23f6fSyx160601 #ifdef IPFILTER_SYNC 924d6c23f6fSyx160601 if ((nat->nat_flags & SI_CLONE) == 0) 925d6c23f6fSyx160601 nat->nat_sync = ipfsync_new(SMC_NAT, fin, nat); 926d6c23f6fSyx160601 #endif 927d6c23f6fSyx160601 928d6c23f6fSyx160601 nat->nat_me = natsave; 929d6c23f6fSyx160601 nat->nat_dir = direction; 930d6c23f6fSyx160601 nat->nat_ifps[0] = np->in_ifps[0]; 931d6c23f6fSyx160601 nat->nat_ifps[1] = np->in_ifps[1]; 932d6c23f6fSyx160601 nat->nat_ptr = np; 933d6c23f6fSyx160601 nat->nat_p = fin->fin_p; 934d6c23f6fSyx160601 nat->nat_v = fin->fin_v; 935d6c23f6fSyx160601 nat->nat_mssclamp = np->in_mssclamp; 936d6c23f6fSyx160601 fr = fin->fin_fr; 937d6c23f6fSyx160601 nat->nat_fr = fr; 938d6c23f6fSyx160601 nat->nat_v = 6; 939d6c23f6fSyx160601 940d6c23f6fSyx160601 #ifdef IPF_V6_PROXIES 941d6c23f6fSyx160601 if ((np->in_apr != NULL) && ((ni->nai_flags & NAT_SLAVE) == 0)) 942d6c23f6fSyx160601 if (appr_new(fin, nat) == -1) 943d6c23f6fSyx160601 return -1; 944d6c23f6fSyx160601 #endif 945d6c23f6fSyx160601 946d6c23f6fSyx160601 if (nat6_insert(nat, fin->fin_rev, ifs) == 0) { 947d6c23f6fSyx160601 if (ifs->ifs_nat_logging) 948d6c23f6fSyx160601 nat_log(nat, (u_int)np->in_redir, ifs); 949d6c23f6fSyx160601 np->in_use++; 950d6c23f6fSyx160601 if (fr != NULL) { 951d6c23f6fSyx160601 MUTEX_ENTER(&fr->fr_lock); 952d6c23f6fSyx160601 fr->fr_ref++; 953d6c23f6fSyx160601 MUTEX_EXIT(&fr->fr_lock); 954d6c23f6fSyx160601 } 955d6c23f6fSyx160601 return 0; 956d6c23f6fSyx160601 } 957d6c23f6fSyx160601 958d6c23f6fSyx160601 /* 959d6c23f6fSyx160601 * nat6_insert failed, so cleanup time... 960d6c23f6fSyx160601 */ 961d6c23f6fSyx160601 return -1; 962d6c23f6fSyx160601 } 963d6c23f6fSyx160601 964d6c23f6fSyx160601 965d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 966d6c23f6fSyx160601 /* Function: nat6_insert */ 967d6c23f6fSyx160601 /* Returns: int - 0 == sucess, -1 == failure */ 968d6c23f6fSyx160601 /* Parameters: nat(I) - pointer to NAT structure */ 969d6c23f6fSyx160601 /* rev(I) - flag indicating forward/reverse direction of packet */ 970d6c23f6fSyx160601 /* Write Lock: ipf_nat */ 971d6c23f6fSyx160601 /* */ 972d6c23f6fSyx160601 /* Insert a NAT entry into the hash tables for searching and add it to the */ 973d6c23f6fSyx160601 /* list of active NAT entries. Adjust global counters when complete. */ 974d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 975d6c23f6fSyx160601 int nat6_insert(nat, rev, ifs) 976d6c23f6fSyx160601 nat_t *nat; 977d6c23f6fSyx160601 int rev; 978d6c23f6fSyx160601 ipf_stack_t *ifs; 979d6c23f6fSyx160601 { 980d6c23f6fSyx160601 u_int hv1, hv2; 981d6c23f6fSyx160601 nat_t **natp; 982d6c23f6fSyx160601 983d6c23f6fSyx160601 /* 984d6c23f6fSyx160601 * Try and return an error as early as possible, so calculate the hash 985d6c23f6fSyx160601 * entry numbers first and then proceed. 986d6c23f6fSyx160601 */ 987d6c23f6fSyx160601 if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { 988d6c23f6fSyx160601 hv1 = NAT_HASH_FN6(&nat->nat_inip6, nat->nat_inport, 989d6c23f6fSyx160601 0xffffffff); 990d6c23f6fSyx160601 hv1 = NAT_HASH_FN6(&nat->nat_oip6, hv1 + nat->nat_oport, 991d6c23f6fSyx160601 ifs->ifs_ipf_nattable_sz); 992d6c23f6fSyx160601 hv2 = NAT_HASH_FN6(&nat->nat_outip6, nat->nat_outport, 993d6c23f6fSyx160601 0xffffffff); 994d6c23f6fSyx160601 hv2 = NAT_HASH_FN6(&nat->nat_oip6, hv2 + nat->nat_oport, 995d6c23f6fSyx160601 ifs->ifs_ipf_nattable_sz); 996d6c23f6fSyx160601 } else { 997d6c23f6fSyx160601 hv1 = NAT_HASH_FN6(&nat->nat_inip6, 0, 0xffffffff); 998d6c23f6fSyx160601 hv1 = NAT_HASH_FN6(&nat->nat_oip6, hv1, 999d6c23f6fSyx160601 ifs->ifs_ipf_nattable_sz); 1000d6c23f6fSyx160601 hv2 = NAT_HASH_FN6(&nat->nat_outip6, 0, 0xffffffff); 1001d6c23f6fSyx160601 hv2 = NAT_HASH_FN6(&nat->nat_oip6, hv2, 1002d6c23f6fSyx160601 ifs->ifs_ipf_nattable_sz); 1003d6c23f6fSyx160601 } 1004d6c23f6fSyx160601 1005d6c23f6fSyx160601 if ((ifs->ifs_nat_stats.ns_bucketlen[0][hv1] >= 1006d6c23f6fSyx160601 ifs->ifs_fr_nat_maxbucket) || 1007d6c23f6fSyx160601 (ifs->ifs_nat_stats.ns_bucketlen[1][hv2] >= 1008d6c23f6fSyx160601 ifs->ifs_fr_nat_maxbucket)) { 1009d6c23f6fSyx160601 return -1; 1010d6c23f6fSyx160601 } 1011d6c23f6fSyx160601 1012d6c23f6fSyx160601 nat->nat_hv[0] = hv1; 1013d6c23f6fSyx160601 nat->nat_hv[1] = hv2; 1014d6c23f6fSyx160601 1015d6c23f6fSyx160601 MUTEX_INIT(&nat->nat_lock, "nat entry lock"); 1016d6c23f6fSyx160601 1017d6c23f6fSyx160601 nat->nat_rev = rev; 1018d6c23f6fSyx160601 nat->nat_ref = 1; 1019d6c23f6fSyx160601 nat->nat_bytes[0] = 0; 1020d6c23f6fSyx160601 nat->nat_pkts[0] = 0; 1021d6c23f6fSyx160601 nat->nat_bytes[1] = 0; 1022d6c23f6fSyx160601 nat->nat_pkts[1] = 0; 1023d6c23f6fSyx160601 1024d6c23f6fSyx160601 nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; 1025d6c23f6fSyx160601 nat->nat_ifps[0] = fr_resolvenic(nat->nat_ifnames[0], 6, ifs); 1026d6c23f6fSyx160601 1027d6c23f6fSyx160601 if (nat->nat_ifnames[1][0] !='\0') { 1028d6c23f6fSyx160601 nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 1029d6c23f6fSyx160601 nat->nat_ifps[1] = fr_resolvenic(nat->nat_ifnames[1], 6, ifs); 1030d6c23f6fSyx160601 } else { 1031d6c23f6fSyx160601 (void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0], 1032d6c23f6fSyx160601 LIFNAMSIZ); 1033d6c23f6fSyx160601 nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 1034d6c23f6fSyx160601 nat->nat_ifps[1] = nat->nat_ifps[0]; 1035d6c23f6fSyx160601 } 1036d6c23f6fSyx160601 1037d6c23f6fSyx160601 nat->nat_next = ifs->ifs_nat_instances; 1038d6c23f6fSyx160601 nat->nat_pnext = &ifs->ifs_nat_instances; 1039d6c23f6fSyx160601 if (ifs->ifs_nat_instances) 1040d6c23f6fSyx160601 ifs->ifs_nat_instances->nat_pnext = &nat->nat_next; 1041d6c23f6fSyx160601 ifs->ifs_nat_instances = nat; 1042d6c23f6fSyx160601 1043d6c23f6fSyx160601 natp = &ifs->ifs_nat_table[0][hv1]; 1044d6c23f6fSyx160601 if (*natp) 1045d6c23f6fSyx160601 (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 1046d6c23f6fSyx160601 nat->nat_phnext[0] = natp; 1047d6c23f6fSyx160601 nat->nat_hnext[0] = *natp; 1048d6c23f6fSyx160601 *natp = nat; 1049d6c23f6fSyx160601 ifs->ifs_nat_stats.ns_bucketlen[0][hv1]++; 1050d6c23f6fSyx160601 1051d6c23f6fSyx160601 natp = &ifs->ifs_nat_table[1][hv2]; 1052d6c23f6fSyx160601 if (*natp) 1053d6c23f6fSyx160601 (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 1054d6c23f6fSyx160601 nat->nat_phnext[1] = natp; 1055d6c23f6fSyx160601 nat->nat_hnext[1] = *natp; 1056d6c23f6fSyx160601 *natp = nat; 1057d6c23f6fSyx160601 ifs->ifs_nat_stats.ns_bucketlen[1][hv2]++; 1058d6c23f6fSyx160601 1059d6c23f6fSyx160601 fr_setnatqueue(nat, rev, ifs); 1060d6c23f6fSyx160601 1061d6c23f6fSyx160601 ifs->ifs_nat_stats.ns_added++; 1062d6c23f6fSyx160601 ifs->ifs_nat_stats.ns_inuse++; 1063d6c23f6fSyx160601 return 0; 1064d6c23f6fSyx160601 } 1065d6c23f6fSyx160601 1066d6c23f6fSyx160601 1067d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 1068d6c23f6fSyx160601 /* Function: nat6_icmperrorlookup */ 1069d6c23f6fSyx160601 /* Returns: nat_t* - point to matching NAT structure */ 1070d6c23f6fSyx160601 /* Parameters: fin(I) - pointer to packet information */ 1071d6c23f6fSyx160601 /* dir(I) - direction of packet (in/out) */ 1072d6c23f6fSyx160601 /* */ 1073d6c23f6fSyx160601 /* Check if the ICMP error message is related to an existing TCP, UDP or */ 1074d6c23f6fSyx160601 /* ICMP query nat entry. It is assumed that the packet is already of the */ 1075d6c23f6fSyx160601 /* the required length. */ 1076d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 1077d6c23f6fSyx160601 nat_t *nat6_icmperrorlookup(fin, dir) 1078d6c23f6fSyx160601 fr_info_t *fin; 1079d6c23f6fSyx160601 int dir; 1080d6c23f6fSyx160601 { 1081d6c23f6fSyx160601 int flags = 0, minlen; 1082d6c23f6fSyx160601 struct icmp6_hdr *orgicmp; 1083d6c23f6fSyx160601 tcphdr_t *tcp = NULL; 1084d6c23f6fSyx160601 u_short data[2]; 1085d6c23f6fSyx160601 nat_t *nat; 1086d6c23f6fSyx160601 ip6_t *oip6; 1087d6c23f6fSyx160601 u_int p; 1088d6c23f6fSyx160601 1089d6c23f6fSyx160601 minlen = 40; 1090d6c23f6fSyx160601 /* 1091d6c23f6fSyx160601 * Does it at least have the return (basic) IP header ? 1092d6c23f6fSyx160601 * Only a basic IP header (no options) should be with an ICMP error 1093d6c23f6fSyx160601 * header. Also, if it's not an error type, then return. 1094d6c23f6fSyx160601 */ 1095d6c23f6fSyx160601 if (!(fin->fin_flx & FI_ICMPERR)) 1096d6c23f6fSyx160601 return NULL; 1097d6c23f6fSyx160601 1098d6c23f6fSyx160601 /* 1099d6c23f6fSyx160601 * Check packet size 1100d6c23f6fSyx160601 */ 1101d6c23f6fSyx160601 if (fin->fin_plen < ICMP6ERR_IPICMPHLEN) 1102d6c23f6fSyx160601 return NULL; 1103d6c23f6fSyx160601 oip6 = (ip6_t *)((char *)fin->fin_dp + 8); 1104d6c23f6fSyx160601 1105d6c23f6fSyx160601 /* 1106d6c23f6fSyx160601 * Is the buffer big enough for all of it ? It's the size of the IP 1107d6c23f6fSyx160601 * header claimed in the encapsulated part which is of concern. It 1108d6c23f6fSyx160601 * may be too big to be in this buffer but not so big that it's 1109d6c23f6fSyx160601 * outside the ICMP packet, leading to TCP deref's causing problems. 1110d6c23f6fSyx160601 * This is possible because we don't know how big oip_hl is when we 1111d6c23f6fSyx160601 * do the pullup early in fr_check() and thus can't gaurantee it is 1112d6c23f6fSyx160601 * all here now. 1113d6c23f6fSyx160601 */ 1114d6c23f6fSyx160601 #ifdef _KERNEL 1115d6c23f6fSyx160601 { 1116d6c23f6fSyx160601 mb_t *m; 1117d6c23f6fSyx160601 1118d6c23f6fSyx160601 m = fin->fin_m; 1119d6c23f6fSyx160601 # if defined(MENTAT) 1120d6c23f6fSyx160601 if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr) 1121d6c23f6fSyx160601 return NULL; 1122d6c23f6fSyx160601 # else 1123d6c23f6fSyx160601 if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN > 1124d6c23f6fSyx160601 (char *)fin->fin_ip + M_LEN(m)) 1125d6c23f6fSyx160601 return NULL; 1126d6c23f6fSyx160601 # endif 1127d6c23f6fSyx160601 } 1128d6c23f6fSyx160601 #endif 1129d6c23f6fSyx160601 1130d6c23f6fSyx160601 if (IP6_NEQ(&fin->fin_dst6, &oip6->ip6_src)) 1131d6c23f6fSyx160601 return NULL; 1132d6c23f6fSyx160601 1133d6c23f6fSyx160601 p = oip6->ip6_nxt; 1134d6c23f6fSyx160601 if (p == IPPROTO_TCP) 1135d6c23f6fSyx160601 flags = IPN_TCP; 1136d6c23f6fSyx160601 else if (p == IPPROTO_UDP) 1137d6c23f6fSyx160601 flags = IPN_UDP; 1138d6c23f6fSyx160601 else if (p == IPPROTO_ICMPV6) { 1139d6c23f6fSyx160601 orgicmp = (struct icmp6_hdr *)(oip6 + 1); 1140d6c23f6fSyx160601 1141d6c23f6fSyx160601 /* see if this is related to an ICMP query */ 1142d6c23f6fSyx160601 if (nat_icmpquerytype6(orgicmp->icmp6_type)) { 1143d6c23f6fSyx160601 data[0] = fin->fin_data[0]; 1144d6c23f6fSyx160601 data[1] = fin->fin_data[1]; 1145d6c23f6fSyx160601 fin->fin_data[0] = 0; 1146d6c23f6fSyx160601 fin->fin_data[1] = orgicmp->icmp6_id; 1147d6c23f6fSyx160601 1148d6c23f6fSyx160601 flags = IPN_ICMPERR|IPN_ICMPQUERY; 1149d6c23f6fSyx160601 /* 1150d6c23f6fSyx160601 * NOTE : dir refers to the direction of the original 1151d6c23f6fSyx160601 * ip packet. By definition the icmp error 1152d6c23f6fSyx160601 * message flows in the opposite direction. 1153d6c23f6fSyx160601 */ 1154d6c23f6fSyx160601 if (dir == NAT_INBOUND) 1155d6c23f6fSyx160601 nat = nat6_inlookup(fin, flags, p, 1156d6c23f6fSyx160601 &oip6->ip6_dst, &oip6->ip6_src); 1157d6c23f6fSyx160601 else 1158d6c23f6fSyx160601 nat = nat6_outlookup(fin, flags, p, 1159d6c23f6fSyx160601 &oip6->ip6_dst, &oip6->ip6_src); 1160d6c23f6fSyx160601 fin->fin_data[0] = data[0]; 1161d6c23f6fSyx160601 fin->fin_data[1] = data[1]; 1162d6c23f6fSyx160601 return nat; 1163d6c23f6fSyx160601 } 1164d6c23f6fSyx160601 } 1165d6c23f6fSyx160601 1166d6c23f6fSyx160601 if (flags & IPN_TCPUDP) { 1167d6c23f6fSyx160601 minlen += 8; /* + 64bits of data to get ports */ 1168d6c23f6fSyx160601 if (fin->fin_plen < ICMPERR_ICMPHLEN + minlen) 1169d6c23f6fSyx160601 return NULL; 1170d6c23f6fSyx160601 1171d6c23f6fSyx160601 data[0] = fin->fin_data[0]; 1172d6c23f6fSyx160601 data[1] = fin->fin_data[1]; 1173d6c23f6fSyx160601 tcp = (tcphdr_t *)(oip6 + 1); 1174d6c23f6fSyx160601 fin->fin_data[0] = ntohs(tcp->th_dport); 1175d6c23f6fSyx160601 fin->fin_data[1] = ntohs(tcp->th_sport); 1176d6c23f6fSyx160601 1177d6c23f6fSyx160601 if (dir == NAT_INBOUND) { 1178d6c23f6fSyx160601 nat = nat6_inlookup(fin, flags, p, 1179d6c23f6fSyx160601 &oip6->ip6_dst, &oip6->ip6_src); 1180d6c23f6fSyx160601 } else { 1181d6c23f6fSyx160601 nat = nat6_outlookup(fin, flags, p, 1182d6c23f6fSyx160601 &oip6->ip6_dst, &oip6->ip6_src); 1183d6c23f6fSyx160601 } 1184d6c23f6fSyx160601 fin->fin_data[0] = data[0]; 1185d6c23f6fSyx160601 fin->fin_data[1] = data[1]; 1186d6c23f6fSyx160601 return nat; 1187d6c23f6fSyx160601 } 1188d6c23f6fSyx160601 if (dir == NAT_INBOUND) 1189d6c23f6fSyx160601 return nat6_inlookup(fin, 0, p, &oip6->ip6_dst, &oip6->ip6_src); 1190d6c23f6fSyx160601 else 1191d6c23f6fSyx160601 return nat6_outlookup(fin, 0, p, &oip6->ip6_dst, 1192d6c23f6fSyx160601 &oip6->ip6_src); 1193d6c23f6fSyx160601 } 1194d6c23f6fSyx160601 1195d6c23f6fSyx160601 1196d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 1197d6c23f6fSyx160601 /* Function: nat6_icmperror */ 1198d6c23f6fSyx160601 /* Returns: nat_t* - point to matching NAT structure */ 1199d6c23f6fSyx160601 /* Parameters: fin(I) - pointer to packet information */ 1200d6c23f6fSyx160601 /* nflags(I) - NAT flags for this packet */ 1201d6c23f6fSyx160601 /* dir(I) - direction of packet (in/out) */ 1202d6c23f6fSyx160601 /* */ 1203d6c23f6fSyx160601 /* Fix up an ICMP packet which is an error message for an existing NAT */ 1204d6c23f6fSyx160601 /* session. This will correct both packet header data and checksums. */ 1205d6c23f6fSyx160601 /* */ 1206d6c23f6fSyx160601 /* This should *ONLY* be used for incoming ICMP error packets to make sure */ 1207d6c23f6fSyx160601 /* a NAT'd ICMP packet gets correctly recognised. */ 1208d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 1209d6c23f6fSyx160601 nat_t *nat6_icmperror(fin, nflags, dir) 1210d6c23f6fSyx160601 fr_info_t *fin; 1211d6c23f6fSyx160601 u_int *nflags; 1212d6c23f6fSyx160601 int dir; 1213d6c23f6fSyx160601 { 1214d6c23f6fSyx160601 u_32_t sum1, sum2, sumd, psum1, psum2, psumd, sumd1; 1215d6c23f6fSyx160601 i6addr_t in; 1216d6c23f6fSyx160601 struct icmp6_hdr *icmp6, *orgicmp; 1217d6c23f6fSyx160601 int dlen; 1218d6c23f6fSyx160601 udphdr_t *udp; 1219d6c23f6fSyx160601 tcphdr_t *tcp; 1220d6c23f6fSyx160601 nat_t *nat; 1221d6c23f6fSyx160601 ip6_t *oip6; 1222d6c23f6fSyx160601 if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) 1223d6c23f6fSyx160601 return NULL; 1224d6c23f6fSyx160601 1225d6c23f6fSyx160601 /* 1226d6c23f6fSyx160601 * nat6_icmperrorlookup() looks up nat entry associated with the 1227d6c23f6fSyx160601 * offending IP packet and returns pointer to the entry, or NULL 1228d6c23f6fSyx160601 * if packet wasn't natted or for `defective' packets. 1229d6c23f6fSyx160601 */ 1230d6c23f6fSyx160601 1231d6c23f6fSyx160601 if ((fin->fin_v != 6) || !(nat = nat6_icmperrorlookup(fin, dir))) 1232d6c23f6fSyx160601 return NULL; 1233d6c23f6fSyx160601 1234d6c23f6fSyx160601 sumd1 = 0; 1235d6c23f6fSyx160601 *nflags = IPN_ICMPERR; 1236d6c23f6fSyx160601 icmp6 = fin->fin_dp; 1237d6c23f6fSyx160601 oip6 = (ip6_t *)((char *)icmp6 + sizeof (*icmp6)); 1238d6c23f6fSyx160601 udp = (udphdr_t *)(((char *)oip6) + sizeof (*oip6)); 1239d6c23f6fSyx160601 tcp = (tcphdr_t *)udp; 1240d6c23f6fSyx160601 dlen = fin->fin_plen - ((char *)udp - (char *)fin->fin_ip); 1241d6c23f6fSyx160601 1242d6c23f6fSyx160601 /* 1243d6c23f6fSyx160601 * Need to adjust ICMP header to include the real IP#'s and 1244d6c23f6fSyx160601 * port #'s. There are three steps required. 1245d6c23f6fSyx160601 * 1246d6c23f6fSyx160601 * Step 1 1247d6c23f6fSyx160601 * No update needed for ip6 header checksum. 1248d6c23f6fSyx160601 * 1249d6c23f6fSyx160601 * Unlike IPv4, we need to update icmp_cksum for IPv6 address 1250d6c23f6fSyx160601 * changes because there's no ip_sum change to cancel it. 1251d6c23f6fSyx160601 */ 1252d6c23f6fSyx160601 1253d6c23f6fSyx160601 if (IP6_EQ((i6addr_t *)&oip6->ip6_dst, &nat->nat_oip6)) { 1254d6c23f6fSyx160601 sum1 = LONG_SUM6((i6addr_t *)&oip6->ip6_src); 1255d6c23f6fSyx160601 in = nat->nat_inip6; 1256d6c23f6fSyx160601 oip6->ip6_src = in.in6; 1257d6c23f6fSyx160601 } else { 1258d6c23f6fSyx160601 sum1 = LONG_SUM6((i6addr_t *)&oip6->ip6_dst); 1259d6c23f6fSyx160601 in = nat->nat_outip6; 1260d6c23f6fSyx160601 oip6->ip6_dst = in.in6; 1261d6c23f6fSyx160601 } 1262d6c23f6fSyx160601 1263d6c23f6fSyx160601 sum2 = LONG_SUM6(&in); 1264d6c23f6fSyx160601 CALC_SUMD(sum1, sum2, sumd); 1265d6c23f6fSyx160601 1266d6c23f6fSyx160601 /* 1267d6c23f6fSyx160601 * Step 2 1268d6c23f6fSyx160601 * Perform other adjustments based on protocol of offending packet. 1269d6c23f6fSyx160601 */ 1270d6c23f6fSyx160601 1271d6c23f6fSyx160601 switch (oip6->ip6_nxt) { 1272d6c23f6fSyx160601 case IPPROTO_TCP : 1273d6c23f6fSyx160601 case IPPROTO_UDP : 1274d6c23f6fSyx160601 1275d6c23f6fSyx160601 /* 1276d6c23f6fSyx160601 * For offending TCP/UDP IP packets, translate the ports 1277d6c23f6fSyx160601 * based on the NAT specification. 1278d6c23f6fSyx160601 * 1279d6c23f6fSyx160601 * Advance notice : Now it becomes complicated :-) 1280d6c23f6fSyx160601 * 1281d6c23f6fSyx160601 * Since the port and IP addresse fields are both part 1282d6c23f6fSyx160601 * of the TCP/UDP checksum of the offending IP packet, 1283d6c23f6fSyx160601 * we need to adjust that checksum as well. 1284d6c23f6fSyx160601 * 1285d6c23f6fSyx160601 * To further complicate things, the TCP/UDP checksum 1286d6c23f6fSyx160601 * may not be present. We must check to see if the 1287d6c23f6fSyx160601 * length of the data portion is big enough to hold 1288d6c23f6fSyx160601 * the checksum. In the UDP case, a test to determine 1289d6c23f6fSyx160601 * if the checksum is even set is also required. 1290d6c23f6fSyx160601 * 1291d6c23f6fSyx160601 * Any changes to an IP address, port or checksum within 1292d6c23f6fSyx160601 * the ICMP packet requires a change to icmp_cksum. 1293d6c23f6fSyx160601 * 1294d6c23f6fSyx160601 * Be extremely careful here ... The change is dependent 1295d6c23f6fSyx160601 * upon whether or not the TCP/UPD checksum is present. 1296d6c23f6fSyx160601 * 1297d6c23f6fSyx160601 * If TCP/UPD checksum is present, the icmp_cksum must 1298d6c23f6fSyx160601 * compensate for checksum modification resulting from 1299d6c23f6fSyx160601 * IP address change only. Port change and resulting 1300d6c23f6fSyx160601 * data checksum adjustments cancel each other out. 1301d6c23f6fSyx160601 * 1302d6c23f6fSyx160601 * If TCP/UDP checksum is not present, icmp_cksum must 1303d6c23f6fSyx160601 * compensate for port change only. The IP address 1304d6c23f6fSyx160601 * change does not modify anything else in this case. 1305d6c23f6fSyx160601 */ 1306d6c23f6fSyx160601 1307d6c23f6fSyx160601 psum1 = 0; 1308d6c23f6fSyx160601 psum2 = 0; 1309d6c23f6fSyx160601 psumd = 0; 1310d6c23f6fSyx160601 1311d6c23f6fSyx160601 if ((tcp->th_dport == nat->nat_oport) && 1312d6c23f6fSyx160601 (tcp->th_sport != nat->nat_inport)) { 1313d6c23f6fSyx160601 1314d6c23f6fSyx160601 /* 1315d6c23f6fSyx160601 * Translate the source port. 1316d6c23f6fSyx160601 */ 1317d6c23f6fSyx160601 1318d6c23f6fSyx160601 psum1 = ntohs(tcp->th_sport); 1319d6c23f6fSyx160601 psum2 = ntohs(nat->nat_inport); 1320d6c23f6fSyx160601 tcp->th_sport = nat->nat_inport; 1321d6c23f6fSyx160601 1322d6c23f6fSyx160601 } else if ((tcp->th_sport == nat->nat_oport) && 1323d6c23f6fSyx160601 (tcp->th_dport != nat->nat_outport)) { 1324d6c23f6fSyx160601 1325d6c23f6fSyx160601 /* 1326d6c23f6fSyx160601 * Translate the destination port. 1327d6c23f6fSyx160601 */ 1328d6c23f6fSyx160601 1329d6c23f6fSyx160601 psum1 = ntohs(tcp->th_dport); 1330d6c23f6fSyx160601 psum2 = ntohs(nat->nat_outport); 1331d6c23f6fSyx160601 tcp->th_dport = nat->nat_outport; 1332d6c23f6fSyx160601 } 1333d6c23f6fSyx160601 1334d6c23f6fSyx160601 if ((oip6->ip6_nxt == IPPROTO_TCP) && (dlen >= 18)) { 1335d6c23f6fSyx160601 1336d6c23f6fSyx160601 /* 1337d6c23f6fSyx160601 * TCP checksum present. 1338d6c23f6fSyx160601 * 1339d6c23f6fSyx160601 * Adjust data checksum and icmp checksum to 1340d6c23f6fSyx160601 * compensate for any IP address change. 1341d6c23f6fSyx160601 */ 1342d6c23f6fSyx160601 1343d6c23f6fSyx160601 sum1 = ntohs(tcp->th_sum); 1344d6c23f6fSyx160601 fix_datacksum(&tcp->th_sum, sumd); 1345d6c23f6fSyx160601 sum2 = ntohs(tcp->th_sum); 1346d6c23f6fSyx160601 CALC_SUMD(sum1, sum2, sumd); 1347d6c23f6fSyx160601 sumd1 += sumd; 1348d6c23f6fSyx160601 1349d6c23f6fSyx160601 /* 1350d6c23f6fSyx160601 * Also make data checksum adjustment to 1351d6c23f6fSyx160601 * compensate for any port change. 1352d6c23f6fSyx160601 */ 1353d6c23f6fSyx160601 1354d6c23f6fSyx160601 if (psum1 != psum2) { 1355d6c23f6fSyx160601 CALC_SUMD(psum1, psum2, psumd); 1356d6c23f6fSyx160601 fix_datacksum(&tcp->th_sum, psumd); 1357d6c23f6fSyx160601 } 1358d6c23f6fSyx160601 1359d6c23f6fSyx160601 } else if ((oip6->ip6_nxt == IPPROTO_UDP) && 1360d6c23f6fSyx160601 (dlen >= 8) && (udp->uh_sum != 0)) { 1361d6c23f6fSyx160601 1362d6c23f6fSyx160601 /* 1363d6c23f6fSyx160601 * The UDP checksum is present and set. 1364d6c23f6fSyx160601 * 1365d6c23f6fSyx160601 * Adjust data checksum and icmp checksum to 1366d6c23f6fSyx160601 * compensate for any IP address change. 1367d6c23f6fSyx160601 */ 1368d6c23f6fSyx160601 1369d6c23f6fSyx160601 sum1 = ntohs(udp->uh_sum); 1370d6c23f6fSyx160601 fix_datacksum(&udp->uh_sum, sumd); 1371d6c23f6fSyx160601 sum2 = ntohs(udp->uh_sum); 1372d6c23f6fSyx160601 CALC_SUMD(sum1, sum2, sumd); 1373d6c23f6fSyx160601 sumd1 += sumd; 1374d6c23f6fSyx160601 1375d6c23f6fSyx160601 /* 1376d6c23f6fSyx160601 * Also make data checksum adjustment to 1377d6c23f6fSyx160601 * compensate for any port change. 1378d6c23f6fSyx160601 */ 1379d6c23f6fSyx160601 1380d6c23f6fSyx160601 if (psum1 != psum2) { 1381d6c23f6fSyx160601 CALC_SUMD(psum1, psum2, psumd); 1382d6c23f6fSyx160601 fix_datacksum(&udp->uh_sum, psumd); 1383d6c23f6fSyx160601 } 1384d6c23f6fSyx160601 1385d6c23f6fSyx160601 } else { 1386d6c23f6fSyx160601 1387d6c23f6fSyx160601 /* 1388d6c23f6fSyx160601 * Data checksum was not present. 1389d6c23f6fSyx160601 * 1390d6c23f6fSyx160601 * Compensate for any port change. 1391d6c23f6fSyx160601 */ 1392d6c23f6fSyx160601 1393d6c23f6fSyx160601 CALC_SUMD(psum2, psum1, psumd); 1394d6c23f6fSyx160601 sumd1 += psumd; 1395d6c23f6fSyx160601 } 1396d6c23f6fSyx160601 break; 1397d6c23f6fSyx160601 1398d6c23f6fSyx160601 case IPPROTO_ICMPV6 : 1399d6c23f6fSyx160601 1400d6c23f6fSyx160601 orgicmp = (struct icmp6_hdr *)udp; 1401d6c23f6fSyx160601 1402d6c23f6fSyx160601 if ((nat->nat_dir == NAT_OUTBOUND) && 1403d6c23f6fSyx160601 (orgicmp->icmp6_id != nat->nat_inport) && 1404d6c23f6fSyx160601 (dlen >= 8)) { 1405d6c23f6fSyx160601 1406d6c23f6fSyx160601 /* 1407d6c23f6fSyx160601 * Fix ICMP checksum (of the offening ICMP 1408d6c23f6fSyx160601 * query packet) to compensate the change 1409d6c23f6fSyx160601 * in the ICMP id of the offending ICMP 1410d6c23f6fSyx160601 * packet. 1411d6c23f6fSyx160601 * 1412d6c23f6fSyx160601 * Since you modify orgicmp->icmp_id with 1413d6c23f6fSyx160601 * a delta (say x) and you compensate that 1414d6c23f6fSyx160601 * in origicmp->icmp_cksum with a delta 1415d6c23f6fSyx160601 * minus x, you don't have to adjust the 1416d6c23f6fSyx160601 * overall icmp->icmp_cksum 1417d6c23f6fSyx160601 */ 1418d6c23f6fSyx160601 1419d6c23f6fSyx160601 sum1 = ntohs(orgicmp->icmp6_id); 1420d6c23f6fSyx160601 sum2 = ntohs(nat->nat_inport); 1421d6c23f6fSyx160601 CALC_SUMD(sum1, sum2, sumd); 1422d6c23f6fSyx160601 orgicmp->icmp6_id = nat->nat_inport; 1423d6c23f6fSyx160601 fix_datacksum(&orgicmp->icmp6_cksum, sumd); 1424d6c23f6fSyx160601 1425d6c23f6fSyx160601 } /* nat_dir can't be NAT_INBOUND for icmp queries */ 1426d6c23f6fSyx160601 1427d6c23f6fSyx160601 break; 1428d6c23f6fSyx160601 1429d6c23f6fSyx160601 default : 1430d6c23f6fSyx160601 1431d6c23f6fSyx160601 break; 1432d6c23f6fSyx160601 1433d6c23f6fSyx160601 } /* switch (oip6->ip6_nxt) */ 1434d6c23f6fSyx160601 1435d6c23f6fSyx160601 /* 1436d6c23f6fSyx160601 * Step 3 1437d6c23f6fSyx160601 * Make the adjustments to icmp checksum. 1438d6c23f6fSyx160601 */ 1439d6c23f6fSyx160601 1440d6c23f6fSyx160601 if (sumd1 != 0) { 1441d6c23f6fSyx160601 sumd1 = (sumd1 & 0xffff) + (sumd1 >> 16); 1442d6c23f6fSyx160601 sumd1 = (sumd1 & 0xffff) + (sumd1 >> 16); 1443d6c23f6fSyx160601 fix_incksum(&icmp6->icmp6_cksum, sumd1); 1444d6c23f6fSyx160601 } 1445d6c23f6fSyx160601 return nat; 1446d6c23f6fSyx160601 } 1447d6c23f6fSyx160601 1448d6c23f6fSyx160601 1449d6c23f6fSyx160601 /* 1450d6c23f6fSyx160601 * NB: these lookups don't lock access to the list, it assumed that it has 1451d6c23f6fSyx160601 * already been done! 1452d6c23f6fSyx160601 */ 1453d6c23f6fSyx160601 1454d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 1455d6c23f6fSyx160601 /* Function: nat6_inlookup */ 1456d6c23f6fSyx160601 /* Returns: nat_t* - NULL == no match, */ 1457d6c23f6fSyx160601 /* else pointer to matching NAT entry */ 1458d6c23f6fSyx160601 /* Parameters: fin(I) - pointer to packet information */ 1459d6c23f6fSyx160601 /* flags(I) - NAT flags for this packet */ 1460d6c23f6fSyx160601 /* p(I) - protocol for this packet */ 1461d6c23f6fSyx160601 /* src(I) - source IP address */ 1462d6c23f6fSyx160601 /* mapdst(I) - destination IP address */ 1463d6c23f6fSyx160601 /* */ 1464d6c23f6fSyx160601 /* Lookup a nat entry based on the mapped destination ip address/port and */ 1465d6c23f6fSyx160601 /* real source address/port. We use this lookup when receiving a packet, */ 1466d6c23f6fSyx160601 /* we're looking for a table entry, based on the destination address. */ 1467d6c23f6fSyx160601 /* */ 1468d6c23f6fSyx160601 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 1469d6c23f6fSyx160601 /* */ 1470d6c23f6fSyx160601 /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */ 1471d6c23f6fSyx160601 /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 1472d6c23f6fSyx160601 /* */ 1473d6c23f6fSyx160601 /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 1474d6c23f6fSyx160601 /* the packet is of said protocol */ 1475d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 1476d6c23f6fSyx160601 nat_t *nat6_inlookup(fin, flags, p, src, mapdst) 1477d6c23f6fSyx160601 fr_info_t *fin; 1478d6c23f6fSyx160601 u_int flags, p; 1479d6c23f6fSyx160601 struct in6_addr *src, *mapdst; 1480d6c23f6fSyx160601 { 1481d6c23f6fSyx160601 u_short sport, dport; 1482d6c23f6fSyx160601 u_int sflags; 1483d6c23f6fSyx160601 nat_t *nat; 1484d6c23f6fSyx160601 int nflags; 1485d6c23f6fSyx160601 i6addr_t dst; 1486d6c23f6fSyx160601 void *ifp; 1487d6c23f6fSyx160601 u_int hv; 1488d6c23f6fSyx160601 ipf_stack_t *ifs = fin->fin_ifs; 1489d6c23f6fSyx160601 1490d6c23f6fSyx160601 if (fin != NULL) 1491d6c23f6fSyx160601 ifp = fin->fin_ifp; 1492d6c23f6fSyx160601 else 1493d6c23f6fSyx160601 ifp = NULL; 1494d6c23f6fSyx160601 sport = 0; 1495d6c23f6fSyx160601 dport = 0; 1496d6c23f6fSyx160601 dst.in6 = *mapdst; 1497d6c23f6fSyx160601 sflags = flags & NAT_TCPUDPICMP; 1498d6c23f6fSyx160601 1499d6c23f6fSyx160601 switch (p) 1500d6c23f6fSyx160601 { 1501d6c23f6fSyx160601 case IPPROTO_TCP : 1502d6c23f6fSyx160601 case IPPROTO_UDP : 1503d6c23f6fSyx160601 sport = htons(fin->fin_data[0]); 1504d6c23f6fSyx160601 dport = htons(fin->fin_data[1]); 1505d6c23f6fSyx160601 break; 1506d6c23f6fSyx160601 case IPPROTO_ICMPV6 : 1507d6c23f6fSyx160601 if (flags & IPN_ICMPERR) 1508d6c23f6fSyx160601 sport = fin->fin_data[1]; 1509d6c23f6fSyx160601 else 1510d6c23f6fSyx160601 dport = fin->fin_data[1]; 1511d6c23f6fSyx160601 break; 1512d6c23f6fSyx160601 default : 1513d6c23f6fSyx160601 break; 1514d6c23f6fSyx160601 } 1515d6c23f6fSyx160601 1516d6c23f6fSyx160601 1517d6c23f6fSyx160601 if ((flags & SI_WILDP) != 0) 1518d6c23f6fSyx160601 goto find_in_wild_ports; 1519d6c23f6fSyx160601 1520d6c23f6fSyx160601 hv = NAT_HASH_FN6(&dst, dport, 0xffffffff); 1521d6c23f6fSyx160601 hv = NAT_HASH_FN6(src, hv + sport, ifs->ifs_ipf_nattable_sz); 1522d6c23f6fSyx160601 nat = ifs->ifs_nat_table[1][hv]; 1523d6c23f6fSyx160601 for (; nat; nat = nat->nat_hnext[1]) { 1524d6c23f6fSyx160601 if (nat->nat_v != 6) 1525d6c23f6fSyx160601 continue; 1526d6c23f6fSyx160601 1527d6c23f6fSyx160601 if (nat->nat_ifps[0] != NULL) { 1528d6c23f6fSyx160601 if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 1529d6c23f6fSyx160601 continue; 1530d6c23f6fSyx160601 } else if (ifp != NULL) 1531d6c23f6fSyx160601 nat->nat_ifps[0] = ifp; 1532d6c23f6fSyx160601 1533d6c23f6fSyx160601 nflags = nat->nat_flags; 1534d6c23f6fSyx160601 1535d6c23f6fSyx160601 if (IP6_EQ(&nat->nat_oip6, src) && 1536d6c23f6fSyx160601 IP6_EQ(&nat->nat_outip6, &dst) && 1537d6c23f6fSyx160601 (((p == 0) && 1538d6c23f6fSyx160601 (sflags == (nat->nat_flags & IPN_TCPUDPICMP))) || 1539d6c23f6fSyx160601 (p == nat->nat_p))) { 1540d6c23f6fSyx160601 switch (p) 1541d6c23f6fSyx160601 { 1542d6c23f6fSyx160601 #if 0 1543d6c23f6fSyx160601 case IPPROTO_GRE : 1544d6c23f6fSyx160601 if (nat->nat_call[1] != fin->fin_data[0]) 1545d6c23f6fSyx160601 continue; 1546d6c23f6fSyx160601 break; 1547d6c23f6fSyx160601 #endif 1548d6c23f6fSyx160601 case IPPROTO_ICMPV6 : 1549d6c23f6fSyx160601 if ((flags & IPN_ICMPERR) != 0) { 1550d6c23f6fSyx160601 if (nat->nat_outport != sport) 1551d6c23f6fSyx160601 continue; 1552d6c23f6fSyx160601 } else { 1553d6c23f6fSyx160601 if (nat->nat_outport != dport) 1554d6c23f6fSyx160601 continue; 1555d6c23f6fSyx160601 } 1556d6c23f6fSyx160601 break; 1557d6c23f6fSyx160601 case IPPROTO_TCP : 1558d6c23f6fSyx160601 case IPPROTO_UDP : 1559d6c23f6fSyx160601 if (nat->nat_oport != sport) 1560d6c23f6fSyx160601 continue; 1561d6c23f6fSyx160601 if (nat->nat_outport != dport) 1562d6c23f6fSyx160601 continue; 1563d6c23f6fSyx160601 break; 1564d6c23f6fSyx160601 default : 1565d6c23f6fSyx160601 break; 1566d6c23f6fSyx160601 } 1567d6c23f6fSyx160601 1568d6c23f6fSyx160601 #ifdef IPF_V6_PROXIES 1569d6c23f6fSyx160601 ipn = nat->nat_ptr; 1570d6c23f6fSyx160601 if ((ipn != NULL) && (nat->nat_aps != NULL)) 1571d6c23f6fSyx160601 if (appr_match(fin, nat) != 0) 1572d6c23f6fSyx160601 continue; 1573d6c23f6fSyx160601 #endif 1574d6c23f6fSyx160601 return nat; 1575d6c23f6fSyx160601 } 1576d6c23f6fSyx160601 } 1577d6c23f6fSyx160601 1578d6c23f6fSyx160601 /* 1579d6c23f6fSyx160601 * So if we didn't find it but there are wildcard members in the hash 1580d6c23f6fSyx160601 * table, go back and look for them. We do this search and update here 1581d6c23f6fSyx160601 * because it is modifying the NAT table and we want to do this only 1582d6c23f6fSyx160601 * for the first packet that matches. The exception, of course, is 1583d6c23f6fSyx160601 * for "dummy" (FI_IGNORE) lookups. 1584d6c23f6fSyx160601 */ 1585d6c23f6fSyx160601 find_in_wild_ports: 1586d6c23f6fSyx160601 if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) 1587d6c23f6fSyx160601 return NULL; 1588d6c23f6fSyx160601 if (ifs->ifs_nat_stats.ns_wilds == 0) 1589d6c23f6fSyx160601 return NULL; 1590d6c23f6fSyx160601 1591d6c23f6fSyx160601 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 1592d6c23f6fSyx160601 1593d6c23f6fSyx160601 hv = NAT_HASH_FN6(&dst, 0, 0xffffffff); 1594d6c23f6fSyx160601 hv = NAT_HASH_FN6(src, hv, ifs->ifs_ipf_nattable_sz); 1595d6c23f6fSyx160601 1596d6c23f6fSyx160601 WRITE_ENTER(&ifs->ifs_ipf_nat); 1597d6c23f6fSyx160601 1598d6c23f6fSyx160601 nat = ifs->ifs_nat_table[1][hv]; 1599d6c23f6fSyx160601 for (; nat; nat = nat->nat_hnext[1]) { 1600d6c23f6fSyx160601 if (nat->nat_v != 6) 1601d6c23f6fSyx160601 continue; 1602d6c23f6fSyx160601 1603d6c23f6fSyx160601 if (nat->nat_ifps[0] != NULL) { 1604d6c23f6fSyx160601 if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 1605d6c23f6fSyx160601 continue; 1606d6c23f6fSyx160601 } else if (ifp != NULL) 1607d6c23f6fSyx160601 nat->nat_ifps[0] = ifp; 1608d6c23f6fSyx160601 1609d6c23f6fSyx160601 if (nat->nat_p != fin->fin_p) 1610d6c23f6fSyx160601 continue; 1611d6c23f6fSyx160601 if (IP6_NEQ(&nat->nat_oip6, src) || 1612d6c23f6fSyx160601 IP6_NEQ(&nat->nat_outip6, &dst)) 1613d6c23f6fSyx160601 continue; 1614d6c23f6fSyx160601 1615d6c23f6fSyx160601 nflags = nat->nat_flags; 1616d6c23f6fSyx160601 if (!(nflags & (NAT_TCPUDP|SI_WILDP))) 1617d6c23f6fSyx160601 continue; 1618d6c23f6fSyx160601 1619d6c23f6fSyx160601 if (nat_wildok(nat, (int)sport, (int)dport, nflags, 1620d6c23f6fSyx160601 NAT_INBOUND) == 1) { 1621d6c23f6fSyx160601 if ((fin->fin_flx & FI_IGNORE) != 0) 1622d6c23f6fSyx160601 break; 1623d6c23f6fSyx160601 if ((nflags & SI_CLONE) != 0) { 1624d6c23f6fSyx160601 nat = fr_natclone(fin, nat); 1625d6c23f6fSyx160601 if (nat == NULL) 1626d6c23f6fSyx160601 break; 1627d6c23f6fSyx160601 } else { 1628d6c23f6fSyx160601 MUTEX_ENTER(&ifs->ifs_ipf_nat_new); 1629d6c23f6fSyx160601 ifs->ifs_nat_stats.ns_wilds--; 1630d6c23f6fSyx160601 MUTEX_EXIT(&ifs->ifs_ipf_nat_new); 1631d6c23f6fSyx160601 } 1632d6c23f6fSyx160601 nat->nat_oport = sport; 1633d6c23f6fSyx160601 nat->nat_outport = dport; 1634d6c23f6fSyx160601 nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 1635d6c23f6fSyx160601 nat6_tabmove(nat, ifs); 1636d6c23f6fSyx160601 break; 1637d6c23f6fSyx160601 } 1638d6c23f6fSyx160601 } 1639d6c23f6fSyx160601 1640d6c23f6fSyx160601 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); 1641d6c23f6fSyx160601 1642d6c23f6fSyx160601 return nat; 1643d6c23f6fSyx160601 } 1644d6c23f6fSyx160601 1645d6c23f6fSyx160601 1646d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 1647d6c23f6fSyx160601 /* Function: nat6_tabmove */ 1648d6c23f6fSyx160601 /* Returns: Nil */ 1649d6c23f6fSyx160601 /* Parameters: nat(I) - pointer to NAT structure */ 1650d6c23f6fSyx160601 /* Write Lock: ipf_nat */ 1651d6c23f6fSyx160601 /* */ 1652d6c23f6fSyx160601 /* This function is only called for TCP/UDP NAT table entries where the */ 1653d6c23f6fSyx160601 /* original was placed in the table without hashing on the ports and we now */ 1654d6c23f6fSyx160601 /* want to include hashing on port numbers. */ 1655d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 1656d6c23f6fSyx160601 static void nat6_tabmove(nat, ifs) 1657d6c23f6fSyx160601 nat_t *nat; 1658d6c23f6fSyx160601 ipf_stack_t *ifs; 1659d6c23f6fSyx160601 { 1660d6c23f6fSyx160601 nat_t **natp; 1661d6c23f6fSyx160601 u_int hv; 1662d6c23f6fSyx160601 1663d6c23f6fSyx160601 if (nat->nat_flags & SI_CLONE) 1664d6c23f6fSyx160601 return; 1665d6c23f6fSyx160601 1666d6c23f6fSyx160601 /* 1667d6c23f6fSyx160601 * Remove the NAT entry from the old location 1668d6c23f6fSyx160601 */ 1669d6c23f6fSyx160601 if (nat->nat_hnext[0]) 1670d6c23f6fSyx160601 nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 1671d6c23f6fSyx160601 *nat->nat_phnext[0] = nat->nat_hnext[0]; 1672d6c23f6fSyx160601 ifs->ifs_nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--; 1673d6c23f6fSyx160601 1674d6c23f6fSyx160601 if (nat->nat_hnext[1]) 1675d6c23f6fSyx160601 nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 1676d6c23f6fSyx160601 *nat->nat_phnext[1] = nat->nat_hnext[1]; 1677d6c23f6fSyx160601 ifs->ifs_nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--; 1678d6c23f6fSyx160601 1679d6c23f6fSyx160601 /* 1680d6c23f6fSyx160601 * Add into the NAT table in the new position 1681d6c23f6fSyx160601 */ 1682d6c23f6fSyx160601 hv = NAT_HASH_FN6(&nat->nat_inip6, nat->nat_inport, 0xffffffff); 1683d6c23f6fSyx160601 hv = NAT_HASH_FN6(&nat->nat_oip6, hv + nat->nat_oport, 1684d6c23f6fSyx160601 ifs->ifs_ipf_nattable_sz); 1685d6c23f6fSyx160601 nat->nat_hv[0] = hv; 1686d6c23f6fSyx160601 natp = &ifs->ifs_nat_table[0][hv]; 1687d6c23f6fSyx160601 if (*natp) 1688d6c23f6fSyx160601 (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 1689d6c23f6fSyx160601 nat->nat_phnext[0] = natp; 1690d6c23f6fSyx160601 nat->nat_hnext[0] = *natp; 1691d6c23f6fSyx160601 *natp = nat; 1692d6c23f6fSyx160601 ifs->ifs_nat_stats.ns_bucketlen[0][hv]++; 1693d6c23f6fSyx160601 1694d6c23f6fSyx160601 hv = NAT_HASH_FN6(&nat->nat_outip6, nat->nat_outport, 0xffffffff); 1695d6c23f6fSyx160601 hv = NAT_HASH_FN6(&nat->nat_oip6, hv + nat->nat_oport, 1696d6c23f6fSyx160601 ifs->ifs_ipf_nattable_sz); 1697d6c23f6fSyx160601 nat->nat_hv[1] = hv; 1698d6c23f6fSyx160601 natp = &ifs->ifs_nat_table[1][hv]; 1699d6c23f6fSyx160601 if (*natp) 1700d6c23f6fSyx160601 (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 1701d6c23f6fSyx160601 nat->nat_phnext[1] = natp; 1702d6c23f6fSyx160601 nat->nat_hnext[1] = *natp; 1703d6c23f6fSyx160601 *natp = nat; 1704d6c23f6fSyx160601 ifs->ifs_nat_stats.ns_bucketlen[1][hv]++; 1705d6c23f6fSyx160601 } 1706d6c23f6fSyx160601 1707d6c23f6fSyx160601 1708d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 1709d6c23f6fSyx160601 /* Function: nat6_outlookup */ 1710d6c23f6fSyx160601 /* Returns: nat_t* - NULL == no match, */ 1711d6c23f6fSyx160601 /* else pointer to matching NAT entry */ 1712d6c23f6fSyx160601 /* Parameters: fin(I) - pointer to packet information */ 1713d6c23f6fSyx160601 /* flags(I) - NAT flags for this packet */ 1714d6c23f6fSyx160601 /* p(I) - protocol for this packet */ 1715d6c23f6fSyx160601 /* src(I) - source IP address */ 1716d6c23f6fSyx160601 /* dst(I) - destination IP address */ 1717d6c23f6fSyx160601 /* rw(I) - 1 == write lock on ipf_nat held, 0 == read lock. */ 1718d6c23f6fSyx160601 /* */ 1719d6c23f6fSyx160601 /* Lookup a nat entry based on the source 'real' ip address/port and */ 1720d6c23f6fSyx160601 /* destination address/port. We use this lookup when sending a packet out, */ 1721d6c23f6fSyx160601 /* we're looking for a table entry, based on the source address. */ 1722d6c23f6fSyx160601 /* */ 1723d6c23f6fSyx160601 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 1724d6c23f6fSyx160601 /* */ 1725d6c23f6fSyx160601 /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */ 1726d6c23f6fSyx160601 /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 1727d6c23f6fSyx160601 /* */ 1728d6c23f6fSyx160601 /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 1729d6c23f6fSyx160601 /* the packet is of said protocol */ 1730d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 1731d6c23f6fSyx160601 nat_t *nat6_outlookup(fin, flags, p, src, dst) 1732d6c23f6fSyx160601 fr_info_t *fin; 1733d6c23f6fSyx160601 u_int flags, p; 1734d6c23f6fSyx160601 struct in6_addr *src , *dst; 1735d6c23f6fSyx160601 { 1736d6c23f6fSyx160601 u_short sport, dport; 1737d6c23f6fSyx160601 u_int sflags; 1738d6c23f6fSyx160601 nat_t *nat; 1739d6c23f6fSyx160601 int nflags; 1740d6c23f6fSyx160601 void *ifp; 1741d6c23f6fSyx160601 u_int hv; 1742d6c23f6fSyx160601 ipf_stack_t *ifs = fin->fin_ifs; 1743d6c23f6fSyx160601 1744d6c23f6fSyx160601 ifp = fin->fin_ifp; 1745d6c23f6fSyx160601 1746d6c23f6fSyx160601 sflags = flags & IPN_TCPUDPICMP; 1747d6c23f6fSyx160601 sport = 0; 1748d6c23f6fSyx160601 dport = 0; 1749d6c23f6fSyx160601 1750d6c23f6fSyx160601 switch (p) 1751d6c23f6fSyx160601 { 1752d6c23f6fSyx160601 case IPPROTO_TCP : 1753d6c23f6fSyx160601 case IPPROTO_UDP : 1754d6c23f6fSyx160601 sport = htons(fin->fin_data[0]); 1755d6c23f6fSyx160601 dport = htons(fin->fin_data[1]); 1756d6c23f6fSyx160601 break; 1757d6c23f6fSyx160601 case IPPROTO_ICMPV6 : 1758d6c23f6fSyx160601 if (flags & IPN_ICMPERR) 1759d6c23f6fSyx160601 sport = fin->fin_data[1]; 1760d6c23f6fSyx160601 else 1761d6c23f6fSyx160601 dport = fin->fin_data[1]; 1762d6c23f6fSyx160601 break; 1763d6c23f6fSyx160601 default : 1764d6c23f6fSyx160601 break; 1765d6c23f6fSyx160601 } 1766d6c23f6fSyx160601 1767d6c23f6fSyx160601 if ((flags & SI_WILDP) != 0) 1768d6c23f6fSyx160601 goto find_out_wild_ports; 1769d6c23f6fSyx160601 1770d6c23f6fSyx160601 hv = NAT_HASH_FN6(src, sport, 0xffffffff); 1771d6c23f6fSyx160601 hv = NAT_HASH_FN6(dst, hv + dport, ifs->ifs_ipf_nattable_sz); 1772d6c23f6fSyx160601 nat = ifs->ifs_nat_table[0][hv]; 1773d6c23f6fSyx160601 for (; nat; nat = nat->nat_hnext[0]) { 1774d6c23f6fSyx160601 if (nat->nat_v != 6) 1775d6c23f6fSyx160601 continue; 1776d6c23f6fSyx160601 1777d6c23f6fSyx160601 if (nat->nat_ifps[1] != NULL) { 1778d6c23f6fSyx160601 if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 1779d6c23f6fSyx160601 continue; 1780d6c23f6fSyx160601 } else if (ifp != NULL) 1781d6c23f6fSyx160601 nat->nat_ifps[1] = ifp; 1782d6c23f6fSyx160601 1783d6c23f6fSyx160601 nflags = nat->nat_flags; 1784d6c23f6fSyx160601 1785d6c23f6fSyx160601 if (IP6_EQ(&nat->nat_inip6, src) && 1786d6c23f6fSyx160601 IP6_EQ(&nat->nat_oip6, dst) && 1787d6c23f6fSyx160601 (((p == 0) && (sflags == (nflags & NAT_TCPUDPICMP))) || 1788d6c23f6fSyx160601 (p == nat->nat_p))) { 1789d6c23f6fSyx160601 switch (p) 1790d6c23f6fSyx160601 { 1791d6c23f6fSyx160601 #if 0 1792d6c23f6fSyx160601 case IPPROTO_GRE : 1793d6c23f6fSyx160601 if (nat->nat_call[1] != fin->fin_data[0]) 1794d6c23f6fSyx160601 continue; 1795d6c23f6fSyx160601 break; 1796d6c23f6fSyx160601 #endif 1797d6c23f6fSyx160601 case IPPROTO_TCP : 1798d6c23f6fSyx160601 case IPPROTO_UDP : 1799d6c23f6fSyx160601 if (nat->nat_oport != dport) 1800d6c23f6fSyx160601 continue; 1801d6c23f6fSyx160601 if (nat->nat_inport != sport) 1802d6c23f6fSyx160601 continue; 1803d6c23f6fSyx160601 break; 1804d6c23f6fSyx160601 default : 1805d6c23f6fSyx160601 break; 1806d6c23f6fSyx160601 } 1807d6c23f6fSyx160601 1808d6c23f6fSyx160601 #ifdef IPF_V6_PROXIES 1809d6c23f6fSyx160601 ipn = nat->nat_ptr; 1810d6c23f6fSyx160601 if ((ipn != NULL) && (nat->nat_aps != NULL)) 1811d6c23f6fSyx160601 if (appr_match(fin, nat) != 0) 1812d6c23f6fSyx160601 continue; 1813d6c23f6fSyx160601 #endif 1814d6c23f6fSyx160601 return nat; 1815d6c23f6fSyx160601 } 1816d6c23f6fSyx160601 } 1817d6c23f6fSyx160601 1818d6c23f6fSyx160601 /* 1819d6c23f6fSyx160601 * So if we didn't find it but there are wildcard members in the hash 1820d6c23f6fSyx160601 * table, go back and look for them. We do this search and update here 1821d6c23f6fSyx160601 * because it is modifying the NAT table and we want to do this only 1822d6c23f6fSyx160601 * for the first packet that matches. The exception, of course, is 1823d6c23f6fSyx160601 * for "dummy" (FI_IGNORE) lookups. 1824d6c23f6fSyx160601 */ 1825d6c23f6fSyx160601 find_out_wild_ports: 1826d6c23f6fSyx160601 if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) 1827d6c23f6fSyx160601 return NULL; 1828d6c23f6fSyx160601 if (ifs->ifs_nat_stats.ns_wilds == 0) 1829d6c23f6fSyx160601 return NULL; 1830d6c23f6fSyx160601 1831d6c23f6fSyx160601 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 1832d6c23f6fSyx160601 1833d6c23f6fSyx160601 hv = NAT_HASH_FN6(src, 0, 0xffffffff); 1834d6c23f6fSyx160601 hv = NAT_HASH_FN6(dst, hv, ifs->ifs_ipf_nattable_sz); 1835d6c23f6fSyx160601 1836d6c23f6fSyx160601 WRITE_ENTER(&ifs->ifs_ipf_nat); 1837d6c23f6fSyx160601 1838d6c23f6fSyx160601 nat = ifs->ifs_nat_table[0][hv]; 1839d6c23f6fSyx160601 for (; nat; nat = nat->nat_hnext[0]) { 1840d6c23f6fSyx160601 if (nat->nat_v != 6) 1841d6c23f6fSyx160601 continue; 1842d6c23f6fSyx160601 1843d6c23f6fSyx160601 if (nat->nat_ifps[1] != NULL) { 1844d6c23f6fSyx160601 if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 1845d6c23f6fSyx160601 continue; 1846d6c23f6fSyx160601 } else if (ifp != NULL) 1847d6c23f6fSyx160601 nat->nat_ifps[1] = ifp; 1848d6c23f6fSyx160601 1849d6c23f6fSyx160601 if (nat->nat_p != fin->fin_p) 1850d6c23f6fSyx160601 continue; 1851d6c23f6fSyx160601 if (IP6_NEQ(&nat->nat_inip6, src) || 1852d6c23f6fSyx160601 IP6_NEQ(&nat->nat_oip6, dst)) 1853d6c23f6fSyx160601 continue; 1854d6c23f6fSyx160601 1855d6c23f6fSyx160601 nflags = nat->nat_flags; 1856d6c23f6fSyx160601 if (!(nflags & (NAT_TCPUDP|SI_WILDP))) 1857d6c23f6fSyx160601 continue; 1858d6c23f6fSyx160601 1859d6c23f6fSyx160601 if (nat_wildok(nat, (int)sport, (int)dport, nflags, 1860d6c23f6fSyx160601 NAT_OUTBOUND) == 1) { 1861d6c23f6fSyx160601 if ((fin->fin_flx & FI_IGNORE) != 0) 1862d6c23f6fSyx160601 break; 1863d6c23f6fSyx160601 if ((nflags & SI_CLONE) != 0) { 1864d6c23f6fSyx160601 nat = fr_natclone(fin, nat); 1865d6c23f6fSyx160601 if (nat == NULL) 1866d6c23f6fSyx160601 break; 1867d6c23f6fSyx160601 } else { 1868d6c23f6fSyx160601 MUTEX_ENTER(&ifs->ifs_ipf_nat_new); 1869d6c23f6fSyx160601 ifs->ifs_nat_stats.ns_wilds--; 1870d6c23f6fSyx160601 MUTEX_EXIT(&ifs->ifs_ipf_nat_new); 1871d6c23f6fSyx160601 } 1872d6c23f6fSyx160601 nat->nat_inport = sport; 1873d6c23f6fSyx160601 nat->nat_oport = dport; 1874d6c23f6fSyx160601 if (nat->nat_outport == 0) 1875d6c23f6fSyx160601 nat->nat_outport = sport; 1876d6c23f6fSyx160601 nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 1877d6c23f6fSyx160601 nat6_tabmove(nat, ifs); 1878d6c23f6fSyx160601 break; 1879d6c23f6fSyx160601 } 1880d6c23f6fSyx160601 } 1881d6c23f6fSyx160601 1882d6c23f6fSyx160601 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); 1883d6c23f6fSyx160601 1884d6c23f6fSyx160601 return nat; 1885d6c23f6fSyx160601 } 1886d6c23f6fSyx160601 1887d6c23f6fSyx160601 1888d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 1889d6c23f6fSyx160601 /* Function: nat6_lookupredir */ 1890d6c23f6fSyx160601 /* Returns: nat_t* - NULL == no match, */ 1891d6c23f6fSyx160601 /* else pointer to matching NAT entry */ 1892d6c23f6fSyx160601 /* Parameters: np(I) - pointer to description of packet to find NAT table */ 1893d6c23f6fSyx160601 /* entry for. */ 1894d6c23f6fSyx160601 /* */ 1895d6c23f6fSyx160601 /* Lookup the NAT tables to search for a matching redirect */ 1896d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 1897d6c23f6fSyx160601 nat_t *nat6_lookupredir(np, ifs) 1898d6c23f6fSyx160601 natlookup_t *np; 1899d6c23f6fSyx160601 ipf_stack_t *ifs; 1900d6c23f6fSyx160601 { 1901d6c23f6fSyx160601 fr_info_t fi; 1902d6c23f6fSyx160601 nat_t *nat; 1903d6c23f6fSyx160601 1904d6c23f6fSyx160601 bzero((char *)&fi, sizeof (fi)); 1905d6c23f6fSyx160601 if (np->nl_flags & IPN_IN) { 1906d6c23f6fSyx160601 fi.fin_data[0] = ntohs(np->nl_realport); 1907d6c23f6fSyx160601 fi.fin_data[1] = ntohs(np->nl_outport); 1908d6c23f6fSyx160601 } else { 1909d6c23f6fSyx160601 fi.fin_data[0] = ntohs(np->nl_inport); 1910d6c23f6fSyx160601 fi.fin_data[1] = ntohs(np->nl_outport); 1911d6c23f6fSyx160601 } 1912d6c23f6fSyx160601 if (np->nl_flags & IPN_TCP) 1913d6c23f6fSyx160601 fi.fin_p = IPPROTO_TCP; 1914d6c23f6fSyx160601 else if (np->nl_flags & IPN_UDP) 1915d6c23f6fSyx160601 fi.fin_p = IPPROTO_UDP; 1916d6c23f6fSyx160601 else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) 1917d6c23f6fSyx160601 fi.fin_p = IPPROTO_ICMPV6; 1918d6c23f6fSyx160601 1919d6c23f6fSyx160601 fi.fin_ifs = ifs; 1920d6c23f6fSyx160601 /* 1921d6c23f6fSyx160601 * We can do two sorts of lookups: 1922d6c23f6fSyx160601 * - IPN_IN: we have the `real' and `out' address, look for `in'. 1923d6c23f6fSyx160601 * - default: we have the `in' and `out' address, look for `real'. 1924d6c23f6fSyx160601 */ 1925d6c23f6fSyx160601 if (np->nl_flags & IPN_IN) { 1926d6c23f6fSyx160601 if ((nat = nat6_inlookup(&fi, np->nl_flags, fi.fin_p, 1927d6c23f6fSyx160601 &np->nl_realip6, &np->nl_outip6))) { 1928d6c23f6fSyx160601 np->nl_inipaddr = nat->nat_inip6; 1929d6c23f6fSyx160601 np->nl_inport = nat->nat_inport; 1930d6c23f6fSyx160601 } 1931d6c23f6fSyx160601 } else { 1932d6c23f6fSyx160601 /* 1933d6c23f6fSyx160601 * If nl_inip is non null, this is a lookup based on the real 1934d6c23f6fSyx160601 * ip address. Else, we use the fake. 1935d6c23f6fSyx160601 */ 1936d6c23f6fSyx160601 if ((nat = nat6_outlookup(&fi, np->nl_flags, fi.fin_p, 1937d6c23f6fSyx160601 &np->nl_inip6, &np->nl_outip6))) { 1938d6c23f6fSyx160601 if ((np->nl_flags & IPN_FINDFORWARD) != 0) { 1939d6c23f6fSyx160601 fr_info_t fin; 1940d6c23f6fSyx160601 bzero((char *)&fin, sizeof (fin)); 1941d6c23f6fSyx160601 fin.fin_p = nat->nat_p; 1942d6c23f6fSyx160601 fin.fin_data[0] = ntohs(nat->nat_outport); 1943d6c23f6fSyx160601 fin.fin_data[1] = ntohs(nat->nat_oport); 1944d6c23f6fSyx160601 fin.fin_ifs = ifs; 1945d6c23f6fSyx160601 if (nat6_inlookup(&fin, np->nl_flags, fin.fin_p, 1946d6c23f6fSyx160601 &nat->nat_outip6.in6, 1947d6c23f6fSyx160601 &nat->nat_oip6.in6) != NULL) { 1948d6c23f6fSyx160601 np->nl_flags &= ~IPN_FINDFORWARD; 1949d6c23f6fSyx160601 } 1950d6c23f6fSyx160601 } 1951d6c23f6fSyx160601 1952d6c23f6fSyx160601 np->nl_realip6 = nat->nat_outip6.in6; 1953d6c23f6fSyx160601 np->nl_realport = nat->nat_outport; 1954d6c23f6fSyx160601 } 1955d6c23f6fSyx160601 } 1956d6c23f6fSyx160601 1957d6c23f6fSyx160601 return nat; 1958d6c23f6fSyx160601 } 1959d6c23f6fSyx160601 1960d6c23f6fSyx160601 1961d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 1962d6c23f6fSyx160601 /* Function: nat6_match */ 1963d6c23f6fSyx160601 /* Returns: int - 0 == no match, 1 == match */ 1964d6c23f6fSyx160601 /* Parameters: fin(I) - pointer to packet information */ 1965d6c23f6fSyx160601 /* np(I) - pointer to NAT rule */ 1966d6c23f6fSyx160601 /* */ 1967d6c23f6fSyx160601 /* Pull the matching of a packet against a NAT rule out of that complex */ 1968d6c23f6fSyx160601 /* loop inside fr_checknat6in() and lay it out properly in its own function.*/ 1969d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 1970d6c23f6fSyx160601 static int nat6_match(fin, np) 1971d6c23f6fSyx160601 fr_info_t *fin; 1972d6c23f6fSyx160601 ipnat_t *np; 1973d6c23f6fSyx160601 { 1974d6c23f6fSyx160601 frtuc_t *ft; 1975d6c23f6fSyx160601 1976d6c23f6fSyx160601 if (fin->fin_v != 6) 1977d6c23f6fSyx160601 return 0; 1978d6c23f6fSyx160601 1979d6c23f6fSyx160601 if (np->in_p && fin->fin_p != np->in_p) 1980d6c23f6fSyx160601 return 0; 1981d6c23f6fSyx160601 1982d6c23f6fSyx160601 if (fin->fin_out) { 1983d6c23f6fSyx160601 if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK))) 1984d6c23f6fSyx160601 return 0; 1985d6c23f6fSyx160601 if (IP6_MASKNEQ(&fin->fin_src6, &np->in_in[1], &np->in_in[0]) 1986d6c23f6fSyx160601 ^ ((np->in_flags & IPN_NOTSRC) != 0)) 1987d6c23f6fSyx160601 return 0; 1988d6c23f6fSyx160601 if (IP6_MASKNEQ(&fin->fin_dst6, &np->in_src[1], &np->in_src[0]) 1989d6c23f6fSyx160601 ^ ((np->in_flags & IPN_NOTDST) != 0)) 1990d6c23f6fSyx160601 return 0; 1991d6c23f6fSyx160601 } else { 1992d6c23f6fSyx160601 if (!(np->in_redir & NAT_REDIRECT)) 1993d6c23f6fSyx160601 return 0; 1994d6c23f6fSyx160601 if (IP6_MASKNEQ(&fin->fin_src6, &np->in_src[1], &np->in_src[0]) 1995d6c23f6fSyx160601 ^ ((np->in_flags & IPN_NOTSRC) != 0)) 1996d6c23f6fSyx160601 return 0; 1997d6c23f6fSyx160601 if (IP6_MASKNEQ(&fin->fin_dst6, &np->in_out[1], &np->in_out[0]) 1998d6c23f6fSyx160601 ^ ((np->in_flags & IPN_NOTDST) != 0)) 1999d6c23f6fSyx160601 return 0; 2000d6c23f6fSyx160601 } 2001d6c23f6fSyx160601 2002d6c23f6fSyx160601 ft = &np->in_tuc; 2003d6c23f6fSyx160601 if (!(fin->fin_flx & FI_TCPUDP) || 2004d6c23f6fSyx160601 (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { 2005d6c23f6fSyx160601 if (ft->ftu_scmp || ft->ftu_dcmp) 2006d6c23f6fSyx160601 return 0; 2007d6c23f6fSyx160601 return 1; 2008d6c23f6fSyx160601 } 2009d6c23f6fSyx160601 2010d6c23f6fSyx160601 return fr_tcpudpchk(fin, ft); 2011d6c23f6fSyx160601 } 2012d6c23f6fSyx160601 2013d6c23f6fSyx160601 2014d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 2015d6c23f6fSyx160601 /* Function: fr_checknat6out */ 2016d6c23f6fSyx160601 /* Returns: int - -1 == packet failed NAT checks so block it, */ 2017d6c23f6fSyx160601 /* 0 == no packet translation occurred, */ 2018d6c23f6fSyx160601 /* 1 == packet was successfully translated. */ 2019d6c23f6fSyx160601 /* Parameters: fin(I) - pointer to packet information */ 2020d6c23f6fSyx160601 /* passp(I) - pointer to filtering result flags */ 2021d6c23f6fSyx160601 /* */ 2022d6c23f6fSyx160601 /* Check to see if an outcoming packet should be changed. ICMP packets are */ 2023d6c23f6fSyx160601 /* first checked to see if they match an existing entry (if an error), */ 2024d6c23f6fSyx160601 /* otherwise a search of the current NAT table is made. If neither results */ 2025d6c23f6fSyx160601 /* in a match then a search for a matching NAT rule is made. Create a new */ 2026d6c23f6fSyx160601 /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 2027d6c23f6fSyx160601 /* packet header(s) as required. */ 2028d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 2029d6c23f6fSyx160601 int fr_checknat6out(fin, passp) 2030d6c23f6fSyx160601 fr_info_t *fin; 2031d6c23f6fSyx160601 u_32_t *passp; 2032d6c23f6fSyx160601 { 2033d6c23f6fSyx160601 struct ifnet *ifp, *sifp; 2034d6c23f6fSyx160601 int rval, natfailed; 2035d6c23f6fSyx160601 ipnat_t *np = NULL; 2036d6c23f6fSyx160601 u_int nflags = 0; 2037d6c23f6fSyx160601 i6addr_t ipa, iph; 2038d6c23f6fSyx160601 int natadd = 1; 2039d6c23f6fSyx160601 frentry_t *fr; 2040d6c23f6fSyx160601 nat_t *nat; 2041d6c23f6fSyx160601 ipf_stack_t *ifs = fin->fin_ifs; 2042d6c23f6fSyx160601 2043d6c23f6fSyx160601 if (ifs->ifs_nat_stats.ns_rules == 0 || ifs->ifs_fr_nat_lock != 0) 2044d6c23f6fSyx160601 return 0; 2045d6c23f6fSyx160601 2046d6c23f6fSyx160601 natfailed = 0; 2047d6c23f6fSyx160601 fr = fin->fin_fr; 2048d6c23f6fSyx160601 sifp = fin->fin_ifp; 2049d6c23f6fSyx160601 if ((fr != NULL) && !(fr->fr_flags & FR_DUP) && 2050d6c23f6fSyx160601 fr->fr_tifs[fin->fin_rev].fd_ifp && 2051d6c23f6fSyx160601 fr->fr_tifs[fin->fin_rev].fd_ifp != (void *)-1) 2052d6c23f6fSyx160601 fin->fin_ifp = fr->fr_tifs[fin->fin_rev].fd_ifp; 2053d6c23f6fSyx160601 ifp = fin->fin_ifp; 2054d6c23f6fSyx160601 2055d6c23f6fSyx160601 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 2056d6c23f6fSyx160601 switch (fin->fin_p) 2057d6c23f6fSyx160601 { 2058d6c23f6fSyx160601 case IPPROTO_TCP : 2059d6c23f6fSyx160601 nflags = IPN_TCP; 2060d6c23f6fSyx160601 break; 2061d6c23f6fSyx160601 case IPPROTO_UDP : 2062d6c23f6fSyx160601 nflags = IPN_UDP; 2063d6c23f6fSyx160601 break; 2064d6c23f6fSyx160601 case IPPROTO_ICMPV6 : 2065d6c23f6fSyx160601 /* 2066d6c23f6fSyx160601 * This is an incoming packet, so the destination is 2067d6c23f6fSyx160601 * the icmp6_id and the source port equals 0 2068d6c23f6fSyx160601 */ 2069d6c23f6fSyx160601 if ((fin->fin_flx & FI_ICMPQUERY) != 0) 2070d6c23f6fSyx160601 nflags = IPN_ICMPQUERY; 2071d6c23f6fSyx160601 break; 2072d6c23f6fSyx160601 default : 2073d6c23f6fSyx160601 break; 2074d6c23f6fSyx160601 } 2075d6c23f6fSyx160601 2076d6c23f6fSyx160601 #ifdef IPF_V6_PROXIES 2077d6c23f6fSyx160601 if ((nflags & IPN_TCPUDP)) 2078d6c23f6fSyx160601 tcp = fin->fin_dp; 2079d6c23f6fSyx160601 #endif 2080d6c23f6fSyx160601 } 2081d6c23f6fSyx160601 2082d6c23f6fSyx160601 ipa = fin->fin_src6; 2083d6c23f6fSyx160601 2084d6c23f6fSyx160601 READ_ENTER(&ifs->ifs_ipf_nat); 2085d6c23f6fSyx160601 2086d6c23f6fSyx160601 if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) && 2087d6c23f6fSyx160601 (nat = nat6_icmperror(fin, &nflags, NAT_OUTBOUND))) 2088d6c23f6fSyx160601 /*EMPTY*/; 2089d6c23f6fSyx160601 else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin))) 2090d6c23f6fSyx160601 natadd = 0; 2091d6c23f6fSyx160601 else if ((nat = nat6_outlookup(fin, nflags|NAT_SEARCH, 2092d6c23f6fSyx160601 (u_int)fin->fin_p, &fin->fin_src6.in6, 2093d6c23f6fSyx160601 &fin->fin_dst6.in6))) { 2094d6c23f6fSyx160601 nflags = nat->nat_flags; 2095d6c23f6fSyx160601 } else { 2096d6c23f6fSyx160601 u_32_t hv, nmsk; 2097d6c23f6fSyx160601 i6addr_t msk; 2098d6c23f6fSyx160601 int i; 2099d6c23f6fSyx160601 2100d6c23f6fSyx160601 /* 2101d6c23f6fSyx160601 * If there is no current entry in the nat table for this IP#, 2102d6c23f6fSyx160601 * create one for it (if there is a matching rule). 2103d6c23f6fSyx160601 */ 2104d6c23f6fSyx160601 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 2105d6c23f6fSyx160601 i = 3; 2106d6c23f6fSyx160601 msk.i6[0] = 0xffffffff; 2107d6c23f6fSyx160601 msk.i6[1] = 0xffffffff; 2108d6c23f6fSyx160601 msk.i6[2] = 0xffffffff; 2109d6c23f6fSyx160601 msk.i6[3] = 0xffffffff; 2110d6c23f6fSyx160601 nmsk = ifs->ifs_nat6_masks[3]; 2111d6c23f6fSyx160601 WRITE_ENTER(&ifs->ifs_ipf_nat); 2112d6c23f6fSyx160601 maskloop: 2113d6c23f6fSyx160601 IP6_AND(&ipa, &msk, &iph); 2114d6c23f6fSyx160601 hv = NAT_HASH_FN6(&iph, 0, ifs->ifs_ipf_natrules_sz); 2115d6c23f6fSyx160601 for (np = ifs->ifs_nat_rules[hv]; np; np = np->in_mnext) 2116d6c23f6fSyx160601 { 2117d6c23f6fSyx160601 if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) 2118d6c23f6fSyx160601 continue; 2119d6c23f6fSyx160601 if (np->in_v != 6) 2120d6c23f6fSyx160601 continue; 2121d6c23f6fSyx160601 if (np->in_p && (np->in_p != fin->fin_p)) 2122d6c23f6fSyx160601 continue; 2123d6c23f6fSyx160601 if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) 2124d6c23f6fSyx160601 continue; 2125d6c23f6fSyx160601 if (np->in_flags & IPN_FILTER) { 2126d6c23f6fSyx160601 if (!nat6_match(fin, np)) 2127d6c23f6fSyx160601 continue; 2128d6c23f6fSyx160601 } else if (!IP6_MASKEQ(&ipa, &np->in_in[1], 2129d6c23f6fSyx160601 &np->in_in[0])) 2130d6c23f6fSyx160601 continue; 2131d6c23f6fSyx160601 2132d6c23f6fSyx160601 if ((fr != NULL) && 2133d6c23f6fSyx160601 !fr_matchtag(&np->in_tag, &fr->fr_nattag)) 2134d6c23f6fSyx160601 continue; 2135d6c23f6fSyx160601 2136d6c23f6fSyx160601 #ifdef IPF_V6_PROXIES 2137d6c23f6fSyx160601 if (*np->in_plabel != '\0') { 2138d6c23f6fSyx160601 if (((np->in_flags & IPN_FILTER) == 0) && 2139d6c23f6fSyx160601 (np->in_dport != tcp->th_dport)) 2140d6c23f6fSyx160601 continue; 2141d6c23f6fSyx160601 if (appr_ok(fin, tcp, np) == 0) 2142d6c23f6fSyx160601 continue; 2143d6c23f6fSyx160601 } 2144d6c23f6fSyx160601 #endif 2145d6c23f6fSyx160601 2146d6c23f6fSyx160601 if (nat = nat6_new(fin, np, NULL, nflags, 2147d6c23f6fSyx160601 NAT_OUTBOUND)) { 2148d6c23f6fSyx160601 np->in_hits++; 2149d6c23f6fSyx160601 break; 2150d6c23f6fSyx160601 } else 2151d6c23f6fSyx160601 natfailed = -1; 2152d6c23f6fSyx160601 } 2153d6c23f6fSyx160601 if ((np == NULL) && (i >= 0)) { 2154d6c23f6fSyx160601 while (i >= 0) { 2155d6c23f6fSyx160601 while (nmsk) { 2156d6c23f6fSyx160601 msk.i6[i] = htonl(ntohl(msk.i6[i])<<1); 2157d6c23f6fSyx160601 if ((nmsk & 0x80000000) != 0) { 2158d6c23f6fSyx160601 nmsk <<= 1; 2159d6c23f6fSyx160601 goto maskloop; 2160d6c23f6fSyx160601 } 2161d6c23f6fSyx160601 nmsk <<= 1; 2162d6c23f6fSyx160601 } 2163d6c23f6fSyx160601 msk.i6[i--] = 0; 2164d6c23f6fSyx160601 if (i >= 0) { 2165d6c23f6fSyx160601 nmsk = ifs->ifs_nat6_masks[i]; 2166d6c23f6fSyx160601 if (nmsk != 0) 2167d6c23f6fSyx160601 goto maskloop; 2168d6c23f6fSyx160601 } 2169d6c23f6fSyx160601 } 2170d6c23f6fSyx160601 } 2171d6c23f6fSyx160601 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); 2172d6c23f6fSyx160601 } 2173d6c23f6fSyx160601 2174d6c23f6fSyx160601 if (nat != NULL) { 2175d6c23f6fSyx160601 rval = fr_nat6out(fin, nat, natadd, nflags); 217633f2fefdSDarren Reed } else { 2177d6c23f6fSyx160601 rval = natfailed; 217833f2fefdSDarren Reed } 2179d6c23f6fSyx160601 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 2180d6c23f6fSyx160601 2181d6c23f6fSyx160601 if (rval == -1) { 2182d6c23f6fSyx160601 if (passp != NULL) 2183d6c23f6fSyx160601 *passp = FR_BLOCK; 2184d6c23f6fSyx160601 fin->fin_flx |= FI_BADNAT; 2185d6c23f6fSyx160601 } 2186d6c23f6fSyx160601 fin->fin_ifp = sifp; 2187d6c23f6fSyx160601 return rval; 2188d6c23f6fSyx160601 } 2189d6c23f6fSyx160601 2190d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 2191d6c23f6fSyx160601 /* Function: fr_nat6out */ 2192d6c23f6fSyx160601 /* Returns: int - -1 == packet failed NAT checks so block it, */ 2193d6c23f6fSyx160601 /* 1 == packet was successfully translated. */ 2194d6c23f6fSyx160601 /* Parameters: fin(I) - pointer to packet information */ 2195d6c23f6fSyx160601 /* nat(I) - pointer to NAT structure */ 2196d6c23f6fSyx160601 /* natadd(I) - flag indicating if it is safe to add frag cache */ 2197d6c23f6fSyx160601 /* nflags(I) - NAT flags set for this packet */ 2198d6c23f6fSyx160601 /* */ 2199d6c23f6fSyx160601 /* Translate a packet coming "out" on an interface. */ 2200d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 2201d6c23f6fSyx160601 int fr_nat6out(fin, nat, natadd, nflags) 2202d6c23f6fSyx160601 fr_info_t *fin; 2203d6c23f6fSyx160601 nat_t *nat; 2204d6c23f6fSyx160601 int natadd; 2205d6c23f6fSyx160601 u_32_t nflags; 2206d6c23f6fSyx160601 { 2207d6c23f6fSyx160601 struct icmp6_hdr *icmp6; 2208d6c23f6fSyx160601 u_short *csump; 2209d6c23f6fSyx160601 tcphdr_t *tcp; 2210d6c23f6fSyx160601 ipnat_t *np; 2211d6c23f6fSyx160601 int i; 2212d6c23f6fSyx160601 ipf_stack_t *ifs = fin->fin_ifs; 2213d6c23f6fSyx160601 2214*f56257d8SToomas Soome #if defined(SOLARIS) && defined(_KERNEL) 22157ddc9b1aSDarren Reed net_handle_t net_data_p = ifs->ifs_ipf_ipv6; 2216d6c23f6fSyx160601 #endif 2217d6c23f6fSyx160601 2218d6c23f6fSyx160601 tcp = NULL; 2219d6c23f6fSyx160601 icmp6 = NULL; 2220d6c23f6fSyx160601 csump = NULL; 2221d6c23f6fSyx160601 np = nat->nat_ptr; 2222d6c23f6fSyx160601 2223d6c23f6fSyx160601 if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) 2224d6c23f6fSyx160601 (void) fr_nat_newfrag(fin, 0, nat); 2225d6c23f6fSyx160601 2226d6c23f6fSyx160601 MUTEX_ENTER(&nat->nat_lock); 2227d6c23f6fSyx160601 nat->nat_bytes[1] += fin->fin_plen; 2228d6c23f6fSyx160601 nat->nat_pkts[1]++; 2229d6c23f6fSyx160601 MUTEX_EXIT(&nat->nat_lock); 2230d6c23f6fSyx160601 2231d6c23f6fSyx160601 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 2232d6c23f6fSyx160601 if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) { 2233d6c23f6fSyx160601 tcp = fin->fin_dp; 2234d6c23f6fSyx160601 2235d6c23f6fSyx160601 tcp->th_sport = nat->nat_outport; 2236d6c23f6fSyx160601 fin->fin_data[0] = ntohs(nat->nat_outport); 2237d6c23f6fSyx160601 } 2238d6c23f6fSyx160601 2239d6c23f6fSyx160601 if ((nat->nat_outport != 0) && (nflags & IPN_ICMPQUERY)) { 2240d6c23f6fSyx160601 icmp6 = fin->fin_dp; 2241d6c23f6fSyx160601 icmp6->icmp6_id = nat->nat_outport; 2242d6c23f6fSyx160601 } 2243d6c23f6fSyx160601 2244d6c23f6fSyx160601 csump = nat_proto(fin, nat, nflags); 2245d6c23f6fSyx160601 } 2246d6c23f6fSyx160601 2247d6c23f6fSyx160601 fin->fin_ip6->ip6_src = nat->nat_outip6.in6; 2248d6c23f6fSyx160601 fin->fin_src6 = nat->nat_outip6; 2249d6c23f6fSyx160601 2250d6c23f6fSyx160601 nat_update(fin, nat, np); 2251d6c23f6fSyx160601 2252d6c23f6fSyx160601 /* 2253d6c23f6fSyx160601 * TCP/UDP/ICMPv6 checksum needs to be adjusted. 2254d6c23f6fSyx160601 */ 2255d6c23f6fSyx160601 if (csump != NULL && (!(nflags & IPN_TCPUDP) || 2256d6c23f6fSyx160601 !NET_IS_HCK_L4_FULL(net_data_p, fin->fin_m))) { 2257d6c23f6fSyx160601 if (nflags & IPN_TCPUDP && 2258d6c23f6fSyx160601 NET_IS_HCK_L4_PART(net_data_p, fin->fin_m)) { 2259d6c23f6fSyx160601 if (nat->nat_dir == NAT_OUTBOUND) 2260d6c23f6fSyx160601 fix_outcksum(csump, nat->nat_sumd[1]); 2261d6c23f6fSyx160601 else 2262d6c23f6fSyx160601 fix_incksum(csump, nat->nat_sumd[1]); 2263d6c23f6fSyx160601 } else { 2264d6c23f6fSyx160601 if (nat->nat_dir == NAT_OUTBOUND) 2265d6c23f6fSyx160601 fix_outcksum(csump, nat->nat_sumd[0]); 2266d6c23f6fSyx160601 else 2267d6c23f6fSyx160601 fix_incksum(csump, nat->nat_sumd[0]); 2268d6c23f6fSyx160601 } 2269d6c23f6fSyx160601 } 2270d6c23f6fSyx160601 #ifdef IPFILTER_SYNC 2271d6c23f6fSyx160601 ipfsync_update(SMC_NAT, fin, nat->nat_sync); 2272d6c23f6fSyx160601 #endif 2273d6c23f6fSyx160601 /* ------------------------------------------------------------- */ 2274d6c23f6fSyx160601 /* A few quick notes: */ 2275d6c23f6fSyx160601 /* Following are test conditions prior to calling the */ 2276d6c23f6fSyx160601 /* appr_check routine. */ 2277d6c23f6fSyx160601 /* */ 2278d6c23f6fSyx160601 /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 2279d6c23f6fSyx160601 /* with a redirect rule, we attempt to match the packet's */ 2280d6c23f6fSyx160601 /* source port against in_dport, otherwise we'd compare the */ 2281d6c23f6fSyx160601 /* packet's destination. */ 2282d6c23f6fSyx160601 /* ------------------------------------------------------------- */ 2283d6c23f6fSyx160601 if ((np != NULL) && (np->in_apr != NULL)) { 2284d6c23f6fSyx160601 i = appr_check(fin, nat); 2285d6c23f6fSyx160601 if (i == 0) 2286d6c23f6fSyx160601 i = 1; 2287d6c23f6fSyx160601 } else 2288d6c23f6fSyx160601 i = 1; 2289d6c23f6fSyx160601 ATOMIC_INCL(ifs->ifs_nat_stats.ns_mapped[1]); 2290d6c23f6fSyx160601 fin->fin_flx |= FI_NATED; 2291d6c23f6fSyx160601 return i; 2292d6c23f6fSyx160601 } 2293d6c23f6fSyx160601 2294d6c23f6fSyx160601 2295d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 2296d6c23f6fSyx160601 /* Function: fr_checknat6in */ 2297d6c23f6fSyx160601 /* Returns: int - -1 == packet failed NAT checks so block it, */ 2298d6c23f6fSyx160601 /* 0 == no packet translation occurred, */ 2299d6c23f6fSyx160601 /* 1 == packet was successfully translated. */ 2300d6c23f6fSyx160601 /* Parameters: fin(I) - pointer to packet information */ 2301d6c23f6fSyx160601 /* passp(I) - pointer to filtering result flags */ 2302d6c23f6fSyx160601 /* */ 2303d6c23f6fSyx160601 /* Check to see if an incoming packet should be changed. ICMP packets are */ 2304d6c23f6fSyx160601 /* first checked to see if they match an existing entry (if an error), */ 2305d6c23f6fSyx160601 /* otherwise a search of the current NAT table is made. If neither results */ 2306d6c23f6fSyx160601 /* in a match then a search for a matching NAT rule is made. Create a new */ 2307d6c23f6fSyx160601 /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 2308d6c23f6fSyx160601 /* packet header(s) as required. */ 2309d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 2310d6c23f6fSyx160601 int fr_checknat6in(fin, passp) 2311d6c23f6fSyx160601 fr_info_t *fin; 2312d6c23f6fSyx160601 u_32_t *passp; 2313d6c23f6fSyx160601 { 2314d6c23f6fSyx160601 u_int nflags, natadd; 2315d6c23f6fSyx160601 int rval, natfailed; 2316d6c23f6fSyx160601 struct ifnet *ifp; 2317d6c23f6fSyx160601 struct icmp6_hdr *icmp6; 2318d6c23f6fSyx160601 tcphdr_t *tcp; 2319d6c23f6fSyx160601 u_short dport; 2320d6c23f6fSyx160601 ipnat_t *np; 2321d6c23f6fSyx160601 nat_t *nat; 2322d6c23f6fSyx160601 i6addr_t ipa, iph; 2323d6c23f6fSyx160601 ipf_stack_t *ifs = fin->fin_ifs; 2324d6c23f6fSyx160601 2325d6c23f6fSyx160601 if (ifs->ifs_nat_stats.ns_rules == 0 || ifs->ifs_fr_nat_lock != 0) 2326d6c23f6fSyx160601 return 0; 2327d6c23f6fSyx160601 2328d6c23f6fSyx160601 tcp = NULL; 2329d6c23f6fSyx160601 icmp6 = NULL; 2330d6c23f6fSyx160601 dport = 0; 2331d6c23f6fSyx160601 natadd = 1; 2332d6c23f6fSyx160601 nflags = 0; 2333d6c23f6fSyx160601 natfailed = 0; 2334d6c23f6fSyx160601 ifp = fin->fin_ifp; 2335d6c23f6fSyx160601 2336d6c23f6fSyx160601 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 2337d6c23f6fSyx160601 switch (fin->fin_p) 2338d6c23f6fSyx160601 { 2339d6c23f6fSyx160601 case IPPROTO_TCP : 2340d6c23f6fSyx160601 nflags = IPN_TCP; 2341d6c23f6fSyx160601 break; 2342d6c23f6fSyx160601 case IPPROTO_UDP : 2343d6c23f6fSyx160601 nflags = IPN_UDP; 2344d6c23f6fSyx160601 break; 2345d6c23f6fSyx160601 case IPPROTO_ICMPV6 : 2346d6c23f6fSyx160601 icmp6 = fin->fin_dp; 2347d6c23f6fSyx160601 2348d6c23f6fSyx160601 /* 2349d6c23f6fSyx160601 * This is an incoming packet, so the destination is 2350d6c23f6fSyx160601 * the icmp_id and the source port equals 0 2351d6c23f6fSyx160601 */ 2352d6c23f6fSyx160601 if ((fin->fin_flx & FI_ICMPQUERY) != 0) { 2353d6c23f6fSyx160601 nflags = IPN_ICMPQUERY; 2354d6c23f6fSyx160601 dport = icmp6->icmp6_id; 2355d6c23f6fSyx160601 } break; 2356d6c23f6fSyx160601 default : 2357d6c23f6fSyx160601 break; 2358d6c23f6fSyx160601 } 2359d6c23f6fSyx160601 2360d6c23f6fSyx160601 if ((nflags & IPN_TCPUDP)) { 2361d6c23f6fSyx160601 tcp = fin->fin_dp; 2362d6c23f6fSyx160601 dport = tcp->th_dport; 2363d6c23f6fSyx160601 } 2364d6c23f6fSyx160601 } 2365d6c23f6fSyx160601 2366d6c23f6fSyx160601 ipa = fin->fin_dst6; 2367d6c23f6fSyx160601 2368d6c23f6fSyx160601 READ_ENTER(&ifs->ifs_ipf_nat); 2369d6c23f6fSyx160601 2370d6c23f6fSyx160601 if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) && 2371d6c23f6fSyx160601 (nat = nat6_icmperror(fin, &nflags, NAT_INBOUND))) 2372d6c23f6fSyx160601 /*EMPTY*/; 2373d6c23f6fSyx160601 else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin))) 2374d6c23f6fSyx160601 natadd = 0; 2375d6c23f6fSyx160601 else if ((nat = nat6_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, 2376d6c23f6fSyx160601 &fin->fin_src6.in6, &ipa.in6))) { 2377d6c23f6fSyx160601 nflags = nat->nat_flags; 2378d6c23f6fSyx160601 } else { 2379d6c23f6fSyx160601 u_32_t hv, rmsk; 2380d6c23f6fSyx160601 i6addr_t msk; 2381d6c23f6fSyx160601 int i; 2382d6c23f6fSyx160601 2383d6c23f6fSyx160601 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 2384d6c23f6fSyx160601 i = 3; 2385d6c23f6fSyx160601 msk.i6[0] = 0xffffffff; 2386d6c23f6fSyx160601 msk.i6[1] = 0xffffffff; 2387d6c23f6fSyx160601 msk.i6[2] = 0xffffffff; 2388d6c23f6fSyx160601 msk.i6[3] = 0xffffffff; 2389d6c23f6fSyx160601 rmsk = ifs->ifs_rdr6_masks[3]; 2390d6c23f6fSyx160601 WRITE_ENTER(&ifs->ifs_ipf_nat); 2391d6c23f6fSyx160601 /* 2392d6c23f6fSyx160601 * If there is no current entry in the nat table for this IP#, 2393d6c23f6fSyx160601 * create one for it (if there is a matching rule). 2394d6c23f6fSyx160601 */ 2395d6c23f6fSyx160601 maskloop: 2396d6c23f6fSyx160601 IP6_AND(&ipa, &msk, &iph); 2397d6c23f6fSyx160601 hv = NAT_HASH_FN6(&iph, 0, ifs->ifs_ipf_rdrrules_sz); 2398d6c23f6fSyx160601 for (np = ifs->ifs_rdr_rules[hv]; np; np = np->in_rnext) { 2399d6c23f6fSyx160601 if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) 2400d6c23f6fSyx160601 continue; 2401d6c23f6fSyx160601 if (np->in_v != fin->fin_v) 2402d6c23f6fSyx160601 continue; 2403d6c23f6fSyx160601 if (np->in_p && (np->in_p != fin->fin_p)) 2404d6c23f6fSyx160601 continue; 2405d6c23f6fSyx160601 if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) 2406d6c23f6fSyx160601 continue; 2407d6c23f6fSyx160601 if (np->in_flags & IPN_FILTER) { 2408d6c23f6fSyx160601 if (!nat6_match(fin, np)) 2409d6c23f6fSyx160601 continue; 2410d6c23f6fSyx160601 } else { 2411d6c23f6fSyx160601 if (!IP6_MASKEQ(&ipa, &np->in_out[1], 2412d6c23f6fSyx160601 &np->in_out[0])) 2413d6c23f6fSyx160601 continue; 2414d6c23f6fSyx160601 if (np->in_pmin && 2415d6c23f6fSyx160601 ((ntohs(np->in_pmax) < ntohs(dport)) || 2416d6c23f6fSyx160601 (ntohs(dport) < ntohs(np->in_pmin)))) 2417d6c23f6fSyx160601 continue; 2418d6c23f6fSyx160601 } 2419d6c23f6fSyx160601 2420d6c23f6fSyx160601 #ifdef IPF_V6_PROXIES 2421d6c23f6fSyx160601 if (*np->in_plabel != '\0') { 2422d6c23f6fSyx160601 if (!appr_ok(fin, tcp, np)) { 2423d6c23f6fSyx160601 continue; 2424d6c23f6fSyx160601 } 2425d6c23f6fSyx160601 } 2426d6c23f6fSyx160601 #endif 2427d6c23f6fSyx160601 2428d6c23f6fSyx160601 nat = nat6_new(fin, np, NULL, nflags, NAT_INBOUND); 2429d6c23f6fSyx160601 if (nat != NULL) { 2430d6c23f6fSyx160601 np->in_hits++; 2431d6c23f6fSyx160601 break; 2432d6c23f6fSyx160601 } else 2433d6c23f6fSyx160601 natfailed = -1; 2434d6c23f6fSyx160601 } 2435d6c23f6fSyx160601 2436d6c23f6fSyx160601 if ((np == NULL) && (i >= 0)) { 2437d6c23f6fSyx160601 while (i >= 0) { 2438d6c23f6fSyx160601 while (rmsk) { 2439d6c23f6fSyx160601 msk.i6[i] = htonl(ntohl(msk.i6[i])<<1); 2440d6c23f6fSyx160601 if ((rmsk & 0x80000000) != 0) { 2441d6c23f6fSyx160601 rmsk <<= 1; 2442d6c23f6fSyx160601 goto maskloop; 2443d6c23f6fSyx160601 } 2444d6c23f6fSyx160601 rmsk <<= 1; 2445d6c23f6fSyx160601 } 2446d6c23f6fSyx160601 msk.i6[i--] = 0; 2447d6c23f6fSyx160601 if (i >= 0) { 2448d6c23f6fSyx160601 rmsk = ifs->ifs_rdr6_masks[i]; 2449d6c23f6fSyx160601 if (rmsk != 0) 2450d6c23f6fSyx160601 goto maskloop; 2451d6c23f6fSyx160601 } 2452d6c23f6fSyx160601 } 2453d6c23f6fSyx160601 } 2454d6c23f6fSyx160601 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); 2455d6c23f6fSyx160601 } 2456d6c23f6fSyx160601 if (nat != NULL) { 2457d6c23f6fSyx160601 rval = fr_nat6in(fin, nat, natadd, nflags); 245833f2fefdSDarren Reed } else { 2459d6c23f6fSyx160601 rval = natfailed; 246033f2fefdSDarren Reed } 2461d6c23f6fSyx160601 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 2462d6c23f6fSyx160601 2463d6c23f6fSyx160601 if (rval == -1) { 2464d6c23f6fSyx160601 if (passp != NULL) 2465d6c23f6fSyx160601 *passp = FR_BLOCK; 2466d6c23f6fSyx160601 fin->fin_flx |= FI_BADNAT; 2467d6c23f6fSyx160601 } 2468d6c23f6fSyx160601 return rval; 2469d6c23f6fSyx160601 } 2470d6c23f6fSyx160601 2471d6c23f6fSyx160601 2472d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 2473d6c23f6fSyx160601 /* Function: fr_nat6in */ 2474d6c23f6fSyx160601 /* Returns: int - -1 == packet failed NAT checks so block it, */ 2475d6c23f6fSyx160601 /* 1 == packet was successfully translated. */ 2476d6c23f6fSyx160601 /* Parameters: fin(I) - pointer to packet information */ 2477d6c23f6fSyx160601 /* nat(I) - pointer to NAT structure */ 2478d6c23f6fSyx160601 /* natadd(I) - flag indicating if it is safe to add frag cache */ 2479d6c23f6fSyx160601 /* nflags(I) - NAT flags set for this packet */ 2480d6c23f6fSyx160601 /* Locks Held: ipf_nat (READ) */ 2481d6c23f6fSyx160601 /* */ 2482d6c23f6fSyx160601 /* Translate a packet coming "in" on an interface. */ 2483d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 2484d6c23f6fSyx160601 int fr_nat6in(fin, nat, natadd, nflags) 2485d6c23f6fSyx160601 fr_info_t *fin; 2486d6c23f6fSyx160601 nat_t *nat; 2487d6c23f6fSyx160601 int natadd; 2488d6c23f6fSyx160601 u_32_t nflags; 2489d6c23f6fSyx160601 { 2490d6c23f6fSyx160601 struct icmp6_hdr *icmp6; 2491d6c23f6fSyx160601 u_short *csump; 2492d6c23f6fSyx160601 tcphdr_t *tcp; 2493d6c23f6fSyx160601 ipnat_t *np; 2494d6c23f6fSyx160601 ipf_stack_t *ifs = fin->fin_ifs; 2495d6c23f6fSyx160601 2496*f56257d8SToomas Soome #if defined(SOLARIS) && defined(_KERNEL) 24977ddc9b1aSDarren Reed net_handle_t net_data_p = ifs->ifs_ipf_ipv6; 2498d6c23f6fSyx160601 #endif 2499d6c23f6fSyx160601 2500d6c23f6fSyx160601 tcp = NULL; 2501d6c23f6fSyx160601 csump = NULL; 2502d6c23f6fSyx160601 np = nat->nat_ptr; 2503d6c23f6fSyx160601 fin->fin_fr = nat->nat_fr; 2504d6c23f6fSyx160601 2505d6c23f6fSyx160601 if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) 2506d6c23f6fSyx160601 (void) fr_nat_newfrag(fin, 0, nat); 2507d6c23f6fSyx160601 2508d6c23f6fSyx160601 #ifdef IPF_V6_PROXIES 2509d6c23f6fSyx160601 if (np != NULL) { 2510d6c23f6fSyx160601 2511d6c23f6fSyx160601 /* ------------------------------------------------------------- */ 2512d6c23f6fSyx160601 /* A few quick notes: */ 2513d6c23f6fSyx160601 /* Following are test conditions prior to calling the */ 2514d6c23f6fSyx160601 /* appr_check routine. */ 2515d6c23f6fSyx160601 /* */ 2516d6c23f6fSyx160601 /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 2517d6c23f6fSyx160601 /* with a map rule, we attempt to match the packet's */ 2518d6c23f6fSyx160601 /* source port against in_dport, otherwise we'd compare the */ 2519d6c23f6fSyx160601 /* packet's destination. */ 2520d6c23f6fSyx160601 /* ------------------------------------------------------------- */ 2521d6c23f6fSyx160601 if (np->in_apr != NULL) { 2522d6c23f6fSyx160601 i = appr_check(fin, nat); 2523d6c23f6fSyx160601 if (i == -1) { 2524d6c23f6fSyx160601 return -1; 2525d6c23f6fSyx160601 } 2526d6c23f6fSyx160601 } 2527d6c23f6fSyx160601 } 2528d6c23f6fSyx160601 #endif 2529d6c23f6fSyx160601 2530d6c23f6fSyx160601 #ifdef IPFILTER_SYNC 2531d6c23f6fSyx160601 ipfsync_update(SMC_NAT, fin, nat->nat_sync); 2532d6c23f6fSyx160601 #endif 2533d6c23f6fSyx160601 2534d6c23f6fSyx160601 MUTEX_ENTER(&nat->nat_lock); 2535d6c23f6fSyx160601 nat->nat_bytes[0] += fin->fin_plen; 2536d6c23f6fSyx160601 nat->nat_pkts[0]++; 2537d6c23f6fSyx160601 MUTEX_EXIT(&nat->nat_lock); 2538d6c23f6fSyx160601 2539d6c23f6fSyx160601 fin->fin_ip6->ip6_dst = nat->nat_inip6.in6; 2540d6c23f6fSyx160601 fin->fin_dst6 = nat->nat_inip6; 2541d6c23f6fSyx160601 2542d6c23f6fSyx160601 if (nflags & IPN_TCPUDP) 2543d6c23f6fSyx160601 tcp = fin->fin_dp; 2544d6c23f6fSyx160601 2545d6c23f6fSyx160601 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 2546d6c23f6fSyx160601 if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) { 2547d6c23f6fSyx160601 tcp->th_dport = nat->nat_inport; 2548d6c23f6fSyx160601 fin->fin_data[1] = ntohs(nat->nat_inport); 2549d6c23f6fSyx160601 } 2550d6c23f6fSyx160601 2551d6c23f6fSyx160601 2552d6c23f6fSyx160601 if ((nat->nat_inport != 0) && (nflags & IPN_ICMPQUERY)) { 2553d6c23f6fSyx160601 icmp6 = fin->fin_dp; 2554d6c23f6fSyx160601 2555d6c23f6fSyx160601 icmp6->icmp6_id = nat->nat_inport; 2556d6c23f6fSyx160601 } 2557d6c23f6fSyx160601 2558d6c23f6fSyx160601 csump = nat_proto(fin, nat, nflags); 2559d6c23f6fSyx160601 } 2560d6c23f6fSyx160601 2561d6c23f6fSyx160601 nat_update(fin, nat, np); 2562d6c23f6fSyx160601 2563d6c23f6fSyx160601 /* 2564d6c23f6fSyx160601 * In case they are being forwarded, inbound packets always need to have 2565d6c23f6fSyx160601 * their checksum adjusted even if hardware checksum validation said OK. 2566d6c23f6fSyx160601 */ 2567d6c23f6fSyx160601 if (csump != NULL) { 2568d6c23f6fSyx160601 if (nat->nat_dir == NAT_OUTBOUND) 2569d6c23f6fSyx160601 fix_incksum(csump, nat->nat_sumd[0]); 2570d6c23f6fSyx160601 else 2571d6c23f6fSyx160601 fix_outcksum(csump, nat->nat_sumd[0]); 2572d6c23f6fSyx160601 } 2573d6c23f6fSyx160601 2574*f56257d8SToomas Soome #if defined(SOLARIS) && defined(_KERNEL) 2575d6c23f6fSyx160601 if (nflags & IPN_TCPUDP && 2576d6c23f6fSyx160601 NET_IS_HCK_L4_PART(net_data_p, fin->fin_m)) { 2577d6c23f6fSyx160601 /* 2578d6c23f6fSyx160601 * Need to adjust the partial checksum result stored in 2579d6c23f6fSyx160601 * db_cksum16, which will be used for validation in IP. 2580d6c23f6fSyx160601 * See IP_CKSUM_RECV(). 2581d6c23f6fSyx160601 * Adjustment data should be the inverse of the IP address 2582d6c23f6fSyx160601 * changes, because db_cksum16 is supposed to be the complement 2583d6c23f6fSyx160601 * of the pesudo header. 2584d6c23f6fSyx160601 */ 2585d6c23f6fSyx160601 csump = &fin->fin_m->b_datap->db_cksum16; 2586d6c23f6fSyx160601 if (nat->nat_dir == NAT_OUTBOUND) 2587d6c23f6fSyx160601 fix_outcksum(csump, nat->nat_sumd[1]); 2588d6c23f6fSyx160601 else 2589d6c23f6fSyx160601 fix_incksum(csump, nat->nat_sumd[1]); 2590d6c23f6fSyx160601 } 2591d6c23f6fSyx160601 #endif 2592d6c23f6fSyx160601 2593d6c23f6fSyx160601 ATOMIC_INCL(ifs->ifs_nat_stats.ns_mapped[0]); 2594d6c23f6fSyx160601 fin->fin_flx |= FI_NATED; 2595d6c23f6fSyx160601 if (np != NULL && np->in_tag.ipt_num[0] != 0) 2596d6c23f6fSyx160601 fin->fin_nattag = &np->in_tag; 2597d6c23f6fSyx160601 return 1; 2598d6c23f6fSyx160601 } 2599d6c23f6fSyx160601 2600d6c23f6fSyx160601 2601d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 2602d6c23f6fSyx160601 /* Function: nat_icmpquerytype6 */ 2603d6c23f6fSyx160601 /* Returns: int - 1 == success, 0 == failure */ 2604d6c23f6fSyx160601 /* Parameters: icmptype(I) - ICMP type number */ 2605d6c23f6fSyx160601 /* */ 2606d6c23f6fSyx160601 /* Tests to see if the ICMP type number passed is a query/response type or */ 2607d6c23f6fSyx160601 /* not. */ 2608d6c23f6fSyx160601 /* ------------------------------------------------------------------------ */ 2609d6c23f6fSyx160601 static INLINE int nat_icmpquerytype6(icmptype) 2610d6c23f6fSyx160601 int icmptype; 2611d6c23f6fSyx160601 { 2612d6c23f6fSyx160601 2613d6c23f6fSyx160601 /* 2614d6c23f6fSyx160601 * For the ICMP query NAT code, it is essential that both the query 2615d6c23f6fSyx160601 * and the reply match on the NAT rule. Because the NAT structure 2616d6c23f6fSyx160601 * does not keep track of the icmptype, and a single NAT structure 2617d6c23f6fSyx160601 * is used for all icmp types with the same src, dest and id, we 2618d6c23f6fSyx160601 * simply define the replies as queries as well. The funny thing is, 2619d6c23f6fSyx160601 * altough it seems silly to call a reply a query, this is exactly 2620d6c23f6fSyx160601 * as it is defined in the IPv4 specification 2621d6c23f6fSyx160601 */ 2622d6c23f6fSyx160601 2623d6c23f6fSyx160601 switch (icmptype) 2624d6c23f6fSyx160601 { 2625d6c23f6fSyx160601 2626d6c23f6fSyx160601 case ICMP6_ECHO_REPLY: 2627d6c23f6fSyx160601 case ICMP6_ECHO_REQUEST: 2628d6c23f6fSyx160601 /* route aedvertisement/solliciation is currently unsupported: */ 2629d6c23f6fSyx160601 /* it would require rewriting the ICMP data section */ 2630d6c23f6fSyx160601 case ICMP6_MEMBERSHIP_QUERY: 2631d6c23f6fSyx160601 case ICMP6_MEMBERSHIP_REPORT: 2632d6c23f6fSyx160601 case ICMP6_MEMBERSHIP_REDUCTION: 2633d6c23f6fSyx160601 case ICMP6_WRUREQUEST: 2634d6c23f6fSyx160601 case ICMP6_WRUREPLY: 2635d6c23f6fSyx160601 case MLD6_MTRACE_RESP: 2636d6c23f6fSyx160601 case MLD6_MTRACE: 2637d6c23f6fSyx160601 return 1; 2638d6c23f6fSyx160601 default: 2639d6c23f6fSyx160601 return 0; 2640d6c23f6fSyx160601 } 2641d6c23f6fSyx160601 } 2642