1254401Scy/* 2254401Scy * Copyright (C) 2012 by Darren Reed. 3254401Scy * 4254401Scy * See the IPFILTER.LICENCE file for details on licencing. 5254401Scy */ 6254401Scy#if defined(KERNEL) || defined(_KERNEL) 7254401Scy# undef KERNEL 8255332Scy# undef _KERNEL 9254401Scy# define KERNEL 1 10255332Scy# define _KERNEL 1 11254401Scy#endif 12254401Scy#include <sys/errno.h> 13254401Scy#include <sys/types.h> 14254401Scy#include <sys/param.h> 15254401Scy#include <sys/time.h> 16254401Scy#include <sys/file.h> 17254401Scy#if defined(_KERNEL) && defined(__NetBSD_Version__) && \ 18254401Scy (__NetBSD_Version__ >= 399002000) 19254401Scy# include <sys/kauth.h> 20254401Scy#endif 21254401Scy#if !defined(_KERNEL) 22254401Scy# include <stdio.h> 23254401Scy# include <string.h> 24254401Scy# include <stdlib.h> 25255332Scy# define _KERNEL 26254401Scy# ifdef ipf_nat6__OpenBSD__ 27254401Scystruct file; 28254401Scy# endif 29254401Scy# include <sys/uio.h> 30255332Scy# undef _KERNEL 31254401Scy#endif 32369277Scy#if defined(_KERNEL) && defined(__FreeBSD__) 33254401Scy# include <sys/filio.h> 34254401Scy# include <sys/fcntl.h> 35254401Scy#else 36254401Scy# include <sys/ioctl.h> 37254401Scy#endif 38254401Scy# include <sys/fcntl.h> 39254401Scy# include <sys/protosw.h> 40254401Scy#include <sys/socket.h> 41254401Scy#if defined(_KERNEL) 42254401Scy# include <sys/systm.h> 43344833Scy# if !defined(__SVR4) 44254401Scy# include <sys/mbuf.h> 45254401Scy# endif 46254401Scy#endif 47344833Scy#if defined(__SVR4) 48254401Scy# include <sys/filio.h> 49254401Scy# include <sys/byteorder.h> 50255332Scy# ifdef _KERNEL 51254401Scy# include <sys/dditypes.h> 52254401Scy# endif 53254401Scy# include <sys/stream.h> 54254401Scy# include <sys/kmem.h> 55254401Scy#endif 56369277Scy#if defined(__FreeBSD__) 57254401Scy# include <sys/queue.h> 58254401Scy#endif 59254401Scy#include <net/if.h> 60369277Scy#if defined(__FreeBSD__) 61254401Scy# include <net/if_var.h> 62254401Scy#endif 63254401Scy#ifdef sun 64254401Scy# include <net/af.h> 65254401Scy#endif 66254401Scy#include <net/route.h> 67254401Scy#include <netinet/in.h> 68254401Scy#include <netinet/in_systm.h> 69254401Scy#include <netinet/ip.h> 70254401Scy 71254401Scy#ifdef RFC1825 72254401Scy# include <vpn/md5.h> 73254401Scy# include <vpn/ipsec.h> 74254401Scyextern struct ifnet vpnif; 75254401Scy#endif 76254401Scy 77254401Scy# include <netinet/ip_var.h> 78254401Scy#include <netinet/tcp.h> 79254401Scy#include <netinet/udp.h> 80254401Scy#include <netinet/ip_icmp.h> 81254401Scy#include "netinet/ip_compat.h" 82254401Scy#include <netinet/tcpip.h> 83254401Scy#include "netinet/ip_fil.h" 84254401Scy#include "netinet/ip_nat.h" 85254401Scy#include "netinet/ip_frag.h" 86254401Scy#include "netinet/ip_state.h" 87254401Scy#include "netinet/ip_proxy.h" 88254401Scy#include "netinet/ip_lookup.h" 89254401Scy#include "netinet/ip_dstlist.h" 90254401Scy#include "netinet/ip_sync.h" 91369277Scy#if defined(__FreeBSD__) 92254401Scy# include <sys/malloc.h> 93254401Scy#endif 94254401Scy#ifdef HAS_SYS_MD5_H 95254401Scy# include <sys/md5.h> 96254401Scy#else 97254401Scy# include "md5.h" 98254401Scy#endif 99254401Scy/* END OF INCLUDES */ 100254401Scy 101254401Scy#undef SOCKADDR_IN 102254401Scy#define SOCKADDR_IN struct sockaddr_in 103254401Scy 104254401Scy#if !defined(lint) 105254401Scystatic const char rcsid[] = "@(#)$Id: ip_nat6.c,v 1.22.2.20 2012/07/22 08:04:23 darren_r Exp $"; 106254401Scy#endif 107254401Scy 108254401Scy#ifdef USE_INET6 109369245Sgit2svnstatic struct hostmap *ipf_nat6_hostmap(ipf_nat_softc_t *, ipnat_t *, 110254401Scy i6addr_t *, i6addr_t *, 111369245Sgit2svn i6addr_t *, u_32_t); 112369245Sgit2svnstatic int ipf_nat6_match(fr_info_t *, ipnat_t *); 113369245Sgit2svnstatic void ipf_nat6_tabmove(ipf_nat_softc_t *, nat_t *); 114369245Sgit2svnstatic int ipf_nat6_decap(fr_info_t *, nat_t *); 115369245Sgit2svnstatic int ipf_nat6_nextaddr(fr_info_t *, nat_addr_t *, i6addr_t *, 116369245Sgit2svn i6addr_t *); 117369245Sgit2svnstatic int ipf_nat6_icmpquerytype(int); 118369245Sgit2svnstatic int ipf_nat6_out(fr_info_t *, nat_t *, int, u_32_t); 119369245Sgit2svnstatic int ipf_nat6_in(fr_info_t *, nat_t *, int, u_32_t); 120369245Sgit2svnstatic int ipf_nat6_builddivertmp(ipf_nat_softc_t *, ipnat_t *); 121369245Sgit2svnstatic int ipf_nat6_nextaddrinit(ipf_main_softc_t *, char *, 122369245Sgit2svn nat_addr_t *, int, void *); 123369245Sgit2svnstatic int ipf_nat6_insert(ipf_main_softc_t *, ipf_nat_softc_t *, 124369245Sgit2svn nat_t *); 125254401Scy 126254401Scy 127254401Scy#define NINCLSIDE6(y,x) ATOMIC_INCL(softn->ipf_nat_stats.ns_side6[y].x) 128254401Scy#define NBUMPSIDE(y,x) softn->ipf_nat_stats.ns_side[y].x++ 129254401Scy#define NBUMPSIDE6(y,x) softn->ipf_nat_stats.ns_side6[y].x++ 130254401Scy#define NBUMPSIDE6D(y,x) \ 131254401Scy do { \ 132254401Scy softn->ipf_nat_stats.ns_side6[y].x++; \ 133254401Scy DT(x); \ 134254401Scy } while (0) 135254401Scy#define NBUMPSIDE6DX(y,x,z) \ 136254401Scy do { \ 137254401Scy softn->ipf_nat_stats.ns_side6[y].x++; \ 138254401Scy DT(z); \ 139254401Scy } while (0) 140254401Scy 141254401Scy 142254401Scy/* ------------------------------------------------------------------------ */ 143254401Scy/* Function: ipf_nat6_ruleaddrinit */ 144254401Scy/* Returns: int - 0 == success, else failure */ 145254401Scy/* Parameters: in(I) - NAT rule that requires address fields to be init'd */ 146254401Scy/* */ 147254401Scy/* For each of the source/destination address fields in a NAT rule, call */ 148254401Scy/* ipf_nat6_nextaddrinit() to prepare the structure for active duty. Other */ 149254401Scy/* IPv6 specific actions can also be taken care of here. */ 150254401Scy/* ------------------------------------------------------------------------ */ 151254401Scyint 152254401Scyipf_nat6_ruleaddrinit(softc, softn, n) 153254401Scy ipf_main_softc_t *softc; 154254401Scy ipf_nat_softc_t *softn; 155254401Scy ipnat_t *n; 156254401Scy{ 157254401Scy int idx, error; 158254401Scy 159254401Scy if (n->in_redir == NAT_BIMAP) { 160254401Scy n->in_ndstip6 = n->in_osrcip6; 161254401Scy n->in_ndstmsk6 = n->in_osrcmsk6; 162254401Scy n->in_odstip6 = n->in_nsrcip6; 163254401Scy n->in_odstmsk6 = n->in_nsrcmsk6; 164254401Scy 165254401Scy } 166254401Scy 167254401Scy if (n->in_redir & NAT_REDIRECT) 168254401Scy idx = 1; 169254401Scy else 170254401Scy idx = 0; 171254401Scy /* 172254401Scy * Initialise all of the address fields. 173254401Scy */ 174254401Scy error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_osrc, 1, 175254401Scy n->in_ifps[idx]); 176254401Scy if (error != 0) 177254401Scy return error; 178254401Scy 179254401Scy error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_odst, 1, 180254401Scy n->in_ifps[idx]); 181254401Scy if (error != 0) 182254401Scy return error; 183254401Scy 184254401Scy error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_nsrc, 1, 185254401Scy n->in_ifps[idx]); 186254401Scy if (error != 0) 187254401Scy return error; 188254401Scy 189254401Scy error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_ndst, 1, 190254401Scy n->in_ifps[idx]); 191254401Scy if (error != 0) 192254401Scy return error; 193254401Scy 194254401Scy if (n->in_redir & NAT_DIVERTUDP) 195254401Scy ipf_nat6_builddivertmp(softn, n); 196254401Scy return 0; 197254401Scy} 198254401Scy 199254401Scy 200254401Scy/* ------------------------------------------------------------------------ */ 201254401Scy/* Function: ipf_nat6_addrdr */ 202254401Scy/* Returns: Nil */ 203254401Scy/* Parameters: n(I) - pointer to NAT rule to add */ 204254401Scy/* */ 205254401Scy/* Adds a redirect rule to the hash table of redirect rules and the list of */ 206254401Scy/* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ 207254401Scy/* use by redirect rules. */ 208254401Scy/* ------------------------------------------------------------------------ */ 209254401Scyvoid 210254401Scyipf_nat6_addrdr(softn, n) 211254401Scy ipf_nat_softc_t *softn; 212254401Scy ipnat_t *n; 213254401Scy{ 214254401Scy i6addr_t *mask; 215254401Scy ipnat_t **np; 216254401Scy i6addr_t j; 217254401Scy u_int hv; 218254401Scy int k; 219254401Scy 220254401Scy if ((n->in_redir & NAT_BIMAP) == NAT_BIMAP) { 221254401Scy k = count6bits(n->in_nsrcmsk6.i6); 222254401Scy mask = &n->in_nsrcmsk6; 223254401Scy IP6_AND(&n->in_odstip6, &n->in_odstmsk6, &j); 224254401Scy hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_rdrrules_sz); 225254401Scy 226254401Scy } else if (n->in_odstatype == FRI_NORMAL) { 227254401Scy k = count6bits(n->in_odstmsk6.i6); 228254401Scy mask = &n->in_odstmsk6; 229254401Scy IP6_AND(&n->in_odstip6, &n->in_odstmsk6, &j); 230254401Scy hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_rdrrules_sz); 231254401Scy } else { 232254401Scy k = 0; 233254401Scy hv = 0; 234254401Scy mask = NULL; 235254401Scy } 236254401Scy ipf_inet6_mask_add(k, mask, &softn->ipf_nat6_rdr_mask); 237254401Scy 238254401Scy np = softn->ipf_nat_rdr_rules + hv; 239254401Scy while (*np != NULL) 240254401Scy np = &(*np)->in_rnext; 241254401Scy n->in_rnext = NULL; 242254401Scy n->in_prnext = np; 243254401Scy n->in_hv[0] = hv; 244254401Scy n->in_use++; 245254401Scy *np = n; 246254401Scy} 247254401Scy 248254401Scy 249254401Scy/* ------------------------------------------------------------------------ */ 250254401Scy/* Function: ipf_nat6_addmap */ 251254401Scy/* Returns: Nil */ 252254401Scy/* Parameters: n(I) - pointer to NAT rule to add */ 253254401Scy/* */ 254254401Scy/* Adds a NAT map rule to the hash table of rules and the list of loaded */ 255254401Scy/* NAT rules. Updates the bitmask indicating which netmasks are in use by */ 256254401Scy/* redirect rules. */ 257254401Scy/* ------------------------------------------------------------------------ */ 258254401Scyvoid 259254401Scyipf_nat6_addmap(softn, n) 260254401Scy ipf_nat_softc_t *softn; 261254401Scy ipnat_t *n; 262254401Scy{ 263254401Scy i6addr_t *mask; 264254401Scy ipnat_t **np; 265254401Scy i6addr_t j; 266254401Scy u_int hv; 267254401Scy int k; 268254401Scy 269254401Scy if (n->in_osrcatype == FRI_NORMAL) { 270254401Scy k = count6bits(n->in_osrcmsk6.i6); 271254401Scy mask = &n->in_osrcmsk6; 272254401Scy IP6_AND(&n->in_osrcip6, &n->in_osrcmsk6, &j); 273254401Scy hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_maprules_sz); 274254401Scy } else { 275254401Scy k = 0; 276254401Scy hv = 0; 277254401Scy mask = NULL; 278254401Scy } 279254401Scy ipf_inet6_mask_add(k, mask, &softn->ipf_nat6_map_mask); 280254401Scy 281254401Scy np = softn->ipf_nat_map_rules + hv; 282254401Scy while (*np != NULL) 283254401Scy np = &(*np)->in_mnext; 284254401Scy n->in_mnext = NULL; 285254401Scy n->in_pmnext = np; 286254401Scy n->in_hv[1] = hv; 287254401Scy n->in_use++; 288254401Scy *np = n; 289254401Scy} 290254401Scy 291254401Scy 292254401Scy/* ------------------------------------------------------------------------ */ 293254401Scy/* Function: ipf_nat6_del_rdr */ 294254401Scy/* Returns: Nil */ 295254401Scy/* Parameters: n(I) - pointer to NAT rule to delete */ 296254401Scy/* */ 297254401Scy/* Removes a NAT rdr rule from the hash table of NAT rdr rules. */ 298254401Scy/* ------------------------------------------------------------------------ */ 299254401Scyvoid 300254401Scyipf_nat6_delrdr(softn, n) 301254401Scy ipf_nat_softc_t *softn; 302254401Scy ipnat_t *n; 303254401Scy{ 304254401Scy i6addr_t *mask; 305254401Scy int k; 306254401Scy 307254401Scy if ((n->in_redir & NAT_BIMAP) == NAT_BIMAP) { 308254401Scy k = count6bits(n->in_nsrcmsk6.i6); 309254401Scy mask = &n->in_nsrcmsk6; 310254401Scy } else if (n->in_odstatype == FRI_NORMAL) { 311254401Scy k = count6bits(n->in_odstmsk6.i6); 312254401Scy mask = &n->in_odstmsk6; 313254401Scy } else { 314254401Scy k = 0; 315254401Scy mask = NULL; 316254401Scy } 317254401Scy ipf_inet6_mask_del(k, mask, &softn->ipf_nat6_rdr_mask); 318254401Scy 319254401Scy if (n->in_rnext != NULL) 320254401Scy n->in_rnext->in_prnext = n->in_prnext; 321254401Scy *n->in_prnext = n->in_rnext; 322254401Scy n->in_use--; 323254401Scy} 324254401Scy 325254401Scy 326254401Scy/* ------------------------------------------------------------------------ */ 327254401Scy/* Function: ipf_nat6_delmap */ 328254401Scy/* Returns: Nil */ 329254401Scy/* Parameters: n(I) - pointer to NAT rule to delete */ 330254401Scy/* */ 331254401Scy/* Removes a NAT map rule from the hash table of NAT map rules. */ 332254401Scy/* ------------------------------------------------------------------------ */ 333254401Scyvoid 334254401Scyipf_nat6_delmap(softn, n) 335254401Scy ipf_nat_softc_t *softn; 336254401Scy ipnat_t *n; 337254401Scy{ 338254401Scy i6addr_t *mask; 339254401Scy int k; 340254401Scy 341254401Scy if (n->in_osrcatype == FRI_NORMAL) { 342254401Scy k = count6bits(n->in_osrcmsk6.i6); 343254401Scy mask = &n->in_osrcmsk6; 344254401Scy } else { 345254401Scy k = 0; 346254401Scy mask = NULL; 347254401Scy } 348254401Scy ipf_inet6_mask_del(k, mask, &softn->ipf_nat6_map_mask); 349254401Scy 350254401Scy if (n->in_mnext != NULL) 351254401Scy n->in_mnext->in_pmnext = n->in_pmnext; 352254401Scy *n->in_pmnext = n->in_mnext; 353254401Scy n->in_use--; 354254401Scy} 355254401Scy 356254401Scy 357254401Scy/* ------------------------------------------------------------------------ */ 358254401Scy/* Function: ipf_nat6_hostmap */ 359254401Scy/* Returns: struct hostmap* - NULL if no hostmap could be created, */ 360254401Scy/* else a pointer to the hostmapping to use */ 361254401Scy/* Parameters: np(I) - pointer to NAT rule */ 362254401Scy/* real(I) - real IP address */ 363254401Scy/* map(I) - mapped IP address */ 364254401Scy/* port(I) - destination port number */ 365254401Scy/* Write Locks: ipf_nat */ 366254401Scy/* */ 367254401Scy/* Check if an ip address has already been allocated for a given mapping */ 368254401Scy/* that is not doing port based translation. If is not yet allocated, then */ 369254401Scy/* create a new entry if a non-NULL NAT rule pointer has been supplied. */ 370254401Scy/* ------------------------------------------------------------------------ */ 371254401Scystatic struct hostmap * 372254401Scyipf_nat6_hostmap(softn, np, src, dst, map, port) 373254401Scy ipf_nat_softc_t *softn; 374254401Scy ipnat_t *np; 375254401Scy i6addr_t *src, *dst, *map; 376254401Scy u_32_t port; 377254401Scy{ 378254401Scy hostmap_t *hm; 379254401Scy u_int hv; 380254401Scy 381254401Scy hv = (src->i6[3] ^ dst->i6[3]); 382254401Scy hv += (src->i6[2] ^ dst->i6[2]); 383254401Scy hv += (src->i6[1] ^ dst->i6[1]); 384254401Scy hv += (src->i6[0] ^ dst->i6[0]); 385254401Scy hv += src->i6[3]; 386254401Scy hv += src->i6[2]; 387254401Scy hv += src->i6[1]; 388254401Scy hv += src->i6[0]; 389254401Scy hv += dst->i6[3]; 390254401Scy hv += dst->i6[2]; 391254401Scy hv += dst->i6[1]; 392254401Scy hv += dst->i6[0]; 393369275Scy hv %= softn->ipf_nat_hostmap_sz; 394254401Scy for (hm = softn->ipf_hm_maptable[hv]; hm; hm = hm->hm_next) 395254401Scy if (IP6_EQ(&hm->hm_osrc6, src) && 396254401Scy IP6_EQ(&hm->hm_odst6, dst) && 397254401Scy ((np == NULL) || (np == hm->hm_ipnat)) && 398254401Scy ((port == 0) || (port == hm->hm_port))) { 399254401Scy softn->ipf_nat_stats.ns_hm_addref++; 400254401Scy hm->hm_ref++; 401254401Scy return hm; 402254401Scy } 403254401Scy 404254401Scy if (np == NULL) { 405254401Scy softn->ipf_nat_stats.ns_hm_nullnp++; 406254401Scy return NULL; 407254401Scy } 408254401Scy 409254401Scy KMALLOC(hm, hostmap_t *); 410254401Scy if (hm) { 411254401Scy hm->hm_next = softn->ipf_hm_maplist; 412254401Scy hm->hm_pnext = &softn->ipf_hm_maplist; 413254401Scy if (softn->ipf_hm_maplist != NULL) 414254401Scy softn->ipf_hm_maplist->hm_pnext = &hm->hm_next; 415254401Scy softn->ipf_hm_maplist = hm; 416254401Scy hm->hm_hnext = softn->ipf_hm_maptable[hv]; 417254401Scy hm->hm_phnext = softn->ipf_hm_maptable + hv; 418254401Scy if (softn->ipf_hm_maptable[hv] != NULL) 419254401Scy softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; 420254401Scy softn->ipf_hm_maptable[hv] = hm; 421254401Scy hm->hm_ipnat = np; 422254401Scy np->in_use++; 423254401Scy hm->hm_osrcip6 = *src; 424254401Scy hm->hm_odstip6 = *dst; 425254401Scy hm->hm_nsrcip6 = *map; 426254401Scy hm->hm_ndstip6.i6[0] = 0; 427254401Scy hm->hm_ndstip6.i6[1] = 0; 428254401Scy hm->hm_ndstip6.i6[2] = 0; 429254401Scy hm->hm_ndstip6.i6[3] = 0; 430254401Scy hm->hm_ref = 1; 431254401Scy hm->hm_port = port; 432254401Scy hm->hm_hv = hv; 433254401Scy hm->hm_v = 6; 434254401Scy softn->ipf_nat_stats.ns_hm_new++; 435254401Scy } else { 436254401Scy softn->ipf_nat_stats.ns_hm_newfail++; 437254401Scy } 438254401Scy return hm; 439254401Scy} 440254401Scy 441254401Scy 442254401Scy/* ------------------------------------------------------------------------ */ 443254401Scy/* Function: ipf_nat6_newmap */ 444254401Scy/* Returns: int - -1 == error, 0 == success */ 445254401Scy/* Parameters: fin(I) - pointer to packet information */ 446254401Scy/* nat(I) - pointer to NAT entry */ 447254401Scy/* ni(I) - pointer to structure with misc. information needed */ 448254401Scy/* to create new NAT entry. */ 449254401Scy/* */ 450254401Scy/* Given an empty NAT structure, populate it with new information about a */ 451254401Scy/* new NAT session, as defined by the matching NAT rule. */ 452254401Scy/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 453254401Scy/* to the new IP address for the translation. */ 454254401Scy/* ------------------------------------------------------------------------ */ 455254401Scyint 456254401Scyipf_nat6_newmap(fin, nat, ni) 457254401Scy fr_info_t *fin; 458254401Scy nat_t *nat; 459254401Scy natinfo_t *ni; 460254401Scy{ 461254401Scy ipf_main_softc_t *softc = fin->fin_main_soft; 462254401Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 463254401Scy u_short st_port, dport, sport, port, sp, dp; 464254401Scy i6addr_t in, st_ip; 465254401Scy hostmap_t *hm; 466254401Scy u_32_t flags; 467254401Scy ipnat_t *np; 468254401Scy nat_t *natl; 469254401Scy int l; 470254401Scy 471254401Scy /* 472254401Scy * If it's an outbound packet which doesn't match any existing 473254401Scy * record, then create a new port 474254401Scy */ 475254401Scy l = 0; 476254401Scy hm = NULL; 477254401Scy np = ni->nai_np; 478254401Scy st_ip = np->in_snip6; 479254401Scy st_port = np->in_spnext; 480254401Scy flags = nat->nat_flags; 481254401Scy 482254401Scy if (flags & IPN_ICMPQUERY) { 483254401Scy sport = fin->fin_data[1]; 484254401Scy dport = 0; 485254401Scy } else { 486254401Scy sport = htons(fin->fin_data[0]); 487254401Scy dport = htons(fin->fin_data[1]); 488254401Scy } 489254401Scy 490254401Scy /* 491254401Scy * Do a loop until we either run out of entries to try or we find 492254401Scy * a NAT mapping that isn't currently being used. This is done 493254401Scy * because the change to the source is not (usually) being fixed. 494254401Scy */ 495254401Scy do { 496254401Scy port = 0; 497254401Scy in = np->in_nsrc.na_nextaddr; 498254401Scy if (l == 0) { 499254401Scy /* 500254401Scy * Check to see if there is an existing NAT 501254401Scy * setup for this IP address pair. 502254401Scy */ 503254401Scy hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6, 504254401Scy &fin->fin_dst6, &in, 0); 505254401Scy if (hm != NULL) 506254401Scy in = hm->hm_nsrcip6; 507254401Scy } else if ((l == 1) && (hm != NULL)) { 508254401Scy ipf_nat_hostmapdel(softc, &hm); 509254401Scy } 510254401Scy 511254401Scy nat->nat_hm = hm; 512254401Scy 513254401Scy if (IP6_ISONES(&np->in_nsrcmsk6) && (np->in_spnext == 0)) { 514254401Scy if (l > 0) { 515254401Scy NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_1); 516254401Scy return -1; 517254401Scy } 518254401Scy } 519254401Scy 520254401Scy if ((np->in_redir == NAT_BIMAP) && 521254401Scy IP6_EQ(&np->in_osrcmsk6, &np->in_nsrcmsk6)) { 522254401Scy i6addr_t temp; 523254401Scy /* 524254401Scy * map the address block in a 1:1 fashion 525254401Scy */ 526254401Scy temp.i6[0] = fin->fin_src6.i6[0] & 527254401Scy ~np->in_osrcmsk6.i6[0]; 528254401Scy temp.i6[1] = fin->fin_src6.i6[1] & 529254401Scy ~np->in_osrcmsk6.i6[1]; 530254401Scy temp.i6[2] = fin->fin_src6.i6[2] & 531254401Scy ~np->in_osrcmsk6.i6[0]; 532254401Scy temp.i6[3] = fin->fin_src6.i6[3] & 533254401Scy ~np->in_osrcmsk6.i6[3]; 534254401Scy in = np->in_nsrcip6; 535254401Scy IP6_MERGE(&in, &temp, &np->in_osrc); 536254401Scy 537254401Scy#ifdef NEED_128BIT_MATH 538254401Scy } else if (np->in_redir & NAT_MAPBLK) { 539254401Scy if ((l >= np->in_ppip) || ((l > 0) && 540254401Scy !(flags & IPN_TCPUDP))) { 541254401Scy NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_2); 542254401Scy return -1; 543254401Scy } 544254401Scy /* 545254401Scy * map-block - Calculate destination address. 546254401Scy */ 547254401Scy IP6_MASK(&in, &fin->fin_src6, &np->in_osrcmsk6); 548254401Scy in = ntohl(in); 549254401Scy inb = in; 550254401Scy in.s_addr /= np->in_ippip; 551254401Scy in.s_addr &= ntohl(~np->in_nsrcmsk6); 552254401Scy in.s_addr += ntohl(np->in_nsrcaddr6); 553254401Scy /* 554254401Scy * Calculate destination port. 555254401Scy */ 556254401Scy if ((flags & IPN_TCPUDP) && 557254401Scy (np->in_ppip != 0)) { 558254401Scy port = ntohs(sport) + l; 559254401Scy port %= np->in_ppip; 560254401Scy port += np->in_ppip * 561254401Scy (inb.s_addr % np->in_ippip); 562254401Scy port += MAPBLK_MINPORT; 563254401Scy port = htons(port); 564254401Scy } 565254401Scy#endif 566254401Scy 567254401Scy } else if (IP6_ISZERO(&np->in_nsrcaddr) && 568254401Scy IP6_ISONES(&np->in_nsrcmsk)) { 569254401Scy /* 570254401Scy * 0/32 - use the interface's IP address. 571254401Scy */ 572254401Scy if ((l > 0) || 573254401Scy ipf_ifpaddr(softc, 6, FRI_NORMAL, fin->fin_ifp, 574254401Scy &in, NULL) == -1) { 575254401Scy NBUMPSIDE6DX(1, ns_new_ifpaddr, 576254401Scy ns_new_ifpaddr_1); 577254401Scy return -1; 578254401Scy } 579254401Scy 580254401Scy } else if (IP6_ISZERO(&np->in_nsrcip6) && 581254401Scy IP6_ISZERO(&np->in_nsrcmsk6)) { 582254401Scy /* 583254401Scy * 0/0 - use the original source address/port. 584254401Scy */ 585254401Scy if (l > 0) { 586254401Scy NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_3); 587254401Scy return -1; 588254401Scy } 589254401Scy in = fin->fin_src6; 590254401Scy 591254401Scy } else if (!IP6_ISONES(&np->in_nsrcmsk6) && 592254401Scy (np->in_spnext == 0) && ((l > 0) || (hm == NULL))) { 593254401Scy IP6_INC(&np->in_snip6); 594254401Scy } 595254401Scy 596254401Scy natl = NULL; 597254401Scy 598254401Scy if ((flags & IPN_TCPUDP) && 599254401Scy ((np->in_redir & NAT_MAPBLK) == 0) && 600254401Scy (np->in_flags & IPN_AUTOPORTMAP)) { 601254401Scy#ifdef NEED_128BIT_MATH 602254401Scy /* 603254401Scy * "ports auto" (without map-block) 604254401Scy */ 605254401Scy if ((l > 0) && (l % np->in_ppip == 0)) { 606254401Scy if ((l > np->in_ppip) && 607254401Scy !IP6_ISONES(&np->in_nsrcmsk)) { 608254401Scy IP6_INC(&np->in_snip6) 609254401Scy } 610254401Scy } 611254401Scy if (np->in_ppip != 0) { 612254401Scy port = ntohs(sport); 613254401Scy port += (l % np->in_ppip); 614254401Scy port %= np->in_ppip; 615254401Scy port += np->in_ppip * 616254401Scy (ntohl(fin->fin_src6) % 617254401Scy np->in_ippip); 618254401Scy port += MAPBLK_MINPORT; 619254401Scy port = htons(port); 620254401Scy } 621254401Scy#endif 622254401Scy 623254401Scy } else if (((np->in_redir & NAT_MAPBLK) == 0) && 624254401Scy (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) { 625254401Scy /* 626254401Scy * Standard port translation. Select next port. 627254401Scy */ 628254401Scy if (np->in_flags & IPN_SEQUENTIAL) { 629254401Scy port = np->in_spnext; 630254401Scy } else { 631254401Scy port = ipf_random() % (np->in_spmax - 632254401Scy np->in_spmin + 1); 633254401Scy port += np->in_spmin; 634254401Scy } 635254401Scy port = htons(port); 636254401Scy np->in_spnext++; 637254401Scy 638254401Scy if (np->in_spnext > np->in_spmax) { 639254401Scy np->in_spnext = np->in_spmin; 640254401Scy if (!IP6_ISONES(&np->in_nsrcmsk6)) { 641254401Scy IP6_INC(&np->in_snip6); 642254401Scy } 643254401Scy } 644254401Scy } 645254401Scy 646254401Scy if (np->in_flags & IPN_SIPRANGE) { 647254401Scy if (IP6_GT(&np->in_snip, &np->in_nsrcmsk)) 648254401Scy np->in_snip6 = np->in_nsrcip6; 649254401Scy } else { 650254401Scy i6addr_t a1, a2; 651254401Scy 652254401Scy a1 = np->in_snip6; 653254401Scy IP6_INC(&a1); 654254401Scy IP6_AND(&a1, &np->in_nsrcmsk6, &a2); 655254401Scy 656254401Scy if (!IP6_ISONES(&np->in_nsrcmsk6) && 657254401Scy IP6_GT(&a2, &np->in_nsrcip6)) { 658254401Scy IP6_ADD(&np->in_nsrcip6, 1, &np->in_snip6); 659254401Scy } 660254401Scy } 661254401Scy 662254401Scy if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) 663254401Scy port = sport; 664254401Scy 665254401Scy /* 666254401Scy * Here we do a lookup of the connection as seen from 667254401Scy * the outside. If an IP# pair already exists, try 668254401Scy * again. So if you have A->B becomes C->B, you can 669254401Scy * also have D->E become C->E but not D->B causing 670254401Scy * another C->B. Also take protocol and ports into 671254401Scy * account when determining whether a pre-existing 672254401Scy * NAT setup will cause an external conflict where 673254401Scy * this is appropriate. 674254401Scy */ 675254401Scy sp = fin->fin_data[0]; 676254401Scy dp = fin->fin_data[1]; 677254401Scy fin->fin_data[0] = fin->fin_data[1]; 678254401Scy fin->fin_data[1] = ntohs(port); 679254401Scy natl = ipf_nat6_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 680254401Scy (u_int)fin->fin_p, &fin->fin_dst6.in6, 681254401Scy &in.in6); 682254401Scy fin->fin_data[0] = sp; 683254401Scy fin->fin_data[1] = dp; 684254401Scy 685254401Scy /* 686254401Scy * Has the search wrapped around and come back to the 687254401Scy * start ? 688254401Scy */ 689254401Scy if ((natl != NULL) && 690254401Scy (np->in_spnext != 0) && (st_port == np->in_spnext) && 691254401Scy (!IP6_ISZERO(&np->in_snip6) && 692254401Scy IP6_EQ(&st_ip, &np->in_snip6))) { 693254401Scy NBUMPSIDE6D(1, ns_wrap); 694254401Scy return -1; 695254401Scy } 696254401Scy l++; 697254401Scy } while (natl != NULL); 698254401Scy 699254401Scy /* Setup the NAT table */ 700254401Scy nat->nat_osrc6 = fin->fin_src6; 701254401Scy nat->nat_nsrc6 = in; 702254401Scy nat->nat_odst6 = fin->fin_dst6; 703254401Scy nat->nat_ndst6 = fin->fin_dst6; 704254401Scy if (nat->nat_hm == NULL) 705254401Scy nat->nat_hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6, 706254401Scy &fin->fin_dst6, 707254401Scy &nat->nat_nsrc6, 0); 708254401Scy 709254401Scy if (flags & IPN_TCPUDP) { 710254401Scy nat->nat_osport = sport; 711254401Scy nat->nat_nsport = port; /* sport */ 712254401Scy nat->nat_odport = dport; 713254401Scy nat->nat_ndport = dport; 714254401Scy ((tcphdr_t *)fin->fin_dp)->th_sport = port; 715254401Scy } else if (flags & IPN_ICMPQUERY) { 716254401Scy nat->nat_oicmpid = fin->fin_data[1]; 717254401Scy ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = port; 718254401Scy nat->nat_nicmpid = port; 719254401Scy } 720254401Scy return 0; 721254401Scy} 722254401Scy 723254401Scy 724254401Scy/* ------------------------------------------------------------------------ */ 725254401Scy/* Function: ipf_nat6_newrdr */ 726254401Scy/* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ 727254401Scy/* allow rule to be moved if IPN_ROUNDR is set. */ 728254401Scy/* Parameters: fin(I) - pointer to packet information */ 729254401Scy/* nat(I) - pointer to NAT entry */ 730254401Scy/* ni(I) - pointer to structure with misc. information needed */ 731254401Scy/* to create new NAT entry. */ 732254401Scy/* */ 733254401Scy/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 734254401Scy/* to the new IP address for the translation. */ 735254401Scy/* ------------------------------------------------------------------------ */ 736254401Scyint 737254401Scyipf_nat6_newrdr(fin, nat, ni) 738254401Scy fr_info_t *fin; 739254401Scy nat_t *nat; 740254401Scy natinfo_t *ni; 741254401Scy{ 742254401Scy ipf_main_softc_t *softc = fin->fin_main_soft; 743254401Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 744254401Scy u_short nport, dport, sport; 745254401Scy u_short sp, dp; 746254401Scy hostmap_t *hm; 747254401Scy u_32_t flags; 748254401Scy i6addr_t in; 749254401Scy ipnat_t *np; 750254401Scy nat_t *natl; 751254401Scy int move; 752254401Scy 753254401Scy move = 1; 754254401Scy hm = NULL; 755254401Scy in.i6[0] = 0; 756254401Scy in.i6[1] = 0; 757254401Scy in.i6[2] = 0; 758254401Scy in.i6[3] = 0; 759254401Scy np = ni->nai_np; 760254401Scy flags = nat->nat_flags; 761254401Scy 762254401Scy if (flags & IPN_ICMPQUERY) { 763254401Scy dport = fin->fin_data[1]; 764254401Scy sport = 0; 765254401Scy } else { 766254401Scy sport = htons(fin->fin_data[0]); 767254401Scy dport = htons(fin->fin_data[1]); 768254401Scy } 769254401Scy 770254401Scy /* TRACE sport, dport */ 771254401Scy 772254401Scy 773254401Scy /* 774254401Scy * If the matching rule has IPN_STICKY set, then we want to have the 775254401Scy * same rule kick in as before. Why would this happen? If you have 776254401Scy * a collection of rdr rules with "round-robin sticky", the current 777254401Scy * packet might match a different one to the previous connection but 778254401Scy * we want the same destination to be used. 779254401Scy */ 780254401Scy if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) && 781254401Scy ((np->in_flags & IPN_STICKY) != 0)) { 782254401Scy hm = ipf_nat6_hostmap(softn, NULL, &fin->fin_src6, 783254401Scy &fin->fin_dst6, &in, (u_32_t)dport); 784254401Scy if (hm != NULL) { 785254401Scy in = hm->hm_ndstip6; 786254401Scy np = hm->hm_ipnat; 787254401Scy ni->nai_np = np; 788254401Scy move = 0; 789254401Scy } 790254401Scy } 791254401Scy 792254401Scy /* 793254401Scy * Otherwise, it's an inbound packet. Most likely, we don't 794254401Scy * want to rewrite source ports and source addresses. Instead, 795254401Scy * we want to rewrite to a fixed internal address and fixed 796254401Scy * internal port. 797254401Scy */ 798254401Scy if (np->in_flags & IPN_SPLIT) { 799254401Scy in = np->in_dnip6; 800254401Scy 801254401Scy if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { 802254401Scy hm = ipf_nat6_hostmap(softn, NULL, &fin->fin_src6, 803254401Scy &fin->fin_dst6, &in, 804254401Scy (u_32_t)dport); 805254401Scy if (hm != NULL) { 806254401Scy in = hm->hm_ndstip6; 807254401Scy move = 0; 808254401Scy } 809254401Scy } 810254401Scy 811254401Scy if (hm == NULL || hm->hm_ref == 1) { 812254401Scy if (IP6_EQ(&np->in_ndstip6, &in)) { 813254401Scy np->in_dnip6 = np->in_ndstmsk6; 814254401Scy move = 0; 815254401Scy } else { 816254401Scy np->in_dnip6 = np->in_ndstip6; 817254401Scy } 818254401Scy } 819254401Scy 820254401Scy } else if (IP6_ISZERO(&np->in_ndstaddr) && 821254401Scy IP6_ISONES(&np->in_ndstmsk)) { 822254401Scy /* 823254401Scy * 0/32 - use the interface's IP address. 824254401Scy */ 825254401Scy if (ipf_ifpaddr(softc, 6, FRI_NORMAL, fin->fin_ifp, 826254401Scy &in, NULL) == -1) { 827254401Scy NBUMPSIDE6DX(0, ns_new_ifpaddr, ns_new_ifpaddr_2); 828254401Scy return -1; 829254401Scy } 830254401Scy 831254401Scy } else if (IP6_ISZERO(&np->in_ndstip6) && 832254401Scy IP6_ISZERO(&np->in_ndstmsk6)) { 833254401Scy /* 834254401Scy * 0/0 - use the original destination address/port. 835254401Scy */ 836254401Scy in = fin->fin_dst6; 837254401Scy 838254401Scy } else if (np->in_redir == NAT_BIMAP && 839254401Scy IP6_EQ(&np->in_ndstmsk6, &np->in_odstmsk6)) { 840254401Scy i6addr_t temp; 841254401Scy /* 842254401Scy * map the address block in a 1:1 fashion 843254401Scy */ 844254401Scy temp.i6[0] = fin->fin_dst6.i6[0] & ~np->in_osrcmsk6.i6[0]; 845254401Scy temp.i6[1] = fin->fin_dst6.i6[1] & ~np->in_osrcmsk6.i6[1]; 846254401Scy temp.i6[2] = fin->fin_dst6.i6[2] & ~np->in_osrcmsk6.i6[0]; 847254401Scy temp.i6[3] = fin->fin_dst6.i6[3] & ~np->in_osrcmsk6.i6[3]; 848254401Scy in = np->in_ndstip6; 849254401Scy IP6_MERGE(&in, &temp, &np->in_ndstmsk6); 850254401Scy } else { 851254401Scy in = np->in_ndstip6; 852254401Scy } 853254401Scy 854254401Scy if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) 855254401Scy nport = dport; 856254401Scy else { 857254401Scy /* 858254401Scy * Whilst not optimized for the case where 859254401Scy * pmin == pmax, the gain is not significant. 860254401Scy */ 861254401Scy if (((np->in_flags & IPN_FIXEDDPORT) == 0) && 862254401Scy (np->in_odport != np->in_dtop)) { 863254401Scy nport = ntohs(dport) - np->in_odport + np->in_dpmax; 864254401Scy nport = htons(nport); 865254401Scy } else { 866254401Scy nport = htons(np->in_dpnext); 867254401Scy np->in_dpnext++; 868254401Scy if (np->in_dpnext > np->in_dpmax) 869254401Scy np->in_dpnext = np->in_dpmin; 870254401Scy } 871254401Scy } 872254401Scy 873254401Scy /* 874254401Scy * When the redirect-to address is set to 0.0.0.0, just 875254401Scy * assume a blank `forwarding' of the packet. We don't 876254401Scy * setup any translation for this either. 877254401Scy */ 878254401Scy if (IP6_ISZERO(&in)) { 879254401Scy if (nport == dport) { 880254401Scy NBUMPSIDE6D(0, ns_xlate_null); 881254401Scy return -1; 882254401Scy } 883254401Scy in = fin->fin_dst6; 884254401Scy } 885254401Scy 886254401Scy /* 887254401Scy * Check to see if this redirect mapping already exists and if 888254401Scy * it does, return "failure" (allowing it to be created will just 889254401Scy * cause one or both of these "connections" to stop working.) 890254401Scy */ 891254401Scy sp = fin->fin_data[0]; 892254401Scy dp = fin->fin_data[1]; 893254401Scy fin->fin_data[1] = fin->fin_data[0]; 894254401Scy fin->fin_data[0] = ntohs(nport); 895254401Scy natl = ipf_nat6_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 896254401Scy (u_int)fin->fin_p, &in.in6, 897254401Scy &fin->fin_src6.in6); 898254401Scy fin->fin_data[0] = sp; 899254401Scy fin->fin_data[1] = dp; 900254401Scy if (natl != NULL) { 901254401Scy NBUMPSIDE6D(0, ns_xlate_exists); 902254401Scy return -1; 903254401Scy } 904254401Scy 905254401Scy nat->nat_ndst6 = in; 906254401Scy nat->nat_odst6 = fin->fin_dst6; 907254401Scy nat->nat_nsrc6 = fin->fin_src6; 908254401Scy nat->nat_osrc6 = fin->fin_src6; 909254401Scy if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0)) 910254401Scy nat->nat_hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6, 911254401Scy &fin->fin_dst6, &in, 912254401Scy (u_32_t)dport); 913254401Scy 914254401Scy if (flags & IPN_TCPUDP) { 915254401Scy nat->nat_odport = dport; 916254401Scy nat->nat_ndport = nport; 917254401Scy nat->nat_osport = sport; 918254401Scy nat->nat_nsport = sport; 919254401Scy ((tcphdr_t *)fin->fin_dp)->th_dport = nport; 920254401Scy } else if (flags & IPN_ICMPQUERY) { 921254401Scy nat->nat_oicmpid = fin->fin_data[1]; 922254401Scy ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = nport; 923254401Scy nat->nat_nicmpid = nport; 924254401Scy } 925254401Scy 926254401Scy return move; 927254401Scy} 928254401Scy 929254401Scy/* ------------------------------------------------------------------------ */ 930254401Scy/* Function: ipf_nat6_add */ 931254401Scy/* Returns: nat6_t* - NULL == failure to create new NAT structure, */ 932254401Scy/* else pointer to new NAT structure */ 933254401Scy/* Parameters: fin(I) - pointer to packet information */ 934254401Scy/* np(I) - pointer to NAT rule */ 935254401Scy/* natsave(I) - pointer to where to store NAT struct pointer */ 936254401Scy/* flags(I) - flags describing the current packet */ 937254401Scy/* direction(I) - direction of packet (in/out) */ 938254401Scy/* Write Lock: ipf_nat */ 939254401Scy/* */ 940254401Scy/* Attempts to create a new NAT entry. Does not actually change the packet */ 941254401Scy/* in any way. */ 942254401Scy/* */ 943254401Scy/* This fucntion is in three main parts: (1) deal with creating a new NAT */ 944254401Scy/* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */ 945254401Scy/* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */ 946254401Scy/* and (3) building that structure and putting it into the NAT table(s). */ 947254401Scy/* */ 948254401Scy/* NOTE: natsave should NOT be used top point back to an ipstate_t struct */ 949254401Scy/* as it can result in memory being corrupted. */ 950254401Scy/* ------------------------------------------------------------------------ */ 951254401Scynat_t * 952254401Scyipf_nat6_add(fin, np, natsave, flags, direction) 953254401Scy fr_info_t *fin; 954254401Scy ipnat_t *np; 955254401Scy nat_t **natsave; 956254401Scy u_int flags; 957254401Scy int direction; 958254401Scy{ 959254401Scy ipf_main_softc_t *softc = fin->fin_main_soft; 960254401Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 961254401Scy hostmap_t *hm = NULL; 962254401Scy nat_t *nat, *natl; 963254401Scy natstat_t *nsp; 964254401Scy u_int nflags; 965254401Scy natinfo_t ni; 966254401Scy int move; 967344833Scy#if SOLARIS && defined(_KERNEL) && defined(ICK_M_CTL_MAGIC) 968254401Scy qpktinfo_t *qpi = fin->fin_qpi; 969254401Scy#endif 970254401Scy 971254401Scy nsp = &softn->ipf_nat_stats; 972254401Scy 973254401Scy if ((nsp->ns_active * 100 / softn->ipf_nat_table_max) > 974254401Scy softn->ipf_nat_table_wm_high) { 975254401Scy softn->ipf_nat_doflush = 1; 976254401Scy } 977254401Scy 978254401Scy if (nsp->ns_active >= softn->ipf_nat_table_max) { 979254401Scy NBUMPSIDE6(fin->fin_out, ns_table_max); 980254401Scy return NULL; 981254401Scy } 982254401Scy 983254401Scy move = 1; 984254401Scy nflags = np->in_flags & flags; 985254401Scy nflags &= NAT_FROMRULE; 986254401Scy 987254401Scy ni.nai_np = np; 988254401Scy ni.nai_dport = 0; 989254401Scy ni.nai_sport = 0; 990254401Scy 991254401Scy /* Give me a new nat */ 992254401Scy KMALLOC(nat, nat_t *); 993254401Scy if (nat == NULL) { 994254401Scy NBUMPSIDE6(fin->fin_out, ns_memfail); 995254401Scy /* 996254401Scy * Try to automatically tune the max # of entries in the 997254401Scy * table allowed to be less than what will cause kmem_alloc() 998254401Scy * to fail and try to eliminate panics due to out of memory 999254401Scy * conditions arising. 1000254401Scy */ 1001254401Scy if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) && 1002254401Scy (nsp->ns_active > 100)) { 1003254401Scy softn->ipf_nat_table_max = nsp->ns_active - 100; 1004254401Scy printf("table_max reduced to %d\n", 1005254401Scy softn->ipf_nat_table_max); 1006254401Scy } 1007254401Scy return NULL; 1008254401Scy } 1009254401Scy 1010254401Scy if (flags & IPN_ICMPQUERY) { 1011254401Scy /* 1012254401Scy * In the ICMP query NAT code, we translate the ICMP id fields 1013254401Scy * to make them unique. This is indepedent of the ICMP type 1014254401Scy * (e.g. in the unlikely event that a host sends an echo and 1015254401Scy * an tstamp request with the same id, both packets will have 1016254401Scy * their ip address/id field changed in the same way). 1017254401Scy */ 1018254401Scy /* The icmp6_id field is used by the sender to identify the 1019254401Scy * process making the icmp request. (the receiver justs 1020254401Scy * copies it back in its response). So, it closely matches 1021254401Scy * the concept of source port. We overlay sport, so we can 1022254401Scy * maximally reuse the existing code. 1023254401Scy */ 1024254401Scy ni.nai_sport = fin->fin_data[1]; 1025254401Scy ni.nai_dport = 0; 1026254401Scy } 1027254401Scy 1028254401Scy bzero((char *)nat, sizeof(*nat)); 1029254401Scy nat->nat_flags = flags; 1030254401Scy nat->nat_redir = np->in_redir; 1031254401Scy nat->nat_dir = direction; 1032254401Scy nat->nat_pr[0] = fin->fin_p; 1033254401Scy nat->nat_pr[1] = fin->fin_p; 1034254401Scy 1035254401Scy /* 1036254401Scy * Search the current table for a match and create a new mapping 1037254401Scy * if there is none found. 1038254401Scy */ 1039254401Scy if (np->in_redir & NAT_DIVERTUDP) { 1040254401Scy move = ipf_nat6_newdivert(fin, nat, &ni); 1041254401Scy 1042254401Scy } else if (np->in_redir & NAT_REWRITE) { 1043254401Scy move = ipf_nat6_newrewrite(fin, nat, &ni); 1044254401Scy 1045254401Scy } else if (direction == NAT_OUTBOUND) { 1046254401Scy /* 1047254401Scy * We can now arrange to call this for the same connection 1048254401Scy * because ipf_nat6_new doesn't protect the code path into 1049254401Scy * this function. 1050254401Scy */ 1051254401Scy natl = ipf_nat6_outlookup(fin, nflags, (u_int)fin->fin_p, 1052254401Scy &fin->fin_src6.in6, 1053254401Scy &fin->fin_dst6.in6); 1054254401Scy if (natl != NULL) { 1055254401Scy KFREE(nat); 1056254401Scy nat = natl; 1057254401Scy goto done; 1058254401Scy } 1059254401Scy 1060254401Scy move = ipf_nat6_newmap(fin, nat, &ni); 1061254401Scy } else { 1062254401Scy /* 1063254401Scy * NAT_INBOUND is used for redirects rules 1064254401Scy */ 1065254401Scy natl = ipf_nat6_inlookup(fin, nflags, (u_int)fin->fin_p, 1066254401Scy &fin->fin_src6.in6, 1067254401Scy &fin->fin_dst6.in6); 1068254401Scy if (natl != NULL) { 1069254401Scy KFREE(nat); 1070254401Scy nat = natl; 1071254401Scy goto done; 1072254401Scy } 1073254401Scy 1074254401Scy move = ipf_nat6_newrdr(fin, nat, &ni); 1075254401Scy } 1076254401Scy if (move == -1) 1077254401Scy goto badnat; 1078254401Scy 1079254401Scy np = ni.nai_np; 1080254401Scy 1081254401Scy nat->nat_mssclamp = np->in_mssclamp; 1082254401Scy nat->nat_me = natsave; 1083254401Scy nat->nat_fr = fin->fin_fr; 1084254401Scy nat->nat_rev = fin->fin_rev; 1085254401Scy nat->nat_ptr = np; 1086254401Scy nat->nat_dlocal = np->in_dlocal; 1087254401Scy 1088254401Scy if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) { 1089254401Scy if (ipf_proxy_new(fin, nat) == -1) { 1090254401Scy NBUMPSIDE6D(fin->fin_out, ns_appr_fail); 1091254401Scy goto badnat; 1092254401Scy } 1093254401Scy } 1094254401Scy 1095254401Scy nat->nat_ifps[0] = np->in_ifps[0]; 1096254401Scy if (np->in_ifps[0] != NULL) { 1097254401Scy COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]); 1098254401Scy } 1099254401Scy 1100254401Scy nat->nat_ifps[1] = np->in_ifps[1]; 1101254401Scy if (np->in_ifps[1] != NULL) { 1102254401Scy COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]); 1103254401Scy } 1104254401Scy 1105254401Scy if (ipf_nat6_finalise(fin, nat) == -1) { 1106254401Scy goto badnat; 1107254401Scy } 1108254401Scy 1109254401Scy np->in_use++; 1110254401Scy 1111254401Scy if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { 1112254401Scy if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) { 1113254401Scy ipf_nat6_delrdr(softn, np); 1114254401Scy ipf_nat6_addrdr(softn, np); 1115254401Scy } else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) { 1116254401Scy ipf_nat6_delmap(softn, np); 1117254401Scy ipf_nat6_addmap(softn, np); 1118254401Scy } 1119254401Scy } 1120254401Scy 1121254401Scy if (flags & SI_WILDP) 1122254401Scy nsp->ns_wilds++; 1123254401Scy softn->ipf_nat_stats.ns_proto[nat->nat_pr[0]]++; 1124254401Scy 1125254401Scy goto done; 1126254401Scybadnat: 1127254401Scy NBUMPSIDE6(fin->fin_out, ns_badnatnew); 1128254401Scy if ((hm = nat->nat_hm) != NULL) 1129254401Scy ipf_nat_hostmapdel(softc, &hm); 1130254401Scy KFREE(nat); 1131254401Scy nat = NULL; 1132254401Scydone: 1133254401Scy if (nat != NULL && np != NULL) 1134254401Scy np->in_hits++; 1135254401Scy if (natsave != NULL) 1136254401Scy *natsave = nat; 1137254401Scy return nat; 1138254401Scy} 1139254401Scy 1140254401Scy 1141254401Scy/* ------------------------------------------------------------------------ */ 1142254401Scy/* Function: ipf_nat6_finalise */ 1143254401Scy/* Returns: int - 0 == sucess, -1 == failure */ 1144254401Scy/* Parameters: fin(I) - pointer to packet information */ 1145254401Scy/* nat(I) - pointer to NAT entry */ 1146254401Scy/* Write Lock: ipf_nat */ 1147254401Scy/* */ 1148254401Scy/* This is the tail end of constructing a new NAT entry and is the same */ 1149254401Scy/* for both IPv4 and IPv6. */ 1150254401Scy/* ------------------------------------------------------------------------ */ 1151254401Scy/*ARGSUSED*/ 1152254401Scyint 1153254401Scyipf_nat6_finalise(fin, nat) 1154254401Scy fr_info_t *fin; 1155254401Scy nat_t *nat; 1156254401Scy{ 1157254401Scy ipf_main_softc_t *softc = fin->fin_main_soft; 1158254401Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 1159254401Scy u_32_t sum1, sum2, sumd; 1160254401Scy frentry_t *fr; 1161254401Scy u_32_t flags; 1162254401Scy 1163254401Scy flags = nat->nat_flags; 1164254401Scy 1165254401Scy switch (fin->fin_p) 1166254401Scy { 1167254401Scy case IPPROTO_ICMPV6 : 1168254401Scy sum1 = LONG_SUM6(&nat->nat_osrc6); 1169254401Scy sum1 += ntohs(nat->nat_oicmpid); 1170254401Scy sum2 = LONG_SUM6(&nat->nat_nsrc6); 1171254401Scy sum2 += ntohs(nat->nat_nicmpid); 1172254401Scy CALC_SUMD(sum1, sum2, sumd); 1173254401Scy nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 1174254401Scy 1175254401Scy sum1 = LONG_SUM6(&nat->nat_odst6); 1176254401Scy sum2 = LONG_SUM6(&nat->nat_ndst6); 1177254401Scy CALC_SUMD(sum1, sum2, sumd); 1178254401Scy nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); 1179254401Scy break; 1180254401Scy 1181254401Scy case IPPROTO_TCP : 1182254401Scy case IPPROTO_UDP : 1183254401Scy sum1 = LONG_SUM6(&nat->nat_osrc6); 1184254401Scy sum1 += ntohs(nat->nat_osport); 1185254401Scy sum2 = LONG_SUM6(&nat->nat_nsrc6); 1186254401Scy sum2 += ntohs(nat->nat_nsport); 1187254401Scy CALC_SUMD(sum1, sum2, sumd); 1188254401Scy nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 1189254401Scy 1190254401Scy sum1 = LONG_SUM6(&nat->nat_odst6); 1191254401Scy sum1 += ntohs(nat->nat_odport); 1192254401Scy sum2 = LONG_SUM6(&nat->nat_ndst6); 1193254401Scy sum2 += ntohs(nat->nat_ndport); 1194254401Scy CALC_SUMD(sum1, sum2, sumd); 1195254401Scy nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); 1196254401Scy break; 1197254401Scy 1198254401Scy default : 1199254401Scy sum1 = LONG_SUM6(&nat->nat_osrc6); 1200254401Scy sum2 = LONG_SUM6(&nat->nat_nsrc6); 1201254401Scy CALC_SUMD(sum1, sum2, sumd); 1202254401Scy nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 1203254401Scy 1204254401Scy sum1 = LONG_SUM6(&nat->nat_odst6); 1205254401Scy sum2 = LONG_SUM6(&nat->nat_ndst6); 1206254401Scy CALC_SUMD(sum1, sum2, sumd); 1207254401Scy nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); 1208254401Scy break; 1209254401Scy } 1210254401Scy 1211254401Scy /* 1212254401Scy * Compute the partial checksum, just in case. 1213254401Scy * This is only ever placed into outbound packets so care needs 1214254401Scy * to be taken over which pair of addresses are used. 1215254401Scy */ 1216254401Scy if (nat->nat_dir == NAT_OUTBOUND) { 1217254401Scy sum1 = LONG_SUM6(&nat->nat_nsrc6); 1218254401Scy sum1 += LONG_SUM6(&nat->nat_ndst6); 1219254401Scy } else { 1220254401Scy sum1 = LONG_SUM6(&nat->nat_osrc6); 1221254401Scy sum1 += LONG_SUM6(&nat->nat_odst6); 1222254401Scy } 1223254401Scy sum1 += nat->nat_pr[1]; 1224254401Scy nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16); 1225254401Scy 1226254401Scy if ((nat->nat_flags & SI_CLONE) == 0) 1227254401Scy nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat); 1228254401Scy 1229254401Scy if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { 1230254401Scy nat->nat_mtu[0] = GETIFMTU_6(nat->nat_ifps[0]); 1231254401Scy } 1232254401Scy 1233254401Scy if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { 1234254401Scy nat->nat_mtu[1] = GETIFMTU_6(nat->nat_ifps[1]); 1235254401Scy } 1236254401Scy 1237254401Scy nat->nat_v[0] = 6; 1238254401Scy nat->nat_v[1] = 6; 1239254401Scy 1240254401Scy if (ipf_nat6_insert(softc, softn, nat) == 0) { 1241254401Scy if (softn->ipf_nat_logging) 1242254401Scy ipf_nat_log(softc, softn, nat, NL_NEW); 1243254401Scy fr = nat->nat_fr; 1244254401Scy if (fr != NULL) { 1245254401Scy MUTEX_ENTER(&fr->fr_lock); 1246254401Scy fr->fr_ref++; 1247254401Scy MUTEX_EXIT(&fr->fr_lock); 1248254401Scy } 1249254401Scy return 0; 1250254401Scy } 1251254401Scy 1252254401Scy NBUMPSIDE6D(fin->fin_out, ns_unfinalised); 1253254401Scy /* 1254254401Scy * nat6_insert failed, so cleanup time... 1255254401Scy */ 1256254401Scy if (nat->nat_sync != NULL) 1257254401Scy ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); 1258254401Scy return -1; 1259254401Scy} 1260254401Scy 1261254401Scy 1262254401Scy/* ------------------------------------------------------------------------ */ 1263254401Scy/* Function: ipf_nat6_insert */ 1264254401Scy/* Returns: int - 0 == sucess, -1 == failure */ 1265254401Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1266254401Scy/* softn(I) - pointer to NAT context structure */ 1267254401Scy/* nat(I) - pointer to NAT structure */ 1268254401Scy/* Write Lock: ipf_nat */ 1269254401Scy/* */ 1270254401Scy/* Insert a NAT entry into the hash tables for searching and add it to the */ 1271254401Scy/* list of active NAT entries. Adjust global counters when complete. */ 1272254401Scy/* ------------------------------------------------------------------------ */ 1273254401Scystatic int 1274254401Scyipf_nat6_insert(softc, softn, nat) 1275254401Scy ipf_main_softc_t *softc; 1276254401Scy ipf_nat_softc_t *softn; 1277254401Scy nat_t *nat; 1278254401Scy{ 1279254401Scy u_int hv1, hv2; 1280254401Scy u_32_t sp, dp; 1281254401Scy ipnat_t *in; 1282254401Scy 1283254401Scy /* 1284254401Scy * Try and return an error as early as possible, so calculate the hash 1285254401Scy * entry numbers first and then proceed. 1286254401Scy */ 1287254401Scy if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { 1288254401Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 1289254401Scy sp = nat->nat_osport; 1290254401Scy dp = nat->nat_odport; 1291254401Scy } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { 1292254401Scy sp = 0; 1293254401Scy dp = nat->nat_oicmpid; 1294254401Scy } else { 1295254401Scy sp = 0; 1296254401Scy dp = 0; 1297254401Scy } 1298254401Scy hv1 = NAT_HASH_FN6(&nat->nat_osrc6, sp, 0xffffffff); 1299254401Scy hv1 = NAT_HASH_FN6(&nat->nat_odst6, hv1 + dp, 1300254401Scy softn->ipf_nat_table_sz); 1301254401Scy 1302254401Scy /* 1303254401Scy * TRACE nat6_osrc6, nat6_osport, nat6_odst6, 1304254401Scy * nat6_odport, hv1 1305254401Scy */ 1306254401Scy 1307254401Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 1308254401Scy sp = nat->nat_nsport; 1309254401Scy dp = nat->nat_ndport; 1310254401Scy } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { 1311254401Scy sp = 0; 1312254401Scy dp = nat->nat_nicmpid; 1313254401Scy } else { 1314254401Scy sp = 0; 1315254401Scy dp = 0; 1316254401Scy } 1317254401Scy hv2 = NAT_HASH_FN6(&nat->nat_nsrc6, sp, 0xffffffff); 1318254401Scy hv2 = NAT_HASH_FN6(&nat->nat_ndst6, hv2 + dp, 1319254401Scy softn->ipf_nat_table_sz); 1320254401Scy /* 1321254401Scy * TRACE nat6_nsrcaddr, nat6_nsport, nat6_ndstaddr, 1322254401Scy * nat6_ndport, hv1 1323254401Scy */ 1324254401Scy } else { 1325254401Scy hv1 = NAT_HASH_FN6(&nat->nat_osrc6, 0, 0xffffffff); 1326254401Scy hv1 = NAT_HASH_FN6(&nat->nat_odst6, hv1, 1327254401Scy softn->ipf_nat_table_sz); 1328254401Scy /* TRACE nat6_osrcip6, nat6_odstip6, hv1 */ 1329254401Scy 1330254401Scy hv2 = NAT_HASH_FN6(&nat->nat_nsrc6, 0, 0xffffffff); 1331254401Scy hv2 = NAT_HASH_FN6(&nat->nat_ndst6, hv2, 1332254401Scy softn->ipf_nat_table_sz); 1333254401Scy /* TRACE nat6_nsrcip6, nat6_ndstip6, hv2 */ 1334254401Scy } 1335254401Scy 1336254401Scy nat->nat_hv[0] = hv1; 1337254401Scy nat->nat_hv[1] = hv2; 1338254401Scy 1339254401Scy MUTEX_INIT(&nat->nat_lock, "nat entry lock"); 1340254401Scy 1341254401Scy in = nat->nat_ptr; 1342254401Scy nat->nat_ref = nat->nat_me ? 2 : 1; 1343254401Scy 1344254401Scy nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; 1345254401Scy nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0], 1346254401Scy nat->nat_v[0]); 1347254401Scy 1348254401Scy if (nat->nat_ifnames[1][0] != '\0') { 1349254401Scy nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 1350254401Scy nat->nat_ifps[1] = ipf_resolvenic(softc, nat->nat_ifnames[1], 1351254401Scy nat->nat_v[1]); 1352254401Scy } else if (in->in_ifnames[1] != -1) { 1353254401Scy char *name; 1354254401Scy 1355254401Scy name = in->in_names + in->in_ifnames[1]; 1356254401Scy if (name[1] != '\0' && name[0] != '-' && name[0] != '*') { 1357254401Scy (void) strncpy(nat->nat_ifnames[1], 1358254401Scy nat->nat_ifnames[0], LIFNAMSIZ); 1359254401Scy nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 1360254401Scy nat->nat_ifps[1] = nat->nat_ifps[0]; 1361254401Scy } 1362254401Scy } 1363254401Scy if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { 1364254401Scy nat->nat_mtu[0] = GETIFMTU_6(nat->nat_ifps[0]); 1365254401Scy } 1366254401Scy if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { 1367254401Scy nat->nat_mtu[1] = GETIFMTU_6(nat->nat_ifps[1]); 1368254401Scy } 1369254401Scy 1370254401Scy return ipf_nat_hashtab_add(softc, softn, nat); 1371254401Scy} 1372254401Scy 1373254401Scy 1374254401Scy/* ------------------------------------------------------------------------ */ 1375254401Scy/* Function: ipf_nat6_icmperrorlookup */ 1376254401Scy/* Returns: nat6_t* - point to matching NAT structure */ 1377254401Scy/* Parameters: fin(I) - pointer to packet information */ 1378254401Scy/* dir(I) - direction of packet (in/out) */ 1379254401Scy/* */ 1380254401Scy/* Check if the ICMP error message is related to an existing TCP, UDP or */ 1381254401Scy/* ICMP query nat entry. It is assumed that the packet is already of the */ 1382254401Scy/* the required length. */ 1383254401Scy/* ------------------------------------------------------------------------ */ 1384254401Scynat_t * 1385254401Scyipf_nat6_icmperrorlookup(fin, dir) 1386254401Scy fr_info_t *fin; 1387254401Scy int dir; 1388254401Scy{ 1389254401Scy ipf_main_softc_t *softc = fin->fin_main_soft; 1390254401Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 1391254401Scy struct icmp6_hdr *icmp6, *orgicmp; 1392254401Scy int flags = 0, type, minlen; 1393254401Scy nat_stat_side_t *nside; 1394254401Scy tcphdr_t *tcp = NULL; 1395254401Scy u_short data[2]; 1396254401Scy ip6_t *oip6; 1397254401Scy nat_t *nat; 1398254401Scy u_int p; 1399254401Scy 1400254401Scy minlen = 40; 1401254401Scy icmp6 = fin->fin_dp; 1402254401Scy type = icmp6->icmp6_type; 1403254401Scy nside = &softn->ipf_nat_stats.ns_side6[fin->fin_out]; 1404254401Scy /* 1405254401Scy * Does it at least have the return (basic) IP header ? 1406254401Scy * Only a basic IP header (no options) should be with an ICMP error 1407254401Scy * header. Also, if it's not an error type, then return. 1408254401Scy */ 1409254401Scy if (!(fin->fin_flx & FI_ICMPERR)) { 1410254401Scy ATOMIC_INCL(nside->ns_icmp_basic); 1411254401Scy return NULL; 1412254401Scy } 1413254401Scy 1414254401Scy /* 1415254401Scy * Check packet size 1416254401Scy */ 1417254401Scy if (fin->fin_plen < ICMP6ERR_IPICMPHLEN) { 1418254401Scy ATOMIC_INCL(nside->ns_icmp_size); 1419254401Scy return NULL; 1420254401Scy } 1421254401Scy oip6 = (ip6_t *)((char *)fin->fin_dp + 8); 1422254401Scy 1423254401Scy /* 1424254401Scy * Is the buffer big enough for all of it ? It's the size of the IP 1425254401Scy * header claimed in the encapsulated part which is of concern. It 1426254401Scy * may be too big to be in this buffer but not so big that it's 1427254401Scy * outside the ICMP packet, leading to TCP deref's causing problems. 1428254401Scy * This is possible because we don't know how big oip_hl is when we 1429254401Scy * do the pullup early in ipf_check() and thus can't gaurantee it is 1430254401Scy * all here now. 1431254401Scy */ 1432255332Scy#ifdef _KERNEL 1433254401Scy { 1434254401Scy mb_t *m; 1435254401Scy 1436254401Scy m = fin->fin_m; 1437369272Scy# if SOLARIS 1438254401Scy if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN > 1439254401Scy (char *)m->b_wptr) { 1440254401Scy ATOMIC_INCL(nside->ns_icmp_mbuf); 1441254401Scy return NULL; 1442254401Scy } 1443254401Scy# else 1444254401Scy if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN > 1445254401Scy (char *)fin->fin_ip + M_LEN(m)) { 1446254401Scy ATOMIC_INCL(nside->ns_icmp_mbuf); 1447254401Scy return NULL; 1448254401Scy } 1449254401Scy# endif 1450254401Scy } 1451254401Scy#endif 1452254401Scy 1453254401Scy if (IP6_NEQ(&fin->fin_dst6, &oip6->ip6_src)) { 1454254401Scy ATOMIC_INCL(nside->ns_icmp_address); 1455254401Scy return NULL; 1456254401Scy } 1457254401Scy 1458254401Scy p = oip6->ip6_nxt; 1459254401Scy if (p == IPPROTO_TCP) 1460254401Scy flags = IPN_TCP; 1461254401Scy else if (p == IPPROTO_UDP) 1462254401Scy flags = IPN_UDP; 1463254401Scy else if (p == IPPROTO_ICMPV6) { 1464254401Scy orgicmp = (struct icmp6_hdr *)(oip6 + 1); 1465254401Scy 1466254401Scy /* see if this is related to an ICMP query */ 1467254401Scy if (ipf_nat6_icmpquerytype(orgicmp->icmp6_type)) { 1468254401Scy data[0] = fin->fin_data[0]; 1469254401Scy data[1] = fin->fin_data[1]; 1470254401Scy fin->fin_data[0] = 0; 1471254401Scy fin->fin_data[1] = orgicmp->icmp6_id; 1472254401Scy 1473254401Scy flags = IPN_ICMPERR|IPN_ICMPQUERY; 1474254401Scy /* 1475254401Scy * NOTE : dir refers to the direction of the original 1476254401Scy * ip packet. By definition the icmp error 1477254401Scy * message flows in the opposite direction. 1478254401Scy */ 1479254401Scy if (dir == NAT_INBOUND) 1480254401Scy nat = ipf_nat6_inlookup(fin, flags, p, 1481254401Scy &oip6->ip6_dst, 1482254401Scy &oip6->ip6_src); 1483254401Scy else 1484254401Scy nat = ipf_nat6_outlookup(fin, flags, p, 1485254401Scy &oip6->ip6_dst, 1486254401Scy &oip6->ip6_src); 1487254401Scy fin->fin_data[0] = data[0]; 1488254401Scy fin->fin_data[1] = data[1]; 1489254401Scy return nat; 1490254401Scy } 1491254401Scy } 1492254401Scy 1493254401Scy if (flags & IPN_TCPUDP) { 1494254401Scy minlen += 8; /* + 64bits of data to get ports */ 1495254401Scy /* TRACE (fin,minlen) */ 1496254401Scy if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) { 1497254401Scy ATOMIC_INCL(nside->ns_icmp_short); 1498254401Scy return NULL; 1499254401Scy } 1500254401Scy 1501254401Scy data[0] = fin->fin_data[0]; 1502254401Scy data[1] = fin->fin_data[1]; 1503254401Scy tcp = (tcphdr_t *)(oip6 + 1); 1504254401Scy fin->fin_data[0] = ntohs(tcp->th_dport); 1505254401Scy fin->fin_data[1] = ntohs(tcp->th_sport); 1506254401Scy 1507254401Scy if (dir == NAT_INBOUND) { 1508254401Scy nat = ipf_nat6_inlookup(fin, flags, p, &oip6->ip6_dst, 1509254401Scy &oip6->ip6_src); 1510254401Scy } else { 1511254401Scy nat = ipf_nat6_outlookup(fin, flags, p, &oip6->ip6_dst, 1512254401Scy &oip6->ip6_src); 1513254401Scy } 1514254401Scy fin->fin_data[0] = data[0]; 1515254401Scy fin->fin_data[1] = data[1]; 1516254401Scy return nat; 1517254401Scy } 1518254401Scy if (dir == NAT_INBOUND) 1519254401Scy nat = ipf_nat6_inlookup(fin, 0, p, &oip6->ip6_dst, 1520254401Scy &oip6->ip6_src); 1521254401Scy else 1522254401Scy nat = ipf_nat6_outlookup(fin, 0, p, &oip6->ip6_dst, 1523254401Scy &oip6->ip6_src); 1524254401Scy 1525254401Scy return nat; 1526254401Scy} 1527254401Scy 1528254401Scy 1529254401Scy/* result = ip1 - ip2 */ 1530254401Scyu_32_t 1531254401Scyipf_nat6_ip6subtract(ip1, ip2) 1532254401Scy i6addr_t *ip1, *ip2; 1533254401Scy{ 1534254401Scy i6addr_t l1, l2, d; 1535254401Scy u_short *s1, *s2, *ds; 1536254401Scy u_32_t r; 1537254401Scy int i, neg; 1538254401Scy 1539254401Scy neg = 0; 1540254401Scy l1 = *ip1; 1541254401Scy l2 = *ip2; 1542254401Scy s1 = (u_short *)&l1; 1543254401Scy s2 = (u_short *)&l2; 1544254401Scy ds = (u_short *)&d; 1545254401Scy 1546254401Scy for (i = 7; i > 0; i--) { 1547254401Scy if (s1[i] > s2[i]) { 1548254401Scy ds[i] = s2[i] + 0x10000 - s1[i]; 1549254401Scy s2[i - 1] += 0x10000; 1550254401Scy } else { 1551254401Scy ds[i] = s2[i] - s1[i]; 1552254401Scy } 1553254401Scy } 1554254401Scy if (s2[0] > s1[0]) { 1555254401Scy ds[0] = s2[0] + 0x10000 - s1[0]; 1556254401Scy neg = 1; 1557254401Scy } else { 1558254401Scy ds[0] = s2[0] - s1[0]; 1559254401Scy } 1560254401Scy 1561254401Scy for (i = 0, r = 0; i < 8; i++) { 1562254401Scy r += ds[i]; 1563254401Scy } 1564254401Scy 1565254401Scy return r; 1566254401Scy} 1567254401Scy 1568254401Scy 1569254401Scy/* ------------------------------------------------------------------------ */ 1570254401Scy/* Function: ipf_nat6_icmperror */ 1571254401Scy/* Returns: nat6_t* - point to matching NAT structure */ 1572254401Scy/* Parameters: fin(I) - pointer to packet information */ 1573254401Scy/* nflags(I) - NAT flags for this packet */ 1574254401Scy/* dir(I) - direction of packet (in/out) */ 1575254401Scy/* */ 1576254401Scy/* Fix up an ICMP packet which is an error message for an existing NAT */ 1577254401Scy/* session. This will correct both packet header data and checksums. */ 1578254401Scy/* */ 1579254401Scy/* This should *ONLY* be used for incoming ICMP error packets to make sure */ 1580254401Scy/* a NAT'd ICMP packet gets correctly recognised. */ 1581254401Scy/* ------------------------------------------------------------------------ */ 1582254401Scynat_t * 1583254401Scyipf_nat6_icmperror(fin, nflags, dir) 1584254401Scy fr_info_t *fin; 1585254401Scy u_int *nflags; 1586254401Scy int dir; 1587254401Scy{ 1588254401Scy ipf_main_softc_t *softc = fin->fin_main_soft; 1589254401Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 1590254401Scy u_32_t sum1, sum2, sumd, sumd2; 1591254401Scy i6addr_t a1, a2, a3, a4; 1592254401Scy struct icmp6_hdr *icmp6; 1593254401Scy int flags, dlen, odst; 1594254401Scy u_short *csump; 1595254401Scy tcphdr_t *tcp; 1596254401Scy ip6_t *oip6; 1597254401Scy nat_t *nat; 1598254401Scy void *dp; 1599254401Scy 1600254401Scy if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { 1601254401Scy NBUMPSIDE6D(fin->fin_out, ns_icmp_short); 1602254401Scy return NULL; 1603254401Scy } 1604254401Scy 1605254401Scy /* 1606254401Scy * ipf_nat6_icmperrorlookup() will return NULL for `defective' packets. 1607254401Scy */ 1608254401Scy if ((fin->fin_v != 6) || !(nat = ipf_nat6_icmperrorlookup(fin, dir))) { 1609254401Scy NBUMPSIDE6D(fin->fin_out, ns_icmp_notfound); 1610254401Scy return NULL; 1611254401Scy } 1612254401Scy 1613254401Scy tcp = NULL; 1614254401Scy csump = NULL; 1615254401Scy flags = 0; 1616254401Scy sumd2 = 0; 1617254401Scy *nflags = IPN_ICMPERR; 1618254401Scy icmp6 = fin->fin_dp; 1619254401Scy oip6 = (ip6_t *)((u_char *)icmp6 + sizeof(*icmp6)); 1620254401Scy dp = (u_char *)oip6 + sizeof(*oip6); 1621254401Scy if (oip6->ip6_nxt == IPPROTO_TCP) { 1622254401Scy tcp = (tcphdr_t *)dp; 1623254401Scy csump = (u_short *)&tcp->th_sum; 1624254401Scy flags = IPN_TCP; 1625254401Scy } else if (oip6->ip6_nxt == IPPROTO_UDP) { 1626254401Scy udphdr_t *udp; 1627254401Scy 1628254401Scy udp = (udphdr_t *)dp; 1629254401Scy tcp = (tcphdr_t *)dp; 1630254401Scy csump = (u_short *)&udp->uh_sum; 1631254401Scy flags = IPN_UDP; 1632254401Scy } else if (oip6->ip6_nxt == IPPROTO_ICMPV6) 1633254401Scy flags = IPN_ICMPQUERY; 1634254401Scy dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip); 1635254401Scy 1636254401Scy /* 1637254401Scy * Need to adjust ICMP header to include the real IP#'s and 1638254401Scy * port #'s. Only apply a checksum change relative to the 1639254401Scy * IP address change as it will be modified again in ipf_nat6_checkout 1640254401Scy * for both address and port. Two checksum changes are 1641254401Scy * necessary for the two header address changes. Be careful 1642254401Scy * to only modify the checksum once for the port # and twice 1643254401Scy * for the IP#. 1644254401Scy */ 1645254401Scy 1646254401Scy /* 1647254401Scy * Step 1 1648254401Scy * Fix the IP addresses in the offending IP packet. You also need 1649254401Scy * to adjust the IP header checksum of that offending IP packet. 1650254401Scy * 1651254401Scy * Normally, you would expect that the ICMP checksum of the 1652254401Scy * ICMP error message needs to be adjusted as well for the 1653254401Scy * IP address change in oip. 1654254401Scy * However, this is a NOP, because the ICMP checksum is 1655254401Scy * calculated over the complete ICMP packet, which includes the 1656254401Scy * changed oip IP addresses and oip6->ip6_sum. However, these 1657254401Scy * two changes cancel each other out (if the delta for 1658254401Scy * the IP address is x, then the delta for ip_sum is minus x), 1659254401Scy * so no change in the icmp_cksum is necessary. 1660254401Scy * 1661254401Scy * Inbound ICMP 1662254401Scy * ------------ 1663254401Scy * MAP rule, SRC=a,DST=b -> SRC=c,DST=b 1664254401Scy * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b) 1665254401Scy * - OIP_SRC(c)=nat6_newsrcip, OIP_DST(b)=nat6_newdstip 1666254401Scy *=> OIP_SRC(c)=nat6_oldsrcip, OIP_DST(b)=nat6_olddstip 1667254401Scy * 1668254401Scy * RDR rule, SRC=a,DST=b -> SRC=a,DST=c 1669254401Scy * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a) 1670254401Scy * - OIP_SRC(b)=nat6_olddstip, OIP_DST(a)=nat6_oldsrcip 1671254401Scy *=> OIP_SRC(b)=nat6_newdstip, OIP_DST(a)=nat6_newsrcip 1672254401Scy * 1673254401Scy * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d 1674254401Scy * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d) 1675254401Scy * - OIP_SRC(c)=nat6_newsrcip, OIP_DST(d)=nat6_newdstip 1676254401Scy *=> OIP_SRC(c)=nat6_oldsrcip, OIP_DST(d)=nat6_olddstip 1677254401Scy * 1678254401Scy * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d 1679254401Scy * - response to outgoing packet (d,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) 1680254401Scy * - OIP_SRC(b)=nat6_olddstip, OIP_DST(a)=nat6_oldsrcip 1681254401Scy *=> OIP_SRC(b)=nat6_newdstip, OIP_DST(a)=nat6_newsrcip 1682254401Scy * 1683254401Scy * Outbound ICMP 1684254401Scy * ------------- 1685254401Scy * MAP rule, SRC=a,DST=b -> SRC=c,DST=b 1686254401Scy * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) 1687254401Scy * - OIP_SRC(b)=nat6_olddstip, OIP_DST(a)=nat6_oldsrcip 1688254401Scy *=> OIP_SRC(b)=nat6_newdstip, OIP_DST(a)=nat6_newsrcip 1689254401Scy * 1690254401Scy * RDR rule, SRC=a,DST=b -> SRC=a,DST=c 1691254401Scy * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c) 1692254401Scy * - OIP_SRC(a)=nat6_newsrcip, OIP_DST(c)=nat6_newdstip 1693254401Scy *=> OIP_SRC(a)=nat6_oldsrcip, OIP_DST(c)=nat6_olddstip 1694254401Scy * 1695254401Scy * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d 1696254401Scy * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d) 1697254401Scy * - OIP_SRC(c)=nat6_olddstip, OIP_DST(d)=nat6_oldsrcip 1698254401Scy *=> OIP_SRC(b)=nat6_newdstip, OIP_DST(a)=nat6_newsrcip 1699254401Scy * 1700254401Scy * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d 1701254401Scy * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a) 1702254401Scy * - OIP_SRC(b)=nat6_newsrcip, OIP_DST(a)=nat6_newdstip 1703254401Scy *=> OIP_SRC(a)=nat6_oldsrcip, OIP_DST(c)=nat6_olddstip 1704254401Scy */ 1705254401Scy 1706254401Scy if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) || 1707254401Scy ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) { 1708254401Scy a1 = nat->nat_osrc6; 1709254401Scy a4.in6 = oip6->ip6_src; 1710254401Scy a3 = nat->nat_odst6; 1711254401Scy a2.in6 = oip6->ip6_dst; 1712254401Scy oip6->ip6_src = a1.in6; 1713254401Scy oip6->ip6_dst = a3.in6; 1714254401Scy odst = 1; 1715254401Scy } else { 1716254401Scy a1 = nat->nat_ndst6; 1717254401Scy a2.in6 = oip6->ip6_dst; 1718254401Scy a3 = nat->nat_nsrc6; 1719254401Scy a4.in6 = oip6->ip6_src; 1720254401Scy oip6->ip6_dst = a3.in6; 1721254401Scy oip6->ip6_src = a1.in6; 1722254401Scy odst = 0; 1723254401Scy } 1724254401Scy 1725254401Scy sumd = 0; 1726254401Scy if (IP6_NEQ(&a3, &a2) || IP6_NEQ(&a1, &a4)) { 1727254401Scy if (IP6_GT(&a3, &a2)) { 1728254401Scy sumd = ipf_nat6_ip6subtract(&a2, &a3); 1729254401Scy sumd--; 1730254401Scy } else { 1731254401Scy sumd = ipf_nat6_ip6subtract(&a2, &a3); 1732254401Scy } 1733254401Scy if (IP6_GT(&a1, &a4)) { 1734254401Scy sumd += ipf_nat6_ip6subtract(&a4, &a1); 1735254401Scy sumd--; 1736254401Scy } else { 1737254401Scy sumd += ipf_nat6_ip6subtract(&a4, &a1); 1738254401Scy } 1739254401Scy sumd = ~sumd; 1740254401Scy } 1741254401Scy 1742254401Scy sumd2 = sumd; 1743254401Scy sum1 = 0; 1744254401Scy sum2 = 0; 1745254401Scy 1746254401Scy /* 1747254401Scy * Fix UDP pseudo header checksum to compensate for the 1748254401Scy * IP address change. 1749254401Scy */ 1750254401Scy if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) { 1751254401Scy u_32_t sum3, sum4; 1752254401Scy /* 1753254401Scy * Step 2 : 1754254401Scy * For offending TCP/UDP IP packets, translate the ports as 1755254401Scy * well, based on the NAT specification. Of course such 1756254401Scy * a change may be reflected in the ICMP checksum as well. 1757254401Scy * 1758254401Scy * Since the port fields are part of the TCP/UDP checksum 1759254401Scy * of the offending IP packet, you need to adjust that checksum 1760254401Scy * as well... except that the change in the port numbers should 1761254401Scy * be offset by the checksum change. However, the TCP/UDP 1762254401Scy * checksum will also need to change if there has been an 1763254401Scy * IP address change. 1764254401Scy */ 1765254401Scy if (odst == 1) { 1766254401Scy sum1 = ntohs(nat->nat_osport); 1767254401Scy sum4 = ntohs(tcp->th_sport); 1768254401Scy sum3 = ntohs(nat->nat_odport); 1769254401Scy sum2 = ntohs(tcp->th_dport); 1770254401Scy 1771254401Scy tcp->th_sport = htons(sum1); 1772254401Scy tcp->th_dport = htons(sum3); 1773254401Scy } else { 1774254401Scy sum1 = ntohs(nat->nat_ndport); 1775254401Scy sum2 = ntohs(tcp->th_dport); 1776254401Scy sum3 = ntohs(nat->nat_nsport); 1777254401Scy sum4 = ntohs(tcp->th_sport); 1778254401Scy 1779254401Scy tcp->th_dport = htons(sum3); 1780254401Scy tcp->th_sport = htons(sum1); 1781254401Scy } 1782254401Scy sumd += sum1 - sum4; 1783254401Scy sumd += sum3 - sum2; 1784254401Scy 1785254401Scy if (sumd != 0 || sumd2 != 0) { 1786254401Scy /* 1787254401Scy * At this point, sumd is the delta to apply to the 1788254401Scy * TCP/UDP header, given the changes in both the IP 1789254401Scy * address and the ports and sumd2 is the delta to 1790254401Scy * apply to the ICMP header, given the IP address 1791254401Scy * change delta that may need to be applied to the 1792254401Scy * TCP/UDP checksum instead. 1793254401Scy * 1794254401Scy * If we will both the IP and TCP/UDP checksums 1795254401Scy * then the ICMP checksum changes by the address 1796254401Scy * delta applied to the TCP/UDP checksum. If we 1797254401Scy * do not change the TCP/UDP checksum them we 1798254401Scy * apply the delta in ports to the ICMP checksum. 1799254401Scy */ 1800254401Scy if (oip6->ip6_nxt == IPPROTO_UDP) { 1801254401Scy if ((dlen >= 8) && (*csump != 0)) { 1802254401Scy ipf_fix_datacksum(csump, sumd); 1803254401Scy } else { 1804254401Scy sumd2 = sum4 - sum1; 1805254401Scy if (sum1 > sum4) 1806254401Scy sumd2--; 1807254401Scy sumd2 += sum2 - sum3; 1808254401Scy if (sum3 > sum2) 1809254401Scy sumd2--; 1810254401Scy } 1811254401Scy } else if (oip6->ip6_nxt == IPPROTO_TCP) { 1812254401Scy if (dlen >= 18) { 1813254401Scy ipf_fix_datacksum(csump, sumd); 1814254401Scy } else { 1815254401Scy sumd2 = sum4 - sum1; 1816254401Scy if (sum1 > sum4) 1817254401Scy sumd2--; 1818254401Scy sumd2 += sum2 - sum3; 1819254401Scy if (sum3 > sum2) 1820254401Scy sumd2--; 1821254401Scy } 1822254401Scy } 1823254401Scy if (sumd2 != 0) { 1824254401Scy sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 1825254401Scy sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 1826254401Scy sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 1827254401Scy ipf_fix_incksum(0, &icmp6->icmp6_cksum, 1828254401Scy sumd2, 0); 1829254401Scy } 1830254401Scy } 1831254401Scy } else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) { 1832254401Scy struct icmp6_hdr *orgicmp; 1833254401Scy 1834254401Scy /* 1835254401Scy * XXX - what if this is bogus hl and we go off the end ? 1836254401Scy * In this case, ipf_nat6_icmperrorlookup() will have 1837254401Scy * returned NULL. 1838254401Scy */ 1839254401Scy orgicmp = (struct icmp6_hdr *)dp; 1840254401Scy 1841254401Scy if (odst == 1) { 1842254401Scy if (orgicmp->icmp6_id != nat->nat_osport) { 1843254401Scy 1844254401Scy /* 1845254401Scy * Fix ICMP checksum (of the offening ICMP 1846254401Scy * query packet) to compensate the change 1847254401Scy * in the ICMP id of the offending ICMP 1848254401Scy * packet. 1849254401Scy * 1850254401Scy * Since you modify orgicmp->icmp6_id with 1851254401Scy * a delta (say x) and you compensate that 1852254401Scy * in origicmp->icmp6_cksum with a delta 1853254401Scy * minus x, you don't have to adjust the 1854254401Scy * overall icmp->icmp6_cksum 1855254401Scy */ 1856254401Scy sum1 = ntohs(orgicmp->icmp6_id); 1857254401Scy sum2 = ntohs(nat->nat_osport); 1858254401Scy CALC_SUMD(sum1, sum2, sumd); 1859254401Scy orgicmp->icmp6_id = nat->nat_oicmpid; 1860254401Scy ipf_fix_datacksum(&orgicmp->icmp6_cksum, sumd); 1861254401Scy } 1862254401Scy } /* nat6_dir == NAT_INBOUND is impossible for icmp queries */ 1863254401Scy } 1864254401Scy return nat; 1865254401Scy} 1866254401Scy 1867254401Scy 1868254401Scy/* 1869254401Scy * MAP-IN MAP-OUT RDR-IN RDR-OUT 1870254401Scy * osrc X == src == src X 1871254401Scy * odst X == dst == dst X 1872254401Scy * nsrc == dst X X == dst 1873254401Scy * ndst == src X X == src 1874254401Scy * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND 1875254401Scy */ 1876254401Scy/* 1877254401Scy * NB: these lookups don't lock access to the list, it assumed that it has 1878254401Scy * already been done! 1879254401Scy */ 1880254401Scy/* ------------------------------------------------------------------------ */ 1881254401Scy/* Function: ipf_nat6_inlookup */ 1882254401Scy/* Returns: nat6_t* - NULL == no match, */ 1883254401Scy/* else pointer to matching NAT entry */ 1884254401Scy/* Parameters: fin(I) - pointer to packet information */ 1885254401Scy/* flags(I) - NAT flags for this packet */ 1886254401Scy/* p(I) - protocol for this packet */ 1887254401Scy/* src(I) - source IP address */ 1888254401Scy/* mapdst(I) - destination IP address */ 1889254401Scy/* */ 1890254401Scy/* Lookup a nat entry based on the mapped destination ip address/port and */ 1891254401Scy/* real source address/port. We use this lookup when receiving a packet, */ 1892254401Scy/* we're looking for a table entry, based on the destination address. */ 1893254401Scy/* */ 1894254401Scy/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 1895254401Scy/* */ 1896254401Scy/* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ 1897254401Scy/* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 1898254401Scy/* */ 1899254401Scy/* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 1900254401Scy/* the packet is of said protocol */ 1901254401Scy/* ------------------------------------------------------------------------ */ 1902254401Scynat_t * 1903254401Scyipf_nat6_inlookup(fin, flags, p, src, mapdst) 1904254401Scy fr_info_t *fin; 1905254401Scy u_int flags, p; 1906254401Scy struct in6_addr *src , *mapdst; 1907254401Scy{ 1908254401Scy ipf_main_softc_t *softc = fin->fin_main_soft; 1909254401Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 1910254401Scy u_short sport, dport; 1911254401Scy grehdr_t *gre; 1912254401Scy ipnat_t *ipn; 1913254401Scy u_int sflags; 1914254401Scy nat_t *nat; 1915254401Scy int nflags; 1916254401Scy i6addr_t dst; 1917254401Scy void *ifp; 1918254401Scy u_int hv; 1919254401Scy 1920254401Scy ifp = fin->fin_ifp; 1921254401Scy sport = 0; 1922254401Scy dport = 0; 1923254401Scy gre = NULL; 1924254401Scy dst.in6 = *mapdst; 1925254401Scy sflags = flags & NAT_TCPUDPICMP; 1926254401Scy 1927254401Scy switch (p) 1928254401Scy { 1929254401Scy case IPPROTO_TCP : 1930254401Scy case IPPROTO_UDP : 1931254401Scy sport = htons(fin->fin_data[0]); 1932254401Scy dport = htons(fin->fin_data[1]); 1933254401Scy break; 1934254401Scy case IPPROTO_ICMPV6 : 1935254401Scy if (flags & IPN_ICMPERR) 1936254401Scy sport = fin->fin_data[1]; 1937254401Scy else 1938254401Scy dport = fin->fin_data[1]; 1939254401Scy break; 1940254401Scy default : 1941254401Scy break; 1942254401Scy } 1943254401Scy 1944254401Scy 1945254401Scy if ((flags & SI_WILDP) != 0) 1946254401Scy goto find_in_wild_ports; 1947254401Scy 1948254401Scy hv = NAT_HASH_FN6(&dst, dport, 0xffffffff); 1949254401Scy hv = NAT_HASH_FN6(src, hv + sport, softn->ipf_nat_table_sz); 1950254401Scy nat = softn->ipf_nat_table[1][hv]; 1951254401Scy /* TRACE dst, dport, src, sport, hv, nat */ 1952254401Scy 1953254401Scy for (; nat; nat = nat->nat_hnext[1]) { 1954254401Scy if (nat->nat_ifps[0] != NULL) { 1955254401Scy if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 1956254401Scy continue; 1957254401Scy } 1958254401Scy 1959254401Scy if (nat->nat_pr[0] != p) 1960254401Scy continue; 1961254401Scy 1962254401Scy switch (nat->nat_dir) 1963254401Scy { 1964254401Scy case NAT_INBOUND : 1965254401Scy if (nat->nat_v[0] != 6) 1966254401Scy continue; 1967254401Scy if (IP6_NEQ(&nat->nat_osrc6, src) || 1968254401Scy IP6_NEQ(&nat->nat_odst6, &dst)) 1969254401Scy continue; 1970254401Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 1971254401Scy if (nat->nat_osport != sport) 1972254401Scy continue; 1973254401Scy if (nat->nat_odport != dport) 1974254401Scy continue; 1975254401Scy 1976254401Scy } else if (p == IPPROTO_ICMPV6) { 1977254401Scy if (nat->nat_osport != dport) { 1978254401Scy continue; 1979254401Scy } 1980254401Scy } 1981254401Scy break; 1982254401Scy case NAT_OUTBOUND : 1983254401Scy if (nat->nat_v[1] != 6) 1984254401Scy continue; 1985254401Scy if (IP6_NEQ(&nat->nat_ndst6, src) || 1986254401Scy IP6_NEQ(&nat->nat_nsrc6, &dst)) 1987254401Scy continue; 1988254401Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 1989254401Scy if (nat->nat_ndport != sport) 1990254401Scy continue; 1991254401Scy if (nat->nat_nsport != dport) 1992254401Scy continue; 1993254401Scy 1994254401Scy } else if (p == IPPROTO_ICMPV6) { 1995254401Scy if (nat->nat_osport != dport) { 1996254401Scy continue; 1997254401Scy } 1998254401Scy } 1999254401Scy break; 2000254401Scy } 2001254401Scy 2002254401Scy 2003254401Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 2004254401Scy ipn = nat->nat_ptr; 2005254401Scy#ifdef IPF_V6_PROXIES 2006254401Scy if ((ipn != NULL) && (nat->nat_aps != NULL)) 2007254401Scy if (appr_match(fin, nat) != 0) 2008254401Scy continue; 2009254401Scy#endif 2010254401Scy } 2011254401Scy if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { 2012254401Scy nat->nat_ifps[0] = ifp; 2013254401Scy nat->nat_mtu[0] = GETIFMTU_6(ifp); 2014254401Scy } 2015254401Scy return nat; 2016254401Scy } 2017254401Scy 2018254401Scy /* 2019254401Scy * So if we didn't find it but there are wildcard members in the hash 2020254401Scy * table, go back and look for them. We do this search and update here 2021254401Scy * because it is modifying the NAT table and we want to do this only 2022254401Scy * for the first packet that matches. The exception, of course, is 2023254401Scy * for "dummy" (FI_IGNORE) lookups. 2024254401Scy */ 2025254401Scyfind_in_wild_ports: 2026254401Scy if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { 2027254401Scy NBUMPSIDE6DX(0, ns_lookup_miss, ns_lookup_miss_1); 2028254401Scy return NULL; 2029254401Scy } 2030254401Scy if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { 2031254401Scy NBUMPSIDE6D(0, ns_lookup_nowild); 2032254401Scy return NULL; 2033254401Scy } 2034254401Scy 2035254401Scy RWLOCK_EXIT(&softc->ipf_nat); 2036254401Scy 2037254401Scy hv = NAT_HASH_FN6(&dst, 0, 0xffffffff); 2038254401Scy hv = NAT_HASH_FN6(src, hv, softn->ipf_nat_table_sz); 2039254401Scy WRITE_ENTER(&softc->ipf_nat); 2040254401Scy 2041254401Scy nat = softn->ipf_nat_table[1][hv]; 2042254401Scy /* TRACE dst, src, hv, nat */ 2043254401Scy for (; nat; nat = nat->nat_hnext[1]) { 2044254401Scy if (nat->nat_ifps[0] != NULL) { 2045254401Scy if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 2046254401Scy continue; 2047254401Scy } 2048254401Scy 2049254401Scy if (nat->nat_pr[0] != fin->fin_p) 2050254401Scy continue; 2051254401Scy 2052254401Scy switch (nat->nat_dir) 2053254401Scy { 2054254401Scy case NAT_INBOUND : 2055254401Scy if (nat->nat_v[0] != 6) 2056254401Scy continue; 2057254401Scy if (IP6_NEQ(&nat->nat_osrc6, src) || 2058254401Scy IP6_NEQ(&nat->nat_odst6, &dst)) 2059254401Scy continue; 2060254401Scy break; 2061254401Scy case NAT_OUTBOUND : 2062254401Scy if (nat->nat_v[1] != 6) 2063254401Scy continue; 2064254401Scy if (IP6_NEQ(&nat->nat_ndst6, src) || 2065254401Scy IP6_NEQ(&nat->nat_nsrc6, &dst)) 2066254401Scy continue; 2067254401Scy break; 2068254401Scy } 2069254401Scy 2070254401Scy nflags = nat->nat_flags; 2071254401Scy if (!(nflags & (NAT_TCPUDP|SI_WILDP))) 2072254401Scy continue; 2073254401Scy 2074254401Scy if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags, 2075254401Scy NAT_INBOUND) == 1) { 2076254401Scy if ((fin->fin_flx & FI_IGNORE) != 0) 2077254401Scy break; 2078254401Scy if ((nflags & SI_CLONE) != 0) { 2079254401Scy nat = ipf_nat_clone(fin, nat); 2080254401Scy if (nat == NULL) 2081254401Scy break; 2082254401Scy } else { 2083254401Scy MUTEX_ENTER(&softn->ipf_nat_new); 2084254401Scy softn->ipf_nat_stats.ns_wilds--; 2085254401Scy MUTEX_EXIT(&softn->ipf_nat_new); 2086254401Scy } 2087254401Scy 2088254401Scy if (nat->nat_dir == NAT_INBOUND) { 2089254401Scy if (nat->nat_osport == 0) { 2090254401Scy nat->nat_osport = sport; 2091254401Scy nat->nat_nsport = sport; 2092254401Scy } 2093254401Scy if (nat->nat_odport == 0) { 2094254401Scy nat->nat_odport = dport; 2095254401Scy nat->nat_ndport = dport; 2096254401Scy } 2097254401Scy } else { 2098254401Scy if (nat->nat_osport == 0) { 2099254401Scy nat->nat_osport = dport; 2100254401Scy nat->nat_nsport = dport; 2101254401Scy } 2102254401Scy if (nat->nat_odport == 0) { 2103254401Scy nat->nat_odport = sport; 2104254401Scy nat->nat_ndport = sport; 2105254401Scy } 2106254401Scy } 2107254401Scy if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { 2108254401Scy nat->nat_ifps[0] = ifp; 2109254401Scy nat->nat_mtu[0] = GETIFMTU_6(ifp); 2110254401Scy } 2111254401Scy nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 2112254401Scy ipf_nat6_tabmove(softn, nat); 2113254401Scy break; 2114254401Scy } 2115254401Scy } 2116254401Scy 2117254401Scy MUTEX_DOWNGRADE(&softc->ipf_nat); 2118254401Scy 2119254401Scy if (nat == NULL) { 2120254401Scy NBUMPSIDE6DX(0, ns_lookup_miss, ns_lookup_miss_2); 2121254401Scy } 2122254401Scy return nat; 2123254401Scy} 2124254401Scy 2125254401Scy 2126254401Scy/* ------------------------------------------------------------------------ */ 2127254401Scy/* Function: ipf_nat6_tabmove */ 2128254401Scy/* Returns: Nil */ 2129254401Scy/* Parameters: nat(I) - pointer to NAT structure */ 2130254401Scy/* Write Lock: ipf_nat */ 2131254401Scy/* */ 2132254401Scy/* This function is only called for TCP/UDP NAT table entries where the */ 2133254401Scy/* original was placed in the table without hashing on the ports and we now */ 2134254401Scy/* want to include hashing on port numbers. */ 2135254401Scy/* ------------------------------------------------------------------------ */ 2136254401Scystatic void 2137254401Scyipf_nat6_tabmove(softn, nat) 2138254401Scy ipf_nat_softc_t *softn; 2139254401Scy nat_t *nat; 2140254401Scy{ 2141254401Scy nat_t **natp; 2142254401Scy u_int hv0, hv1; 2143254401Scy 2144254401Scy if (nat->nat_flags & SI_CLONE) 2145254401Scy return; 2146254401Scy 2147254401Scy /* 2148254401Scy * Remove the NAT entry from the old location 2149254401Scy */ 2150254401Scy if (nat->nat_hnext[0]) 2151254401Scy nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 2152254401Scy *nat->nat_phnext[0] = nat->nat_hnext[0]; 2153254401Scy softn->ipf_nat_stats.ns_side[0].ns_bucketlen[nat->nat_hv[0]]--; 2154254401Scy 2155254401Scy if (nat->nat_hnext[1]) 2156254401Scy nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 2157254401Scy *nat->nat_phnext[1] = nat->nat_hnext[1]; 2158254401Scy softn->ipf_nat_stats.ns_side[1].ns_bucketlen[nat->nat_hv[1]]--; 2159254401Scy 2160254401Scy /* 2161254401Scy * Add into the NAT table in the new position 2162254401Scy */ 2163254401Scy hv0 = NAT_HASH_FN6(&nat->nat_osrc6, nat->nat_osport, 0xffffffff); 2164254401Scy hv0 = NAT_HASH_FN6(&nat->nat_odst6, hv0 + nat->nat_odport, 2165254401Scy softn->ipf_nat_table_sz); 2166254401Scy hv1 = NAT_HASH_FN6(&nat->nat_nsrc6, nat->nat_nsport, 0xffffffff); 2167254401Scy hv1 = NAT_HASH_FN6(&nat->nat_ndst6, hv1 + nat->nat_ndport, 2168254401Scy softn->ipf_nat_table_sz); 2169254401Scy 2170254401Scy if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) { 2171254401Scy u_int swap; 2172254401Scy 2173254401Scy swap = hv0; 2174254401Scy hv0 = hv1; 2175254401Scy hv1 = swap; 2176254401Scy } 2177254401Scy 2178254401Scy /* TRACE nat_osrc6, nat_osport, nat_odst6, nat_odport, hv0 */ 2179254401Scy /* TRACE nat_nsrc6, nat_nsport, nat_ndst6, nat_ndport, hv1 */ 2180254401Scy 2181254401Scy nat->nat_hv[0] = hv0; 2182254401Scy natp = &softn->ipf_nat_table[0][hv0]; 2183254401Scy if (*natp) 2184254401Scy (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 2185254401Scy nat->nat_phnext[0] = natp; 2186254401Scy nat->nat_hnext[0] = *natp; 2187254401Scy *natp = nat; 2188254401Scy softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]++; 2189254401Scy 2190254401Scy nat->nat_hv[1] = hv1; 2191254401Scy natp = &softn->ipf_nat_table[1][hv1]; 2192254401Scy if (*natp) 2193254401Scy (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 2194254401Scy nat->nat_phnext[1] = natp; 2195254401Scy nat->nat_hnext[1] = *natp; 2196254401Scy *natp = nat; 2197254401Scy softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]++; 2198254401Scy} 2199254401Scy 2200254401Scy 2201254401Scy/* ------------------------------------------------------------------------ */ 2202254401Scy/* Function: ipf_nat6_outlookup */ 2203254401Scy/* Returns: nat6_t* - NULL == no match, */ 2204254401Scy/* else pointer to matching NAT entry */ 2205254401Scy/* Parameters: fin(I) - pointer to packet information */ 2206254401Scy/* flags(I) - NAT flags for this packet */ 2207254401Scy/* p(I) - protocol for this packet */ 2208254401Scy/* src(I) - source IP address */ 2209254401Scy/* dst(I) - destination IP address */ 2210254401Scy/* rw(I) - 1 == write lock on held, 0 == read lock. */ 2211254401Scy/* */ 2212254401Scy/* Lookup a nat entry based on the source 'real' ip address/port and */ 2213254401Scy/* destination address/port. We use this lookup when sending a packet out, */ 2214254401Scy/* we're looking for a table entry, based on the source address. */ 2215254401Scy/* */ 2216254401Scy/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 2217254401Scy/* */ 2218254401Scy/* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ 2219254401Scy/* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 2220254401Scy/* */ 2221254401Scy/* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 2222254401Scy/* the packet is of said protocol */ 2223254401Scy/* ------------------------------------------------------------------------ */ 2224254401Scynat_t * 2225254401Scyipf_nat6_outlookup(fin, flags, p, src, dst) 2226254401Scy fr_info_t *fin; 2227254401Scy u_int flags, p; 2228254401Scy struct in6_addr *src , *dst; 2229254401Scy{ 2230254401Scy ipf_main_softc_t *softc = fin->fin_main_soft; 2231254401Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 2232254401Scy u_short sport, dport; 2233254401Scy u_int sflags; 2234254401Scy ipnat_t *ipn; 2235254401Scy nat_t *nat; 2236254401Scy void *ifp; 2237254401Scy u_int hv; 2238254401Scy 2239254401Scy ifp = fin->fin_ifp; 2240254401Scy sflags = flags & IPN_TCPUDPICMP; 2241254401Scy sport = 0; 2242254401Scy dport = 0; 2243254401Scy 2244254401Scy switch (p) 2245254401Scy { 2246254401Scy case IPPROTO_TCP : 2247254401Scy case IPPROTO_UDP : 2248254401Scy sport = htons(fin->fin_data[0]); 2249254401Scy dport = htons(fin->fin_data[1]); 2250254401Scy break; 2251254401Scy case IPPROTO_ICMPV6 : 2252254401Scy if (flags & IPN_ICMPERR) 2253254401Scy sport = fin->fin_data[1]; 2254254401Scy else 2255254401Scy dport = fin->fin_data[1]; 2256254401Scy break; 2257254401Scy default : 2258254401Scy break; 2259254401Scy } 2260254401Scy 2261254401Scy if ((flags & SI_WILDP) != 0) 2262254401Scy goto find_out_wild_ports; 2263254401Scy 2264254401Scy hv = NAT_HASH_FN6(src, sport, 0xffffffff); 2265254401Scy hv = NAT_HASH_FN6(dst, hv + dport, softn->ipf_nat_table_sz); 2266254401Scy nat = softn->ipf_nat_table[0][hv]; 2267254401Scy 2268254401Scy /* TRACE src, sport, dst, dport, hv, nat */ 2269254401Scy 2270254401Scy for (; nat; nat = nat->nat_hnext[0]) { 2271254401Scy if (nat->nat_ifps[1] != NULL) { 2272254401Scy if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 2273254401Scy continue; 2274254401Scy } 2275254401Scy 2276254401Scy if (nat->nat_pr[1] != p) 2277254401Scy continue; 2278254401Scy 2279254401Scy switch (nat->nat_dir) 2280254401Scy { 2281254401Scy case NAT_INBOUND : 2282254401Scy if (nat->nat_v[1] != 6) 2283254401Scy continue; 2284254401Scy if (IP6_NEQ(&nat->nat_ndst6, src) || 2285254401Scy IP6_NEQ(&nat->nat_nsrc6, dst)) 2286254401Scy continue; 2287254401Scy 2288254401Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 2289254401Scy if (nat->nat_ndport != sport) 2290254401Scy continue; 2291254401Scy if (nat->nat_nsport != dport) 2292254401Scy continue; 2293254401Scy 2294254401Scy } else if (p == IPPROTO_ICMPV6) { 2295254401Scy if (nat->nat_osport != dport) { 2296254401Scy continue; 2297254401Scy } 2298254401Scy } 2299254401Scy break; 2300254401Scy case NAT_OUTBOUND : 2301254401Scy if (nat->nat_v[0] != 6) 2302254401Scy continue; 2303254401Scy if (IP6_NEQ(&nat->nat_osrc6, src) || 2304254401Scy IP6_NEQ(&nat->nat_odst6, dst)) 2305254401Scy continue; 2306254401Scy 2307254401Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 2308254401Scy if (nat->nat_odport != dport) 2309254401Scy continue; 2310254401Scy if (nat->nat_osport != sport) 2311254401Scy continue; 2312254401Scy 2313254401Scy } else if (p == IPPROTO_ICMPV6) { 2314254401Scy if (nat->nat_osport != dport) { 2315254401Scy continue; 2316254401Scy } 2317254401Scy } 2318254401Scy break; 2319254401Scy } 2320254401Scy 2321254401Scy ipn = nat->nat_ptr; 2322254401Scy#ifdef IPF_V6_PROXIES 2323254401Scy if ((ipn != NULL) && (nat->nat_aps != NULL)) 2324254401Scy if (appr_match(fin, nat) != 0) 2325254401Scy continue; 2326254401Scy#endif 2327254401Scy 2328254401Scy if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { 2329254401Scy nat->nat_ifps[1] = ifp; 2330254401Scy nat->nat_mtu[1] = GETIFMTU_6(ifp); 2331254401Scy } 2332254401Scy return nat; 2333254401Scy } 2334254401Scy 2335254401Scy /* 2336254401Scy * So if we didn't find it but there are wildcard members in the hash 2337254401Scy * table, go back and look for them. We do this search and update here 2338254401Scy * because it is modifying the NAT table and we want to do this only 2339254401Scy * for the first packet that matches. The exception, of course, is 2340254401Scy * for "dummy" (FI_IGNORE) lookups. 2341254401Scy */ 2342254401Scyfind_out_wild_ports: 2343254401Scy if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { 2344254401Scy NBUMPSIDE6DX(1, ns_lookup_miss, ns_lookup_miss_3); 2345254401Scy return NULL; 2346254401Scy } 2347254401Scy if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { 2348254401Scy NBUMPSIDE6D(1, ns_lookup_nowild); 2349254401Scy return NULL; 2350254401Scy } 2351254401Scy 2352254401Scy RWLOCK_EXIT(&softc->ipf_nat); 2353254401Scy 2354254401Scy hv = NAT_HASH_FN6(src, 0, 0xffffffff); 2355254401Scy hv = NAT_HASH_FN6(dst, hv, softn->ipf_nat_table_sz); 2356254401Scy 2357254401Scy WRITE_ENTER(&softc->ipf_nat); 2358254401Scy 2359254401Scy nat = softn->ipf_nat_table[0][hv]; 2360254401Scy for (; nat; nat = nat->nat_hnext[0]) { 2361254401Scy if (nat->nat_ifps[1] != NULL) { 2362254401Scy if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 2363254401Scy continue; 2364254401Scy } 2365254401Scy 2366254401Scy if (nat->nat_pr[1] != fin->fin_p) 2367254401Scy continue; 2368254401Scy 2369254401Scy switch (nat->nat_dir) 2370254401Scy { 2371254401Scy case NAT_INBOUND : 2372254401Scy if (nat->nat_v[1] != 6) 2373254401Scy continue; 2374254401Scy if (IP6_NEQ(&nat->nat_ndst6, src) || 2375254401Scy IP6_NEQ(&nat->nat_nsrc6, dst)) 2376254401Scy continue; 2377254401Scy break; 2378254401Scy case NAT_OUTBOUND : 2379254401Scy if (nat->nat_v[0] != 6) 2380254401Scy continue; 2381254401Scy if (IP6_NEQ(&nat->nat_osrc6, src) || 2382254401Scy IP6_NEQ(&nat->nat_odst6, dst)) 2383254401Scy continue; 2384254401Scy break; 2385254401Scy } 2386254401Scy 2387254401Scy if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP))) 2388254401Scy continue; 2389254401Scy 2390254401Scy if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags, 2391254401Scy NAT_OUTBOUND) == 1) { 2392254401Scy if ((fin->fin_flx & FI_IGNORE) != 0) 2393254401Scy break; 2394254401Scy if ((nat->nat_flags & SI_CLONE) != 0) { 2395254401Scy nat = ipf_nat_clone(fin, nat); 2396254401Scy if (nat == NULL) 2397254401Scy break; 2398254401Scy } else { 2399254401Scy MUTEX_ENTER(&softn->ipf_nat_new); 2400254401Scy softn->ipf_nat_stats.ns_wilds--; 2401254401Scy MUTEX_EXIT(&softn->ipf_nat_new); 2402254401Scy } 2403254401Scy 2404254401Scy if (nat->nat_dir == NAT_OUTBOUND) { 2405254401Scy if (nat->nat_osport == 0) { 2406254401Scy nat->nat_osport = sport; 2407254401Scy nat->nat_nsport = sport; 2408254401Scy } 2409254401Scy if (nat->nat_odport == 0) { 2410254401Scy nat->nat_odport = dport; 2411254401Scy nat->nat_ndport = dport; 2412254401Scy } 2413254401Scy } else { 2414254401Scy if (nat->nat_osport == 0) { 2415254401Scy nat->nat_osport = dport; 2416254401Scy nat->nat_nsport = dport; 2417254401Scy } 2418254401Scy if (nat->nat_odport == 0) { 2419254401Scy nat->nat_odport = sport; 2420254401Scy nat->nat_ndport = sport; 2421254401Scy } 2422254401Scy } 2423254401Scy if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { 2424254401Scy nat->nat_ifps[1] = ifp; 2425254401Scy nat->nat_mtu[1] = GETIFMTU_6(ifp); 2426254401Scy } 2427254401Scy nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 2428254401Scy ipf_nat6_tabmove(softn, nat); 2429254401Scy break; 2430254401Scy } 2431254401Scy } 2432254401Scy 2433254401Scy MUTEX_DOWNGRADE(&softc->ipf_nat); 2434254401Scy 2435254401Scy if (nat == NULL) { 2436254401Scy NBUMPSIDE6DX(1, ns_lookup_miss, ns_lookup_miss_4); 2437254401Scy } 2438254401Scy return nat; 2439254401Scy} 2440254401Scy 2441254401Scy 2442254401Scy/* ------------------------------------------------------------------------ */ 2443254401Scy/* Function: ipf_nat6_lookupredir */ 2444254401Scy/* Returns: nat6_t* - NULL == no match, */ 2445254401Scy/* else pointer to matching NAT entry */ 2446254401Scy/* Parameters: np(I) - pointer to description of packet to find NAT table */ 2447254401Scy/* entry for. */ 2448254401Scy/* */ 2449254401Scy/* Lookup the NAT tables to search for a matching redirect */ 2450254401Scy/* The contents of natlookup_t should imitate those found in a packet that */ 2451254401Scy/* would be translated - ie a packet coming in for RDR or going out for MAP.*/ 2452254401Scy/* We can do the lookup in one of two ways, imitating an inbound or */ 2453254401Scy/* outbound packet. By default we assume outbound, unless IPN_IN is set. */ 2454254401Scy/* For IN, the fields are set as follows: */ 2455254401Scy/* nl_real* = source information */ 2456254401Scy/* nl_out* = destination information (translated) */ 2457254401Scy/* For an out packet, the fields are set like this: */ 2458254401Scy/* nl_in* = source information (untranslated) */ 2459254401Scy/* nl_out* = destination information (translated) */ 2460254401Scy/* ------------------------------------------------------------------------ */ 2461254401Scynat_t * 2462254401Scyipf_nat6_lookupredir(np) 2463254401Scy natlookup_t *np; 2464254401Scy{ 2465254401Scy fr_info_t fi; 2466254401Scy nat_t *nat; 2467254401Scy 2468254401Scy bzero((char *)&fi, sizeof(fi)); 2469254401Scy if (np->nl_flags & IPN_IN) { 2470254401Scy fi.fin_data[0] = ntohs(np->nl_realport); 2471254401Scy fi.fin_data[1] = ntohs(np->nl_outport); 2472254401Scy } else { 2473254401Scy fi.fin_data[0] = ntohs(np->nl_inport); 2474254401Scy fi.fin_data[1] = ntohs(np->nl_outport); 2475254401Scy } 2476254401Scy if (np->nl_flags & IPN_TCP) 2477254401Scy fi.fin_p = IPPROTO_TCP; 2478254401Scy else if (np->nl_flags & IPN_UDP) 2479254401Scy fi.fin_p = IPPROTO_UDP; 2480254401Scy else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) 2481254401Scy fi.fin_p = IPPROTO_ICMPV6; 2482254401Scy 2483254401Scy /* 2484254401Scy * We can do two sorts of lookups: 2485254401Scy * - IPN_IN: we have the `real' and `out' address, look for `in'. 2486254401Scy * - default: we have the `in' and `out' address, look for `real'. 2487254401Scy */ 2488254401Scy if (np->nl_flags & IPN_IN) { 2489254401Scy if ((nat = ipf_nat6_inlookup(&fi, np->nl_flags, fi.fin_p, 2490254401Scy &np->nl_realip6, 2491254401Scy &np->nl_outip6))) { 2492254401Scy np->nl_inip6 = nat->nat_odst6.in6; 2493254401Scy np->nl_inport = nat->nat_odport; 2494254401Scy } 2495254401Scy } else { 2496254401Scy /* 2497254401Scy * If nl_inip is non null, this is a lookup based on the real 2498254401Scy * ip address. Else, we use the fake. 2499254401Scy */ 2500254401Scy if ((nat = ipf_nat6_outlookup(&fi, np->nl_flags, fi.fin_p, 2501254401Scy &np->nl_inip6, &np->nl_outip6))) { 2502254401Scy 2503254401Scy if ((np->nl_flags & IPN_FINDFORWARD) != 0) { 2504254401Scy fr_info_t fin; 2505254401Scy bzero((char *)&fin, sizeof(fin)); 2506254401Scy fin.fin_p = nat->nat_pr[0]; 2507254401Scy fin.fin_data[0] = ntohs(nat->nat_ndport); 2508254401Scy fin.fin_data[1] = ntohs(nat->nat_nsport); 2509254401Scy if (ipf_nat6_inlookup(&fin, np->nl_flags, 2510254401Scy fin.fin_p, 2511254401Scy &nat->nat_ndst6.in6, 2512254401Scy &nat->nat_nsrc6.in6) != 2513254401Scy NULL) { 2514254401Scy np->nl_flags &= ~IPN_FINDFORWARD; 2515254401Scy } 2516254401Scy } 2517254401Scy 2518315079Scy np->nl_realip6 = nat->nat_odst6.in6; 2519315079Scy np->nl_realport = nat->nat_odport; 2520254401Scy } 2521254401Scy } 2522254401Scy 2523254401Scy return nat; 2524254401Scy} 2525254401Scy 2526254401Scy 2527254401Scy/* ------------------------------------------------------------------------ */ 2528254401Scy/* Function: ipf_nat6_match */ 2529254401Scy/* Returns: int - 0 == no match, 1 == match */ 2530254401Scy/* Parameters: fin(I) - pointer to packet information */ 2531254401Scy/* np(I) - pointer to NAT rule */ 2532254401Scy/* */ 2533254401Scy/* Pull the matching of a packet against a NAT rule out of that complex */ 2534254401Scy/* loop inside ipf_nat6_checkin() and lay it out properly in its own */ 2535254401Scy/* function. */ 2536254401Scy/* ------------------------------------------------------------------------ */ 2537254401Scystatic int 2538254401Scyipf_nat6_match(fin, np) 2539254401Scy fr_info_t *fin; 2540254401Scy ipnat_t *np; 2541254401Scy{ 2542254401Scy frtuc_t *ft; 2543254401Scy int match; 2544254401Scy 2545254401Scy match = 0; 2546254401Scy switch (np->in_osrcatype) 2547254401Scy { 2548254401Scy case FRI_NORMAL : 2549254401Scy match = IP6_MASKNEQ(&fin->fin_src6, &np->in_osrcmsk6, 2550254401Scy &np->in_osrcip6); 2551254401Scy break; 2552254401Scy case FRI_LOOKUP : 2553254401Scy match = (*np->in_osrcfunc)(fin->fin_main_soft, np->in_osrcptr, 2554254401Scy 6, &fin->fin_src6, fin->fin_plen); 2555254401Scy break; 2556254401Scy } 2557254401Scy match ^= ((np->in_flags & IPN_NOTSRC) != 0); 2558254401Scy if (match) 2559254401Scy return 0; 2560254401Scy 2561254401Scy match = 0; 2562254401Scy switch (np->in_odstatype) 2563254401Scy { 2564254401Scy case FRI_NORMAL : 2565254401Scy match = IP6_MASKNEQ(&fin->fin_dst6, &np->in_odstmsk6, 2566254401Scy &np->in_odstip6); 2567254401Scy break; 2568254401Scy case FRI_LOOKUP : 2569254401Scy match = (*np->in_odstfunc)(fin->fin_main_soft, np->in_odstptr, 2570254401Scy 6, &fin->fin_dst6, fin->fin_plen); 2571254401Scy break; 2572254401Scy } 2573254401Scy 2574254401Scy match ^= ((np->in_flags & IPN_NOTDST) != 0); 2575254401Scy if (match) 2576254401Scy return 0; 2577254401Scy 2578254401Scy ft = &np->in_tuc; 2579254401Scy if (!(fin->fin_flx & FI_TCPUDP) || 2580254401Scy (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { 2581254401Scy if (ft->ftu_scmp || ft->ftu_dcmp) 2582254401Scy return 0; 2583254401Scy return 1; 2584254401Scy } 2585254401Scy 2586254401Scy return ipf_tcpudpchk(&fin->fin_fi, ft); 2587254401Scy} 2588254401Scy 2589254401Scy 2590254401Scy/* ------------------------------------------------------------------------ */ 2591254401Scy/* Function: ipf_nat6_checkout */ 2592254401Scy/* Returns: int - -1 == packet failed NAT checks so block it, */ 2593254401Scy/* 0 == no packet translation occurred, */ 2594254401Scy/* 1 == packet was successfully translated. */ 2595254401Scy/* Parameters: fin(I) - pointer to packet information */ 2596254401Scy/* passp(I) - pointer to filtering result flags */ 2597254401Scy/* */ 2598254401Scy/* Check to see if an outcoming packet should be changed. ICMP packets are */ 2599254401Scy/* first checked to see if they match an existing entry (if an error), */ 2600254401Scy/* otherwise a search of the current NAT table is made. If neither results */ 2601254401Scy/* in a match then a search for a matching NAT rule is made. Create a new */ 2602254401Scy/* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 2603254401Scy/* packet header(s) as required. */ 2604254401Scy/* ------------------------------------------------------------------------ */ 2605254401Scyint 2606254401Scyipf_nat6_checkout(fin, passp) 2607254401Scy fr_info_t *fin; 2608254401Scy u_32_t *passp; 2609254401Scy{ 2610254401Scy ipf_main_softc_t *softc = fin->fin_main_soft; 2611254401Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 2612254401Scy struct icmp6_hdr *icmp6 = NULL; 2613254401Scy struct ifnet *ifp, *sifp; 2614254401Scy tcphdr_t *tcp = NULL; 2615254401Scy int rval, natfailed; 2616254401Scy ipnat_t *np = NULL; 2617254401Scy u_int nflags = 0; 2618254401Scy i6addr_t ipa, iph; 2619254401Scy int natadd = 1; 2620254401Scy frentry_t *fr; 2621254401Scy nat_t *nat; 2622254401Scy 2623254401Scy if (softn->ipf_nat_stats.ns_rules == 0 || softn->ipf_nat_lock != 0) 2624254401Scy return 0; 2625254401Scy 2626254401Scy icmp6 = NULL; 2627254401Scy natfailed = 0; 2628254401Scy fr = fin->fin_fr; 2629254401Scy sifp = fin->fin_ifp; 2630254401Scy if (fr != NULL) { 2631254401Scy ifp = fr->fr_tifs[fin->fin_rev].fd_ptr; 2632254401Scy if ((ifp != NULL) && (ifp != (void *)-1)) 2633254401Scy fin->fin_ifp = ifp; 2634254401Scy } 2635254401Scy ifp = fin->fin_ifp; 2636254401Scy 2637254401Scy if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 2638254401Scy switch (fin->fin_p) 2639254401Scy { 2640254401Scy case IPPROTO_TCP : 2641254401Scy nflags = IPN_TCP; 2642254401Scy break; 2643254401Scy case IPPROTO_UDP : 2644254401Scy nflags = IPN_UDP; 2645254401Scy break; 2646254401Scy case IPPROTO_ICMPV6 : 2647254401Scy icmp6 = fin->fin_dp; 2648254401Scy 2649254401Scy /* 2650254401Scy * Apart from ECHO request and reply, all other 2651254401Scy * informational messages should not be translated 2652254401Scy * so as to keep IPv6 working. 2653254401Scy */ 2654254401Scy if (icmp6->icmp6_type > ICMP6_ECHO_REPLY) 2655254401Scy return 0; 2656254401Scy 2657254401Scy /* 2658254401Scy * This is an incoming packet, so the destination is 2659254401Scy * the icmp6_id and the source port equals 0 2660254401Scy */ 2661254401Scy if ((fin->fin_flx & FI_ICMPQUERY) != 0) 2662254401Scy nflags = IPN_ICMPQUERY; 2663254401Scy break; 2664254401Scy default : 2665254401Scy break; 2666254401Scy } 2667254401Scy 2668254401Scy if ((nflags & IPN_TCPUDP)) 2669254401Scy tcp = fin->fin_dp; 2670254401Scy } 2671254401Scy 2672254401Scy ipa = fin->fin_src6; 2673254401Scy 2674254401Scy READ_ENTER(&softc->ipf_nat); 2675254401Scy 2676254401Scy if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) && 2677254401Scy (nat = ipf_nat6_icmperror(fin, &nflags, NAT_OUTBOUND))) 2678254401Scy /*EMPTY*/; 2679254401Scy else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) 2680254401Scy natadd = 0; 2681254401Scy else if ((nat = ipf_nat6_outlookup(fin, nflags|NAT_SEARCH, 2682254401Scy (u_int)fin->fin_p, 2683254401Scy &fin->fin_src6.in6, 2684254401Scy &fin->fin_dst6.in6))) { 2685254401Scy nflags = nat->nat_flags; 2686254401Scy } else if (fin->fin_off == 0) { 2687254401Scy u_32_t hv, nmsk = 0; 2688254401Scy i6addr_t *msk; 2689254401Scy 2690254401Scy /* 2691254401Scy * If there is no current entry in the nat table for this IP#, 2692254401Scy * create one for it (if there is a matching rule). 2693254401Scy */ 2694254401Scymaskloop: 2695254401Scy msk = &softn->ipf_nat6_map_active_masks[nmsk]; 2696254401Scy IP6_AND(&ipa, msk, &iph); 2697254401Scy hv = NAT_HASH_FN6(&iph, 0, softn->ipf_nat_maprules_sz); 2698254401Scy for (np = softn->ipf_nat_map_rules[hv]; np; np = np->in_mnext) { 2699254401Scy if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) 2700254401Scy continue; 2701254401Scy if (np->in_v[0] != 6) 2702254401Scy continue; 2703254401Scy if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p)) 2704254401Scy continue; 2705254401Scy if ((np->in_flags & IPN_RF) && 2706254401Scy !(np->in_flags & nflags)) 2707254401Scy continue; 2708254401Scy if (np->in_flags & IPN_FILTER) { 2709254401Scy switch (ipf_nat6_match(fin, np)) 2710254401Scy { 2711254401Scy case 0 : 2712254401Scy continue; 2713254401Scy case -1 : 2714254401Scy rval = -1; 2715254401Scy goto outmatchfail; 2716254401Scy case 1 : 2717254401Scy default : 2718254401Scy break; 2719254401Scy } 2720254401Scy } else if (!IP6_MASKEQ(&ipa, &np->in_osrcmsk, 2721254401Scy &np->in_osrcip6)) 2722254401Scy continue; 2723254401Scy 2724254401Scy if ((fr != NULL) && 2725254401Scy !ipf_matchtag(&np->in_tag, &fr->fr_nattag)) 2726254401Scy continue; 2727254401Scy 2728254401Scy#ifdef IPF_V6_PROXIES 2729254401Scy if (np->in_plabel != -1) { 2730254401Scy if (((np->in_flags & IPN_FILTER) == 0) && 2731254401Scy (np->in_odport != fin->fin_data[1])) 2732254401Scy continue; 2733254401Scy if (appr_ok(fin, tcp, np) == 0) 2734254401Scy continue; 2735254401Scy } 2736254401Scy#endif 2737254401Scy 2738254401Scy if (np->in_flags & IPN_NO) { 2739254401Scy np->in_hits++; 2740254401Scy break; 2741254401Scy } 2742254401Scy 2743254401Scy MUTEX_ENTER(&softn->ipf_nat_new); 2744254401Scy nat = ipf_nat6_add(fin, np, NULL, nflags, NAT_OUTBOUND); 2745254401Scy MUTEX_EXIT(&softn->ipf_nat_new); 2746254401Scy if (nat != NULL) { 2747254401Scy np->in_hits++; 2748254401Scy break; 2749254401Scy } 2750254401Scy natfailed = -1; 2751254401Scy } 2752254401Scy if ((np == NULL) && (nmsk < softn->ipf_nat6_map_max)) { 2753254401Scy nmsk++; 2754254401Scy goto maskloop; 2755254401Scy } 2756254401Scy } 2757254401Scy 2758254401Scy if (nat != NULL) { 2759254401Scy rval = ipf_nat6_out(fin, nat, natadd, nflags); 2760254401Scy if (rval == 1) { 2761254401Scy MUTEX_ENTER(&nat->nat_lock); 2762254401Scy ipf_nat_update(fin, nat); 2763254401Scy nat->nat_bytes[1] += fin->fin_plen; 2764254401Scy nat->nat_pkts[1]++; 2765254401Scy MUTEX_EXIT(&nat->nat_lock); 2766254401Scy } 2767254401Scy } else 2768254401Scy rval = natfailed; 2769254401Scyoutmatchfail: 2770254401Scy RWLOCK_EXIT(&softc->ipf_nat); 2771254401Scy 2772254401Scy switch (rval) 2773254401Scy { 2774254401Scy case -1 : 2775254401Scy if (passp != NULL) { 2776254401Scy NBUMPSIDE6D(1, ns_drop); 2777254401Scy *passp = FR_BLOCK; 2778254401Scy fin->fin_reason = FRB_NATV6; 2779254401Scy } 2780254401Scy fin->fin_flx |= FI_BADNAT; 2781254401Scy NBUMPSIDE6D(1, ns_badnat); 2782254401Scy break; 2783254401Scy case 0 : 2784254401Scy NBUMPSIDE6D(1, ns_ignored); 2785254401Scy break; 2786254401Scy case 1 : 2787254401Scy NBUMPSIDE6D(1, ns_translated); 2788254401Scy break; 2789254401Scy } 2790254401Scy fin->fin_ifp = sifp; 2791254401Scy return rval; 2792254401Scy} 2793254401Scy 2794254401Scy/* ------------------------------------------------------------------------ */ 2795254401Scy/* Function: ipf_nat6_out */ 2796254401Scy/* Returns: int - -1 == packet failed NAT checks so block it, */ 2797254401Scy/* 1 == packet was successfully translated. */ 2798254401Scy/* Parameters: fin(I) - pointer to packet information */ 2799254401Scy/* nat(I) - pointer to NAT structure */ 2800254401Scy/* natadd(I) - flag indicating if it is safe to add frag cache */ 2801254401Scy/* nflags(I) - NAT flags set for this packet */ 2802254401Scy/* */ 2803254401Scy/* Translate a packet coming "out" on an interface. */ 2804254401Scy/* ------------------------------------------------------------------------ */ 2805254401Scystatic int 2806254401Scyipf_nat6_out(fin, nat, natadd, nflags) 2807254401Scy fr_info_t *fin; 2808254401Scy nat_t *nat; 2809254401Scy int natadd; 2810254401Scy u_32_t nflags; 2811254401Scy{ 2812254401Scy ipf_main_softc_t *softc = fin->fin_main_soft; 2813254401Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 2814254401Scy struct icmp6_hdr *icmp6; 2815254401Scy tcphdr_t *tcp; 2816254401Scy ipnat_t *np; 2817254401Scy int skip; 2818254401Scy int i; 2819254401Scy 2820254401Scy tcp = NULL; 2821254401Scy icmp6 = NULL; 2822254401Scy np = nat->nat_ptr; 2823254401Scy 2824254401Scy if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL)) 2825254401Scy (void) ipf_frag_natnew(softc, fin, 0, nat); 2826254401Scy 2827254401Scy /* 2828254401Scy * Address assignment is after the checksum modification because 2829254401Scy * we are using the address in the packet for determining the 2830254401Scy * correct checksum offset (the ICMP error could be coming from 2831254401Scy * anyone...) 2832254401Scy */ 2833254401Scy switch (nat->nat_dir) 2834254401Scy { 2835254401Scy case NAT_OUTBOUND : 2836254401Scy fin->fin_ip6->ip6_src = nat->nat_nsrc6.in6; 2837254401Scy fin->fin_src6 = nat->nat_nsrc6; 2838254401Scy fin->fin_ip6->ip6_dst = nat->nat_ndst6.in6; 2839254401Scy fin->fin_dst6 = nat->nat_ndst6; 2840254401Scy break; 2841254401Scy 2842254401Scy case NAT_INBOUND : 2843254401Scy fin->fin_ip6->ip6_src = nat->nat_odst6.in6; 2844254401Scy fin->fin_src6 = nat->nat_ndst6; 2845254401Scy fin->fin_ip6->ip6_dst = nat->nat_osrc6.in6; 2846254401Scy fin->fin_dst6 = nat->nat_nsrc6; 2847254401Scy break; 2848254401Scy 2849254401Scy case NAT_DIVERTIN : 2850254401Scy { 2851254401Scy mb_t *m; 2852254401Scy 2853254401Scy skip = ipf_nat6_decap(fin, nat); 2854254401Scy if (skip <= 0) { 2855254401Scy NBUMPSIDE6D(1, ns_decap_fail); 2856254401Scy return -1; 2857254401Scy } 2858254401Scy 2859254401Scy m = fin->fin_m; 2860254401Scy 2861369272Scy#if SOLARIS && defined(_KERNEL) 2862254401Scy m->b_rptr += skip; 2863254401Scy#else 2864254401Scy m->m_data += skip; 2865254401Scy m->m_len -= skip; 2866254401Scy 2867254401Scy# ifdef M_PKTHDR 2868254401Scy if (m->m_flags & M_PKTHDR) 2869254401Scy m->m_pkthdr.len -= skip; 2870254401Scy# endif 2871254401Scy#endif 2872254401Scy 2873254401Scy MUTEX_ENTER(&nat->nat_lock); 2874254401Scy ipf_nat_update(fin, nat); 2875254401Scy MUTEX_EXIT(&nat->nat_lock); 2876254401Scy fin->fin_flx |= FI_NATED; 2877254401Scy if (np != NULL && np->in_tag.ipt_num[0] != 0) 2878254401Scy fin->fin_nattag = &np->in_tag; 2879254401Scy return 1; 2880254401Scy /* NOTREACHED */ 2881254401Scy } 2882254401Scy 2883254401Scy case NAT_DIVERTOUT : 2884254401Scy { 2885254401Scy udphdr_t *uh; 2886254401Scy ip6_t *ip6; 2887254401Scy mb_t *m; 2888254401Scy 2889254401Scy m = M_DUP(np->in_divmp); 2890254401Scy if (m == NULL) { 2891254401Scy NBUMPSIDE6D(1, ns_divert_dup); 2892254401Scy return -1; 2893254401Scy } 2894254401Scy 2895254401Scy ip6 = MTOD(m, ip6_t *); 2896254401Scy 2897254401Scy ip6->ip6_plen = htons(fin->fin_plen + 8); 2898254401Scy 2899254401Scy uh = (udphdr_t *)(ip6 + 1); 2900254401Scy uh->uh_ulen = htons(fin->fin_plen); 2901254401Scy 2902254401Scy PREP_MB_T(fin, m); 2903254401Scy 2904254401Scy fin->fin_ip6 = ip6; 2905254401Scy fin->fin_plen += sizeof(ip6_t) + 8; /* UDP + new IPv4 hdr */ 2906254401Scy fin->fin_dlen += sizeof(ip6_t) + 8; /* UDP + old IPv4 hdr */ 2907254401Scy 2908254401Scy nflags &= ~IPN_TCPUDPICMP; 2909254401Scy 2910254401Scy break; 2911254401Scy } 2912254401Scy 2913254401Scy default : 2914254401Scy break; 2915254401Scy } 2916254401Scy 2917254401Scy if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 2918254401Scy u_short *csump; 2919254401Scy 2920254401Scy if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) { 2921254401Scy tcp = fin->fin_dp; 2922254401Scy 2923254401Scy switch (nat->nat_dir) 2924254401Scy { 2925254401Scy case NAT_OUTBOUND : 2926254401Scy tcp->th_sport = nat->nat_nsport; 2927254401Scy fin->fin_data[0] = ntohs(nat->nat_nsport); 2928254401Scy tcp->th_dport = nat->nat_ndport; 2929254401Scy fin->fin_data[1] = ntohs(nat->nat_ndport); 2930254401Scy break; 2931254401Scy 2932254401Scy case NAT_INBOUND : 2933254401Scy tcp->th_sport = nat->nat_odport; 2934254401Scy fin->fin_data[0] = ntohs(nat->nat_odport); 2935254401Scy tcp->th_dport = nat->nat_osport; 2936254401Scy fin->fin_data[1] = ntohs(nat->nat_osport); 2937254401Scy break; 2938254401Scy } 2939254401Scy } 2940254401Scy 2941254401Scy if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) { 2942254401Scy icmp6 = fin->fin_dp; 2943254401Scy icmp6->icmp6_id = nat->nat_nicmpid; 2944254401Scy } 2945254401Scy 2946254401Scy csump = ipf_nat_proto(fin, nat, nflags); 2947254401Scy 2948254401Scy /* 2949254401Scy * The above comments do not hold for layer 4 (or higher) 2950254401Scy * checksums... 2951254401Scy */ 2952254401Scy if (csump != NULL) { 2953254401Scy if (nat->nat_dir == NAT_OUTBOUND) 2954254401Scy ipf_fix_outcksum(fin->fin_cksum, csump, 2955254401Scy nat->nat_sumd[0], 2956254401Scy nat->nat_sumd[1] + 2957254401Scy fin->fin_dlen); 2958254401Scy else 2959254401Scy ipf_fix_incksum(fin->fin_cksum, csump, 2960254401Scy nat->nat_sumd[0], 2961254401Scy nat->nat_sumd[1] + 2962254401Scy fin->fin_dlen); 2963254401Scy } 2964254401Scy } 2965254401Scy 2966254401Scy ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); 2967254401Scy /* ------------------------------------------------------------- */ 2968254401Scy /* A few quick notes: */ 2969254401Scy /* Following are test conditions prior to calling the */ 2970254401Scy /* ipf_proxy_check routine. */ 2971254401Scy /* */ 2972254401Scy /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 2973254401Scy /* with a redirect rule, we attempt to match the packet's */ 2974254401Scy /* source port against in_dport, otherwise we'd compare the */ 2975254401Scy /* packet's destination. */ 2976254401Scy /* ------------------------------------------------------------- */ 2977254401Scy if ((np != NULL) && (np->in_apr != NULL)) { 2978254401Scy i = ipf_proxy_check(fin, nat); 2979369541Sgit2svn if (i == -1) { 2980254401Scy NBUMPSIDE6D(1, ns_ipf_proxy_fail); 2981254401Scy } 2982254401Scy } else { 2983254401Scy i = 1; 2984254401Scy } 2985254401Scy fin->fin_flx |= FI_NATED; 2986254401Scy return i; 2987254401Scy} 2988254401Scy 2989254401Scy 2990254401Scy/* ------------------------------------------------------------------------ */ 2991254401Scy/* Function: ipf_nat6_checkin */ 2992254401Scy/* Returns: int - -1 == packet failed NAT checks so block it, */ 2993254401Scy/* 0 == no packet translation occurred, */ 2994254401Scy/* 1 == packet was successfully translated. */ 2995254401Scy/* Parameters: fin(I) - pointer to packet information */ 2996254401Scy/* passp(I) - pointer to filtering result flags */ 2997254401Scy/* */ 2998254401Scy/* Check to see if an incoming packet should be changed. ICMP packets are */ 2999254401Scy/* first checked to see if they match an existing entry (if an error), */ 3000254401Scy/* otherwise a search of the current NAT table is made. If neither results */ 3001254401Scy/* in a match then a search for a matching NAT rule is made. Create a new */ 3002254401Scy/* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 3003254401Scy/* packet header(s) as required. */ 3004254401Scy/* ------------------------------------------------------------------------ */ 3005254401Scyint 3006254401Scyipf_nat6_checkin(fin, passp) 3007254401Scy fr_info_t *fin; 3008254401Scy u_32_t *passp; 3009254401Scy{ 3010254401Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3011254401Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 3012254401Scy struct icmp6_hdr *icmp6; 3013254401Scy u_int nflags, natadd; 3014254401Scy int rval, natfailed; 3015254401Scy struct ifnet *ifp; 3016254401Scy i6addr_t ipa, iph; 3017254401Scy tcphdr_t *tcp; 3018254401Scy u_short dport; 3019254401Scy ipnat_t *np; 3020254401Scy nat_t *nat; 3021254401Scy 3022254401Scy if (softn->ipf_nat_stats.ns_rules == 0 || softn->ipf_nat_lock != 0) 3023254401Scy return 0; 3024254401Scy 3025254401Scy tcp = NULL; 3026254401Scy icmp6 = NULL; 3027254401Scy dport = 0; 3028254401Scy natadd = 1; 3029254401Scy nflags = 0; 3030254401Scy natfailed = 0; 3031254401Scy ifp = fin->fin_ifp; 3032254401Scy 3033254401Scy if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 3034254401Scy switch (fin->fin_p) 3035254401Scy { 3036254401Scy case IPPROTO_TCP : 3037254401Scy nflags = IPN_TCP; 3038254401Scy break; 3039254401Scy case IPPROTO_UDP : 3040254401Scy nflags = IPN_UDP; 3041254401Scy break; 3042254401Scy case IPPROTO_ICMPV6 : 3043254401Scy icmp6 = fin->fin_dp; 3044254401Scy 3045254401Scy /* 3046254401Scy * Apart from ECHO request and reply, all other 3047254401Scy * informational messages should not be translated 3048254401Scy * so as to keep IPv6 working. 3049254401Scy */ 3050254401Scy if (icmp6->icmp6_type > ICMP6_ECHO_REPLY) 3051254401Scy return 0; 3052254401Scy 3053254401Scy /* 3054254401Scy * This is an incoming packet, so the destination is 3055254401Scy * the icmp6_id and the source port equals 0 3056254401Scy */ 3057254401Scy if ((fin->fin_flx & FI_ICMPQUERY) != 0) { 3058254401Scy nflags = IPN_ICMPQUERY; 3059254401Scy dport = icmp6->icmp6_id; 3060254401Scy } break; 3061254401Scy default : 3062254401Scy break; 3063254401Scy } 3064254401Scy 3065254401Scy if ((nflags & IPN_TCPUDP)) { 3066254401Scy tcp = fin->fin_dp; 3067254401Scy dport = fin->fin_data[1]; 3068254401Scy } 3069254401Scy } 3070254401Scy 3071254401Scy ipa = fin->fin_dst6; 3072254401Scy 3073254401Scy READ_ENTER(&softc->ipf_nat); 3074254401Scy 3075254401Scy if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) && 3076254401Scy (nat = ipf_nat6_icmperror(fin, &nflags, NAT_INBOUND))) 3077254401Scy /*EMPTY*/; 3078254401Scy else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) 3079254401Scy natadd = 0; 3080254401Scy else if ((nat = ipf_nat6_inlookup(fin, nflags|NAT_SEARCH, 3081254401Scy (u_int)fin->fin_p, 3082254401Scy &fin->fin_src6.in6, &ipa.in6))) { 3083254401Scy nflags = nat->nat_flags; 3084254401Scy } else if (fin->fin_off == 0) { 3085254401Scy u_32_t hv, rmsk = 0; 3086254401Scy i6addr_t *msk; 3087254401Scy 3088254401Scy /* 3089254401Scy * If there is no current entry in the nat table for this IP#, 3090254401Scy * create one for it (if there is a matching rule). 3091254401Scy */ 3092254401Scymaskloop: 3093254401Scy msk = &softn->ipf_nat6_rdr_active_masks[rmsk]; 3094254401Scy IP6_AND(&ipa, msk, &iph); 3095254401Scy hv = NAT_HASH_FN6(&iph, 0, softn->ipf_nat_rdrrules_sz); 3096254401Scy for (np = softn->ipf_nat_rdr_rules[hv]; np; np = np->in_rnext) { 3097254401Scy if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) 3098254401Scy continue; 3099254401Scy if (np->in_v[0] != 6) 3100254401Scy continue; 3101254401Scy if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p)) 3102254401Scy continue; 3103254401Scy if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) 3104254401Scy continue; 3105254401Scy if (np->in_flags & IPN_FILTER) { 3106254401Scy switch (ipf_nat6_match(fin, np)) 3107254401Scy { 3108254401Scy case 0 : 3109254401Scy continue; 3110254401Scy case -1 : 3111254401Scy rval = -1; 3112254401Scy goto inmatchfail; 3113254401Scy case 1 : 3114254401Scy default : 3115254401Scy break; 3116254401Scy } 3117254401Scy } else { 3118254401Scy if (!IP6_MASKEQ(&ipa, &np->in_odstmsk6, 3119254401Scy &np->in_odstip6)) { 3120254401Scy continue; 3121254401Scy } 3122254401Scy if (np->in_odport && 3123254401Scy ((np->in_dtop < dport) || 3124254401Scy (dport < np->in_odport))) 3125254401Scy continue; 3126254401Scy } 3127254401Scy 3128254401Scy#ifdef IPF_V6_PROXIES 3129254401Scy if (np->in_plabel != -1) { 3130254401Scy if (!appr_ok(fin, tcp, np)) { 3131254401Scy continue; 3132254401Scy } 3133254401Scy } 3134254401Scy#endif 3135254401Scy 3136254401Scy if (np->in_flags & IPN_NO) { 3137254401Scy np->in_hits++; 3138254401Scy break; 3139254401Scy } 3140254401Scy 3141254401Scy MUTEX_ENTER(&softn->ipf_nat_new); 3142254401Scy nat = ipf_nat6_add(fin, np, NULL, nflags, NAT_INBOUND); 3143254401Scy MUTEX_EXIT(&softn->ipf_nat_new); 3144254401Scy if (nat != NULL) { 3145254401Scy np->in_hits++; 3146254401Scy break; 3147254401Scy } 3148254401Scy natfailed = -1; 3149254401Scy } 3150254401Scy 3151254401Scy if ((np == NULL) && (rmsk < softn->ipf_nat6_rdr_max)) { 3152254401Scy rmsk++; 3153254401Scy goto maskloop; 3154254401Scy } 3155254401Scy } 3156254401Scy if (nat != NULL) { 3157254401Scy rval = ipf_nat6_in(fin, nat, natadd, nflags); 3158254401Scy if (rval == 1) { 3159254401Scy MUTEX_ENTER(&nat->nat_lock); 3160254401Scy ipf_nat_update(fin, nat); 3161254401Scy nat->nat_bytes[0] += fin->fin_plen; 3162254401Scy nat->nat_pkts[0]++; 3163254401Scy MUTEX_EXIT(&nat->nat_lock); 3164254401Scy } 3165254401Scy } else 3166254401Scy rval = natfailed; 3167254401Scyinmatchfail: 3168254401Scy RWLOCK_EXIT(&softc->ipf_nat); 3169254401Scy 3170254401Scy switch (rval) 3171254401Scy { 3172254401Scy case -1 : 3173254401Scy if (passp != NULL) { 3174254401Scy NBUMPSIDE6D(0, ns_drop); 3175254401Scy *passp = FR_BLOCK; 3176254401Scy fin->fin_reason = FRB_NATV6; 3177254401Scy } 3178254401Scy fin->fin_flx |= FI_BADNAT; 3179254401Scy NBUMPSIDE6D(0, ns_badnat); 3180254401Scy break; 3181254401Scy case 0 : 3182254401Scy NBUMPSIDE6D(0, ns_ignored); 3183254401Scy break; 3184254401Scy case 1 : 3185254401Scy NBUMPSIDE6D(0, ns_translated); 3186254401Scy break; 3187254401Scy } 3188254401Scy return rval; 3189254401Scy} 3190254401Scy 3191254401Scy 3192254401Scy/* ------------------------------------------------------------------------ */ 3193254401Scy/* Function: ipf_nat6_in */ 3194254401Scy/* Returns: int - -1 == packet failed NAT checks so block it, */ 3195254401Scy/* 1 == packet was successfully translated. */ 3196254401Scy/* Parameters: fin(I) - pointer to packet information */ 3197254401Scy/* nat(I) - pointer to NAT structure */ 3198254401Scy/* natadd(I) - flag indicating if it is safe to add frag cache */ 3199254401Scy/* nflags(I) - NAT flags set for this packet */ 3200254401Scy/* Locks Held: (READ) */ 3201254401Scy/* */ 3202254401Scy/* Translate a packet coming "in" on an interface. */ 3203254401Scy/* ------------------------------------------------------------------------ */ 3204254401Scystatic int 3205254401Scyipf_nat6_in(fin, nat, natadd, nflags) 3206254401Scy fr_info_t *fin; 3207254401Scy nat_t *nat; 3208254401Scy int natadd; 3209254401Scy u_32_t nflags; 3210254401Scy{ 3211254401Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3212254401Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 3213254401Scy struct icmp6_hdr *icmp6; 3214254401Scy u_short *csump; 3215254401Scy tcphdr_t *tcp; 3216254401Scy ipnat_t *np; 3217254401Scy int skip; 3218254401Scy int i; 3219254401Scy 3220254401Scy tcp = NULL; 3221254401Scy csump = NULL; 3222254401Scy np = nat->nat_ptr; 3223254401Scy fin->fin_fr = nat->nat_fr; 3224254401Scy 3225254401Scy if (np != NULL) { 3226254401Scy if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) 3227254401Scy (void) ipf_frag_natnew(softc, fin, 0, nat); 3228254401Scy 3229254401Scy /* ------------------------------------------------------------- */ 3230254401Scy /* A few quick notes: */ 3231254401Scy /* Following are test conditions prior to calling the */ 3232254401Scy /* ipf_proxy_check routine. */ 3233254401Scy /* */ 3234254401Scy /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 3235254401Scy /* with a map rule, we attempt to match the packet's */ 3236254401Scy /* source port against in_dport, otherwise we'd compare the */ 3237254401Scy /* packet's destination. */ 3238254401Scy /* ------------------------------------------------------------- */ 3239254401Scy if (np->in_apr != NULL) { 3240254401Scy i = ipf_proxy_check(fin, nat); 3241254401Scy if (i == -1) { 3242254401Scy NBUMPSIDE6D(0, ns_ipf_proxy_fail); 3243254401Scy return -1; 3244254401Scy } 3245254401Scy } 3246254401Scy } 3247254401Scy 3248254401Scy ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); 3249254401Scy 3250254401Scy /* 3251254401Scy * Fix up checksums, not by recalculating them, but 3252254401Scy * simply computing adjustments. 3253254401Scy * Why only do this for some platforms on inbound packets ? 3254254401Scy * Because for those that it is done, IP processing is yet to happen 3255254401Scy * and so the IPv4 header checksum has not yet been evaluated. 3256254401Scy * Perhaps it should always be done for the benefit of things like 3257254401Scy * fast forwarding (so that it doesn't need to be recomputed) but with 3258254401Scy * header checksum offloading, perhaps it is a moot point. 3259254401Scy */ 3260254401Scy 3261254401Scy switch (nat->nat_dir) 3262254401Scy { 3263254401Scy case NAT_INBOUND : 3264254401Scy if ((fin->fin_flx & FI_ICMPERR) == 0) { 3265254401Scy fin->fin_ip6->ip6_src = nat->nat_nsrc6.in6; 3266254401Scy fin->fin_src6 = nat->nat_nsrc6; 3267254401Scy } 3268254401Scy fin->fin_ip6->ip6_dst = nat->nat_ndst6.in6; 3269254401Scy fin->fin_dst6 = nat->nat_ndst6; 3270254401Scy break; 3271254401Scy 3272254401Scy case NAT_OUTBOUND : 3273254401Scy if ((fin->fin_flx & FI_ICMPERR) == 0) { 3274254401Scy fin->fin_ip6->ip6_src = nat->nat_odst6.in6; 3275254401Scy fin->fin_src6 = nat->nat_odst6; 3276254401Scy } 3277254401Scy fin->fin_ip6->ip6_dst = nat->nat_osrc6.in6; 3278254401Scy fin->fin_dst6 = nat->nat_osrc6; 3279254401Scy break; 3280254401Scy 3281254401Scy case NAT_DIVERTIN : 3282254401Scy { 3283254401Scy udphdr_t *uh; 3284254401Scy ip6_t *ip6; 3285254401Scy mb_t *m; 3286254401Scy 3287254401Scy m = M_DUP(np->in_divmp); 3288254401Scy if (m == NULL) { 3289254401Scy NBUMPSIDE6D(0, ns_divert_dup); 3290254401Scy return -1; 3291254401Scy } 3292254401Scy 3293254401Scy ip6 = MTOD(m, ip6_t *); 3294254401Scy ip6->ip6_plen = htons(fin->fin_plen + sizeof(udphdr_t)); 3295254401Scy 3296254401Scy uh = (udphdr_t *)(ip6 + 1); 3297254401Scy uh->uh_ulen = ntohs(fin->fin_plen); 3298254401Scy 3299254401Scy PREP_MB_T(fin, m); 3300254401Scy 3301254401Scy fin->fin_ip6 = ip6; 3302254401Scy fin->fin_plen += sizeof(ip6_t) + 8; /* UDP + new IPv6 hdr */ 3303254401Scy fin->fin_dlen += sizeof(ip6_t) + 8; /* UDP + old IPv6 hdr */ 3304254401Scy 3305254401Scy nflags &= ~IPN_TCPUDPICMP; 3306254401Scy 3307254401Scy break; 3308254401Scy } 3309254401Scy 3310254401Scy case NAT_DIVERTOUT : 3311254401Scy { 3312254401Scy mb_t *m; 3313254401Scy 3314254401Scy skip = ipf_nat6_decap(fin, nat); 3315254401Scy if (skip <= 0) { 3316254401Scy NBUMPSIDE6D(0, ns_decap_fail); 3317254401Scy return -1; 3318254401Scy } 3319254401Scy 3320254401Scy m = fin->fin_m; 3321254401Scy 3322369272Scy#if SOLARIS && defined(_KERNEL) 3323254401Scy m->b_rptr += skip; 3324254401Scy#else 3325254401Scy m->m_data += skip; 3326254401Scy m->m_len -= skip; 3327254401Scy 3328254401Scy# ifdef M_PKTHDR 3329254401Scy if (m->m_flags & M_PKTHDR) 3330254401Scy m->m_pkthdr.len -= skip; 3331254401Scy# endif 3332254401Scy#endif 3333254401Scy 3334254401Scy ipf_nat_update(fin, nat); 3335254401Scy fin->fin_flx |= FI_NATED; 3336254401Scy if (np != NULL && np->in_tag.ipt_num[0] != 0) 3337254401Scy fin->fin_nattag = &np->in_tag; 3338254401Scy return 1; 3339254401Scy /* NOTREACHED */ 3340254401Scy } 3341254401Scy } 3342254401Scy if (nflags & IPN_TCPUDP) 3343254401Scy tcp = fin->fin_dp; 3344254401Scy 3345254401Scy if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 3346254401Scy if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) { 3347254401Scy switch (nat->nat_dir) 3348254401Scy { 3349254401Scy case NAT_INBOUND : 3350254401Scy tcp->th_sport = nat->nat_nsport; 3351254401Scy fin->fin_data[0] = ntohs(nat->nat_nsport); 3352254401Scy tcp->th_dport = nat->nat_ndport; 3353254401Scy fin->fin_data[1] = ntohs(nat->nat_ndport); 3354254401Scy break; 3355254401Scy 3356254401Scy case NAT_OUTBOUND : 3357254401Scy tcp->th_sport = nat->nat_odport; 3358254401Scy fin->fin_data[0] = ntohs(nat->nat_odport); 3359254401Scy tcp->th_dport = nat->nat_osport; 3360254401Scy fin->fin_data[1] = ntohs(nat->nat_osport); 3361254401Scy break; 3362254401Scy } 3363254401Scy } 3364254401Scy 3365254401Scy 3366254401Scy if ((nat->nat_odport != 0) && (nflags & IPN_ICMPQUERY)) { 3367254401Scy icmp6 = fin->fin_dp; 3368254401Scy 3369254401Scy icmp6->icmp6_id = nat->nat_nicmpid; 3370254401Scy } 3371254401Scy 3372254401Scy csump = ipf_nat_proto(fin, nat, nflags); 3373254401Scy } 3374254401Scy 3375254401Scy /* 3376254401Scy * The above comments do not hold for layer 4 (or higher) checksums... 3377254401Scy */ 3378254401Scy if (csump != NULL) { 3379254401Scy if (nat->nat_dir == NAT_OUTBOUND) 3380254401Scy ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0); 3381254401Scy else 3382254401Scy ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0); 3383254401Scy } 3384254401Scy fin->fin_flx |= FI_NATED; 3385254401Scy if (np != NULL && np->in_tag.ipt_num[0] != 0) 3386254401Scy fin->fin_nattag = &np->in_tag; 3387254401Scy return 1; 3388254401Scy} 3389254401Scy 3390254401Scy 3391254401Scy/* ------------------------------------------------------------------------ */ 3392254401Scy/* Function: ipf_nat6_newrewrite */ 3393254401Scy/* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ 3394254401Scy/* allow rule to be moved if IPN_ROUNDR is set. */ 3395254401Scy/* Parameters: fin(I) - pointer to packet information */ 3396254401Scy/* nat(I) - pointer to NAT entry */ 3397254401Scy/* ni(I) - pointer to structure with misc. information needed */ 3398254401Scy/* to create new NAT entry. */ 3399254401Scy/* Write Lock: ipf_nat */ 3400254401Scy/* */ 3401254401Scy/* This function is responsible for setting up an active NAT session where */ 3402254401Scy/* we are changing both the source and destination parameters at the same */ 3403254401Scy/* time. The loop in here works differently to elsewhere - each iteration */ 3404254401Scy/* is responsible for changing a single parameter that can be incremented. */ 3405254401Scy/* So one pass may increase the source IP#, next source port, next dest. IP#*/ 3406254401Scy/* and the last destination port for a total of 4 iterations to try each. */ 3407254401Scy/* This is done to try and exhaustively use the translation space available.*/ 3408254401Scy/* ------------------------------------------------------------------------ */ 3409254401Scyint 3410254401Scyipf_nat6_newrewrite(fin, nat, nai) 3411254401Scy fr_info_t *fin; 3412254401Scy nat_t *nat; 3413254401Scy natinfo_t *nai; 3414254401Scy{ 3415254401Scy int src_search = 1; 3416254401Scy int dst_search = 1; 3417254401Scy fr_info_t frnat; 3418254401Scy u_32_t flags; 3419254401Scy u_short swap; 3420254401Scy ipnat_t *np; 3421254401Scy nat_t *natl; 3422254401Scy int l = 0; 3423254401Scy int changed; 3424254401Scy 3425254401Scy natl = NULL; 3426254401Scy changed = -1; 3427254401Scy np = nai->nai_np; 3428254401Scy flags = nat->nat_flags; 3429254401Scy bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); 3430254401Scy 3431254401Scy nat->nat_hm = NULL; 3432254401Scy 3433254401Scy do { 3434254401Scy changed = -1; 3435254401Scy /* TRACE (l, src_search, dst_search, np) */ 3436254401Scy 3437254401Scy if ((src_search == 0) && (np->in_spnext == 0) && 3438254401Scy (dst_search == 0) && (np->in_dpnext == 0)) { 3439254401Scy if (l > 0) 3440254401Scy return -1; 3441254401Scy } 3442254401Scy 3443254401Scy /* 3444254401Scy * Find a new source address 3445254401Scy */ 3446254401Scy if (ipf_nat6_nextaddr(fin, &np->in_nsrc, &frnat.fin_src6, 3447254401Scy &frnat.fin_src6) == -1) { 3448254401Scy return -1; 3449254401Scy } 3450254401Scy 3451254401Scy if (IP6_ISZERO(&np->in_nsrcip6) && 3452254401Scy IP6_ISONES(&np->in_nsrcmsk6)) { 3453254401Scy src_search = 0; 3454254401Scy if (np->in_stepnext == 0) 3455254401Scy np->in_stepnext = 1; 3456254401Scy 3457254401Scy } else if (IP6_ISZERO(&np->in_nsrcip6) && 3458254401Scy IP6_ISZERO(&np->in_nsrcmsk6)) { 3459254401Scy src_search = 0; 3460254401Scy if (np->in_stepnext == 0) 3461254401Scy np->in_stepnext = 1; 3462254401Scy 3463254401Scy } else if (IP6_ISONES(&np->in_nsrcmsk)) { 3464254401Scy src_search = 0; 3465254401Scy if (np->in_stepnext == 0) 3466254401Scy np->in_stepnext = 1; 3467254401Scy 3468254401Scy } else if (!IP6_ISONES(&np->in_nsrcmsk6)) { 3469254401Scy if (np->in_stepnext == 0 && changed == -1) { 3470254401Scy IP6_INC(&np->in_snip); 3471254401Scy np->in_stepnext++; 3472254401Scy changed = 0; 3473254401Scy } 3474254401Scy } 3475254401Scy 3476254401Scy if ((flags & IPN_TCPUDPICMP) != 0) { 3477254401Scy if (np->in_spnext != 0) 3478254401Scy frnat.fin_data[0] = np->in_spnext; 3479254401Scy 3480254401Scy /* 3481254401Scy * Standard port translation. Select next port. 3482254401Scy */ 3483254401Scy if ((flags & IPN_FIXEDSPORT) != 0) { 3484254401Scy np->in_stepnext = 2; 3485254401Scy } else if ((np->in_stepnext == 1) && 3486254401Scy (changed == -1) && (natl != NULL)) { 3487254401Scy np->in_spnext++; 3488254401Scy np->in_stepnext++; 3489254401Scy changed = 1; 3490254401Scy if (np->in_spnext > np->in_spmax) 3491254401Scy np->in_spnext = np->in_spmin; 3492254401Scy } 3493254401Scy } else { 3494254401Scy np->in_stepnext = 2; 3495254401Scy } 3496254401Scy np->in_stepnext &= 0x3; 3497254401Scy 3498254401Scy /* 3499254401Scy * Find a new destination address 3500254401Scy */ 3501254401Scy /* TRACE (fin, np, l, frnat) */ 3502254401Scy 3503254401Scy if (ipf_nat6_nextaddr(fin, &np->in_ndst, &frnat.fin_dst6, 3504254401Scy &frnat.fin_dst6) == -1) 3505254401Scy return -1; 3506254401Scy 3507254401Scy if (IP6_ISZERO(&np->in_ndstip6) && 3508254401Scy IP6_ISONES(&np->in_ndstmsk6)) { 3509254401Scy dst_search = 0; 3510254401Scy if (np->in_stepnext == 2) 3511254401Scy np->in_stepnext = 3; 3512254401Scy 3513254401Scy } else if (IP6_ISZERO(&np->in_ndstip6) && 3514254401Scy IP6_ISZERO(&np->in_ndstmsk6)) { 3515254401Scy dst_search = 0; 3516254401Scy if (np->in_stepnext == 2) 3517254401Scy np->in_stepnext = 3; 3518254401Scy 3519254401Scy } else if (IP6_ISONES(&np->in_ndstmsk6)) { 3520254401Scy dst_search = 0; 3521254401Scy if (np->in_stepnext == 2) 3522254401Scy np->in_stepnext = 3; 3523254401Scy 3524254401Scy } else if (!IP6_ISONES(&np->in_ndstmsk6)) { 3525254401Scy if ((np->in_stepnext == 2) && (changed == -1) && 3526254401Scy (natl != NULL)) { 3527254401Scy changed = 2; 3528254401Scy np->in_stepnext++; 3529254401Scy IP6_INC(&np->in_dnip6); 3530254401Scy } 3531254401Scy } 3532254401Scy 3533254401Scy if ((flags & IPN_TCPUDPICMP) != 0) { 3534254401Scy if (np->in_dpnext != 0) 3535254401Scy frnat.fin_data[1] = np->in_dpnext; 3536254401Scy 3537254401Scy /* 3538254401Scy * Standard port translation. Select next port. 3539254401Scy */ 3540254401Scy if ((flags & IPN_FIXEDDPORT) != 0) { 3541254401Scy np->in_stepnext = 0; 3542254401Scy } else if (np->in_stepnext == 3 && changed == -1) { 3543254401Scy np->in_dpnext++; 3544254401Scy np->in_stepnext++; 3545254401Scy changed = 3; 3546254401Scy if (np->in_dpnext > np->in_dpmax) 3547254401Scy np->in_dpnext = np->in_dpmin; 3548254401Scy } 3549254401Scy } else { 3550254401Scy if (np->in_stepnext == 3) 3551254401Scy np->in_stepnext = 0; 3552254401Scy } 3553254401Scy 3554254401Scy /* TRACE (frnat) */ 3555254401Scy 3556254401Scy /* 3557254401Scy * Here we do a lookup of the connection as seen from 3558254401Scy * the outside. If an IP# pair already exists, try 3559254401Scy * again. So if you have A->B becomes C->B, you can 3560254401Scy * also have D->E become C->E but not D->B causing 3561254401Scy * another C->B. Also take protocol and ports into 3562254401Scy * account when determining whether a pre-existing 3563254401Scy * NAT setup will cause an external conflict where 3564254401Scy * this is appropriate. 3565254401Scy * 3566254401Scy * fin_data[] is swapped around because we are doing a 3567254401Scy * lookup of the packet is if it were moving in the opposite 3568254401Scy * direction of the one we are working with now. 3569254401Scy */ 3570254401Scy if (flags & IPN_TCPUDP) { 3571254401Scy swap = frnat.fin_data[0]; 3572254401Scy frnat.fin_data[0] = frnat.fin_data[1]; 3573254401Scy frnat.fin_data[1] = swap; 3574254401Scy } 3575254401Scy if (fin->fin_out == 1) { 3576254401Scy natl = ipf_nat6_inlookup(&frnat, 3577254401Scy flags & ~(SI_WILDP|NAT_SEARCH), 3578254401Scy (u_int)frnat.fin_p, 3579254401Scy &frnat.fin_dst6.in6, 3580254401Scy &frnat.fin_src6.in6); 3581254401Scy 3582254401Scy } else { 3583254401Scy natl = ipf_nat6_outlookup(&frnat, 3584254401Scy flags & ~(SI_WILDP|NAT_SEARCH), 3585254401Scy (u_int)frnat.fin_p, 3586254401Scy &frnat.fin_dst6.in6, 3587254401Scy &frnat.fin_src6.in6); 3588254401Scy } 3589254401Scy if (flags & IPN_TCPUDP) { 3590254401Scy swap = frnat.fin_data[0]; 3591254401Scy frnat.fin_data[0] = frnat.fin_data[1]; 3592254401Scy frnat.fin_data[1] = swap; 3593254401Scy } 3594254401Scy 3595254401Scy /* TRACE natl, in_stepnext, l */ 3596254401Scy 3597254401Scy if ((natl != NULL) && (l > 8)) /* XXX 8 is arbitrary */ 3598254401Scy return -1; 3599254401Scy 3600254401Scy np->in_stepnext &= 0x3; 3601254401Scy 3602254401Scy l++; 3603254401Scy changed = -1; 3604254401Scy } while (natl != NULL); 3605254401Scy nat->nat_osrc6 = fin->fin_src6; 3606254401Scy nat->nat_odst6 = fin->fin_dst6; 3607254401Scy nat->nat_nsrc6 = frnat.fin_src6; 3608254401Scy nat->nat_ndst6 = frnat.fin_dst6; 3609254401Scy 3610254401Scy if ((flags & IPN_TCPUDP) != 0) { 3611254401Scy nat->nat_osport = htons(fin->fin_data[0]); 3612254401Scy nat->nat_odport = htons(fin->fin_data[1]); 3613254401Scy nat->nat_nsport = htons(frnat.fin_data[0]); 3614254401Scy nat->nat_ndport = htons(frnat.fin_data[1]); 3615254401Scy } else if ((flags & IPN_ICMPQUERY) != 0) { 3616254401Scy nat->nat_oicmpid = fin->fin_data[1]; 3617254401Scy nat->nat_nicmpid = frnat.fin_data[1]; 3618254401Scy } 3619254401Scy 3620254401Scy return 0; 3621254401Scy} 3622254401Scy 3623254401Scy 3624254401Scy/* ------------------------------------------------------------------------ */ 3625254401Scy/* Function: ipf_nat6_newdivert */ 3626254401Scy/* Returns: int - -1 == error, 0 == success */ 3627254401Scy/* Parameters: fin(I) - pointer to packet information */ 3628254401Scy/* nat(I) - pointer to NAT entry */ 3629254401Scy/* ni(I) - pointer to structure with misc. information needed */ 3630254401Scy/* to create new NAT entry. */ 3631254401Scy/* Write Lock: ipf_nat */ 3632254401Scy/* */ 3633254401Scy/* Create a new NAT divert session as defined by the NAT rule. This is */ 3634254401Scy/* somewhat different to other NAT session creation routines because we */ 3635254401Scy/* do not iterate through either port numbers or IP addresses, searching */ 3636254401Scy/* for a unique mapping, however, a complimentary duplicate check is made. */ 3637254401Scy/* ------------------------------------------------------------------------ */ 3638254401Scyint 3639254401Scyipf_nat6_newdivert(fin, nat, nai) 3640254401Scy fr_info_t *fin; 3641254401Scy nat_t *nat; 3642254401Scy natinfo_t *nai; 3643254401Scy{ 3644254401Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3645254401Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 3646254401Scy fr_info_t frnat; 3647254401Scy ipnat_t *np; 3648254401Scy nat_t *natl; 3649254401Scy int p; 3650254401Scy 3651254401Scy np = nai->nai_np; 3652254401Scy bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); 3653254401Scy 3654254401Scy nat->nat_pr[0] = 0; 3655254401Scy nat->nat_osrc6 = fin->fin_src6; 3656254401Scy nat->nat_odst6 = fin->fin_dst6; 3657254401Scy nat->nat_osport = htons(fin->fin_data[0]); 3658254401Scy nat->nat_odport = htons(fin->fin_data[1]); 3659254401Scy frnat.fin_src6 = np->in_snip6; 3660254401Scy frnat.fin_dst6 = np->in_dnip6; 3661254401Scy 3662254401Scy if (np->in_redir & NAT_DIVERTUDP) { 3663254401Scy frnat.fin_data[0] = np->in_spnext; 3664254401Scy frnat.fin_data[1] = np->in_dpnext; 3665254401Scy frnat.fin_flx |= FI_TCPUDP; 3666254401Scy p = IPPROTO_UDP; 3667254401Scy } else { 3668254401Scy frnat.fin_flx &= ~FI_TCPUDP; 3669254401Scy p = IPPROTO_IPIP; 3670254401Scy } 3671254401Scy 3672254401Scy if (fin->fin_out == 1) { 3673254401Scy natl = ipf_nat6_inlookup(&frnat, 0, p, &frnat.fin_dst6.in6, 3674254401Scy &frnat.fin_src6.in6); 3675254401Scy 3676254401Scy } else { 3677254401Scy natl = ipf_nat6_outlookup(&frnat, 0, p, &frnat.fin_dst6.in6, 3678254401Scy &frnat.fin_src6.in6); 3679254401Scy } 3680254401Scy 3681254401Scy if (natl != NULL) { 3682254401Scy NBUMPSIDE6D(fin->fin_out, ns_divert_exist); 3683254401Scy return -1; 3684254401Scy } 3685254401Scy 3686254401Scy nat->nat_nsrc6 = frnat.fin_src6; 3687254401Scy nat->nat_ndst6 = frnat.fin_dst6; 3688254401Scy if (np->in_redir & NAT_DIVERTUDP) { 3689254401Scy nat->nat_nsport = htons(frnat.fin_data[0]); 3690254401Scy nat->nat_ndport = htons(frnat.fin_data[1]); 3691254401Scy } 3692254401Scy nat->nat_pr[fin->fin_out] = fin->fin_p; 3693254401Scy nat->nat_pr[1 - fin->fin_out] = p; 3694254401Scy 3695254401Scy if (np->in_redir & NAT_REDIRECT) 3696254401Scy nat->nat_dir = NAT_DIVERTIN; 3697254401Scy else 3698254401Scy nat->nat_dir = NAT_DIVERTOUT; 3699254401Scy 3700254401Scy return 0; 3701254401Scy} 3702254401Scy 3703254401Scy 3704254401Scy/* ------------------------------------------------------------------------ */ 3705254401Scy/* Function: nat6_builddivertmp */ 3706254401Scy/* Returns: int - -1 == error, 0 == success */ 3707254401Scy/* Parameters: np(I) - pointer to a NAT rule */ 3708254401Scy/* */ 3709254401Scy/* For divert rules, a skeleton packet representing what will be prepended */ 3710254401Scy/* to the real packet is created. Even though we don't have the full */ 3711254401Scy/* packet here, a checksum is calculated that we update later when we */ 3712254401Scy/* fill in the final details. At present a 0 checksum for UDP is being set */ 3713254401Scy/* here because it is expected that divert will be used for localhost. */ 3714254401Scy/* ------------------------------------------------------------------------ */ 3715254401Scystatic int 3716254401Scyipf_nat6_builddivertmp(softn, np) 3717254401Scy ipf_nat_softc_t *softn; 3718254401Scy ipnat_t *np; 3719254401Scy{ 3720254401Scy udphdr_t *uh; 3721254401Scy size_t len; 3722254401Scy ip6_t *ip6; 3723254401Scy 3724254401Scy if ((np->in_redir & NAT_DIVERTUDP) != 0) 3725254401Scy len = sizeof(ip6_t) + sizeof(udphdr_t); 3726254401Scy else 3727254401Scy len = sizeof(ip6_t); 3728254401Scy 3729254401Scy ALLOC_MB_T(np->in_divmp, len); 3730254401Scy if (np->in_divmp == NULL) { 3731254401Scy ATOMIC_INCL(softn->ipf_nat_stats.ns_divert_build); 3732254401Scy return -1; 3733254401Scy } 3734254401Scy 3735254401Scy /* 3736254401Scy * First, the header to get the packet diverted to the new destination 3737254401Scy */ 3738254401Scy ip6 = MTOD(np->in_divmp, ip6_t *); 3739254401Scy ip6->ip6_vfc = 0x60; 3740254401Scy if ((np->in_redir & NAT_DIVERTUDP) != 0) 3741254401Scy ip6->ip6_nxt = IPPROTO_UDP; 3742254401Scy else 3743254401Scy ip6->ip6_nxt = IPPROTO_IPIP; 3744254401Scy ip6->ip6_hlim = 255; 3745254401Scy ip6->ip6_plen = 0; 3746254401Scy ip6->ip6_src = np->in_snip6.in6; 3747254401Scy ip6->ip6_dst = np->in_dnip6.in6; 3748254401Scy 3749254401Scy if (np->in_redir & NAT_DIVERTUDP) { 3750254401Scy uh = (udphdr_t *)((u_char *)ip6 + sizeof(*ip6)); 3751254401Scy uh->uh_sum = 0; 3752254401Scy uh->uh_ulen = 8; 3753254401Scy uh->uh_sport = htons(np->in_spnext); 3754254401Scy uh->uh_dport = htons(np->in_dpnext); 3755254401Scy } 3756254401Scy 3757254401Scy return 0; 3758254401Scy} 3759254401Scy 3760254401Scy 3761254401Scy#define MINDECAP (sizeof(ip6_t) + sizeof(udphdr_t) + sizeof(ip6_t)) 3762254401Scy 3763254401Scy/* ------------------------------------------------------------------------ */ 3764254401Scy/* Function: nat6_decap */ 3765254401Scy/* Returns: int - -1 == error, 0 == success */ 3766254401Scy/* Parameters: fin(I) - pointer to packet information */ 3767254401Scy/* nat(I) - pointer to current NAT session */ 3768254401Scy/* */ 3769254401Scy/* This function is responsible for undoing a packet's encapsulation in the */ 3770254401Scy/* reverse of an encap/divert rule. After removing the outer encapsulation */ 3771254401Scy/* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/ 3772254401Scy/* match the "new" packet as it may still be used by IPFilter elsewhere. */ 3773254401Scy/* We use "dir" here as the basis for some of the expectations about the */ 3774254401Scy/* outer header. If we return an error, the goal is to leave the original */ 3775254401Scy/* packet information undisturbed - this falls short at the end where we'd */ 3776254401Scy/* need to back a backup copy of "fin" - expensive. */ 3777254401Scy/* ------------------------------------------------------------------------ */ 3778254401Scystatic int 3779254401Scyipf_nat6_decap(fin, nat) 3780254401Scy fr_info_t *fin; 3781254401Scy nat_t *nat; 3782254401Scy{ 3783254401Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3784254401Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 3785254401Scy char *hdr; 3786254401Scy int skip; 3787254401Scy mb_t *m; 3788254401Scy 3789254401Scy if ((fin->fin_flx & FI_ICMPERR) != 0) { 3790254401Scy return 0; 3791254401Scy } 3792254401Scy 3793254401Scy m = fin->fin_m; 3794254401Scy skip = fin->fin_hlen; 3795254401Scy 3796254401Scy switch (nat->nat_dir) 3797254401Scy { 3798254401Scy case NAT_DIVERTIN : 3799254401Scy case NAT_DIVERTOUT : 3800254401Scy if (fin->fin_plen < MINDECAP) 3801254401Scy return -1; 3802254401Scy skip += sizeof(udphdr_t); 3803254401Scy break; 3804254401Scy 3805254401Scy case NAT_ENCAPIN : 3806254401Scy case NAT_ENCAPOUT : 3807254401Scy if (fin->fin_plen < (skip + sizeof(ip6_t))) 3808254401Scy return -1; 3809254401Scy break; 3810254401Scy default : 3811254401Scy return -1; 3812254401Scy /* NOTREACHED */ 3813254401Scy } 3814254401Scy 3815254401Scy /* 3816254401Scy * The aim here is to keep the original packet details in "fin" for 3817254401Scy * as long as possible so that returning with an error is for the 3818254401Scy * original packet and there is little undoing work to do. 3819254401Scy */ 3820254401Scy if (M_LEN(m) < skip + sizeof(ip6_t)) { 3821254401Scy if (ipf_pr_pullup(fin, skip + sizeof(ip6_t)) == -1) 3822254401Scy return -1; 3823254401Scy } 3824254401Scy 3825254401Scy hdr = MTOD(fin->fin_m, char *); 3826254401Scy fin->fin_ip6 = (ip6_t *)(hdr + skip); 3827254401Scy 3828254401Scy if (ipf_pr_pullup(fin, skip + sizeof(ip6_t)) == -1) { 3829254401Scy NBUMPSIDE6D(fin->fin_out, ns_decap_pullup); 3830254401Scy return -1; 3831254401Scy } 3832254401Scy 3833254401Scy fin->fin_hlen = sizeof(ip6_t); 3834254401Scy fin->fin_dlen -= skip; 3835254401Scy fin->fin_plen -= skip; 3836254401Scy fin->fin_ipoff += skip; 3837254401Scy 3838254401Scy if (ipf_makefrip(sizeof(ip6_t), (ip_t *)hdr, fin) == -1) { 3839254401Scy NBUMPSIDE6D(fin->fin_out, ns_decap_bad); 3840254401Scy return -1; 3841254401Scy } 3842254401Scy 3843254401Scy return skip; 3844254401Scy} 3845254401Scy 3846254401Scy 3847254401Scy/* ------------------------------------------------------------------------ */ 3848254401Scy/* Function: nat6_nextaddr */ 3849254401Scy/* Returns: int - -1 == bad input (no new address), */ 3850254401Scy/* 0 == success and dst has new address */ 3851254401Scy/* Parameters: fin(I) - pointer to packet information */ 3852254401Scy/* na(I) - how to generate new address */ 3853254401Scy/* old(I) - original address being replaced */ 3854254401Scy/* dst(O) - where to put the new address */ 3855254401Scy/* Write Lock: ipf_nat */ 3856254401Scy/* */ 3857254401Scy/* This function uses the contents of the "na" structure, in combination */ 3858254401Scy/* with "old" to produce a new address to store in "dst". Not all of the */ 3859254401Scy/* possible uses of "na" will result in a new address. */ 3860254401Scy/* ------------------------------------------------------------------------ */ 3861254401Scystatic int 3862254401Scyipf_nat6_nextaddr(fin, na, old, dst) 3863254401Scy fr_info_t *fin; 3864254401Scy nat_addr_t *na; 3865254401Scy i6addr_t *old, *dst; 3866254401Scy{ 3867254401Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3868254401Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 3869254401Scy i6addr_t newip, new; 3870254401Scy u_32_t amin, amax; 3871254401Scy int error; 3872254401Scy 3873254401Scy new.i6[0] = 0; 3874254401Scy new.i6[1] = 0; 3875254401Scy new.i6[2] = 0; 3876254401Scy new.i6[3] = 0; 3877254401Scy amin = na->na_addr[0].in4.s_addr; 3878254401Scy 3879254401Scy switch (na->na_atype) 3880254401Scy { 3881254401Scy case FRI_RANGE : 3882254401Scy amax = na->na_addr[1].in4.s_addr; 3883254401Scy break; 3884254401Scy 3885254401Scy case FRI_NETMASKED : 3886254401Scy case FRI_DYNAMIC : 3887254401Scy case FRI_NORMAL : 3888254401Scy /* 3889254401Scy * Compute the maximum address by adding the inverse of the 3890254401Scy * netmask to the minimum address. 3891254401Scy */ 3892254401Scy amax = ~na->na_addr[1].in4.s_addr; 3893254401Scy amax |= amin; 3894254401Scy break; 3895254401Scy 3896254401Scy case FRI_LOOKUP : 3897254401Scy break; 3898254401Scy 3899254401Scy case FRI_BROADCAST : 3900254401Scy case FRI_PEERADDR : 3901254401Scy case FRI_NETWORK : 3902254401Scy default : 3903254401Scy return -1; 3904254401Scy } 3905254401Scy 3906254401Scy error = -1; 3907254401Scy switch (na->na_function) 3908254401Scy { 3909254401Scy case IPLT_DSTLIST : 3910254401Scy error = ipf_dstlist_select_node(fin, na->na_ptr, dst->i6, 3911254401Scy NULL); 3912254401Scy break; 3913254401Scy 3914254401Scy case IPLT_NONE : 3915254401Scy /* 3916254401Scy * 0/0 as the new address means leave it alone. 3917254401Scy */ 3918254401Scy if (na->na_addr[0].in4.s_addr == 0 && 3919254401Scy na->na_addr[1].in4.s_addr == 0) { 3920254401Scy new = *old; 3921254401Scy 3922254401Scy /* 3923254401Scy * 0/32 means get the interface's address 3924254401Scy */ 3925254401Scy } else if (IP6_ISZERO(&na->na_addr[0].in6) && 3926254401Scy IP6_ISONES(&na->na_addr[1].in6)) { 3927254401Scy if (ipf_ifpaddr(softc, 6, na->na_atype, 3928254401Scy fin->fin_ifp, &newip, NULL) == -1) { 3929254401Scy NBUMPSIDE6(fin->fin_out, ns_ifpaddrfail); 3930254401Scy return -1; 3931254401Scy } 3932254401Scy new = newip; 3933254401Scy } else { 3934254401Scy new.in6 = na->na_nextip6; 3935254401Scy } 3936254401Scy *dst = new; 3937254401Scy error = 0; 3938254401Scy break; 3939254401Scy 3940254401Scy default : 3941254401Scy NBUMPSIDE6(fin->fin_out, ns_badnextaddr); 3942254401Scy break; 3943254401Scy } 3944254401Scy 3945254401Scy return error; 3946254401Scy} 3947254401Scy 3948254401Scy 3949254401Scy/* ------------------------------------------------------------------------ */ 3950254401Scy/* Function: ipf_nat6_nextaddrinit */ 3951254401Scy/* Returns: int - 0 == success, else error number */ 3952254401Scy/* Parameters: na(I) - NAT address information for generating new addr*/ 3953254401Scy/* base(I) - start of where to find strings */ 3954254401Scy/* initial(I) - flag indicating if it is the first call for */ 3955254401Scy/* this "na" structure. */ 3956254401Scy/* ifp(I) - network interface to derive address */ 3957254401Scy/* information from. */ 3958254401Scy/* */ 3959254401Scy/* This function is expected to be called in two scenarious: when a new NAT */ 3960254401Scy/* rule is loaded into the kernel and when the list of NAT rules is sync'd */ 3961254401Scy/* up with the valid network interfaces (possibly due to them changing.) */ 3962254401Scy/* To distinguish between these, the "initial" parameter is used. If it is */ 3963254401Scy/* 1 then this indicates the rule has just been reloaded and 0 for when we */ 3964254401Scy/* are updating information. This difference is important because in */ 3965254401Scy/* instances where we are not updating address information associated with */ 3966254401Scy/* a network interface, we don't want to disturb what the "next" address to */ 3967254401Scy/* come out of ipf_nat6_nextaddr() will be. */ 3968254401Scy/* ------------------------------------------------------------------------ */ 3969254401Scystatic int 3970254401Scyipf_nat6_nextaddrinit(softc, base, na, initial, ifp) 3971254401Scy ipf_main_softc_t *softc; 3972254401Scy char *base; 3973254401Scy nat_addr_t *na; 3974254401Scy int initial; 3975254401Scy void *ifp; 3976254401Scy{ 3977254401Scy switch (na->na_atype) 3978254401Scy { 3979254401Scy case FRI_LOOKUP : 3980254401Scy if (na->na_subtype == 0) { 3981254401Scy na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT, 3982254401Scy na->na_type, 3983254401Scy na->na_num, 3984254401Scy &na->na_func); 3985254401Scy } else if (na->na_subtype == 1) { 3986254401Scy na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT, 3987254401Scy na->na_type, 3988254401Scy base + na->na_num, 3989254401Scy &na->na_func); 3990254401Scy } 3991254401Scy if (na->na_func == NULL) { 3992254401Scy IPFERROR(60072); 3993254401Scy return ESRCH; 3994254401Scy } 3995254401Scy if (na->na_ptr == NULL) { 3996254401Scy IPFERROR(60073); 3997254401Scy return ESRCH; 3998254401Scy } 3999254401Scy break; 4000254401Scy case FRI_DYNAMIC : 4001254401Scy case FRI_BROADCAST : 4002254401Scy case FRI_NETWORK : 4003254401Scy case FRI_NETMASKED : 4004254401Scy case FRI_PEERADDR : 4005254401Scy if (ifp != NULL) 4006254401Scy (void )ipf_ifpaddr(softc, 6, na->na_atype, ifp, 4007254401Scy &na->na_addr[0], 4008254401Scy &na->na_addr[1]); 4009254401Scy break; 4010254401Scy 4011254401Scy case FRI_SPLIT : 4012254401Scy case FRI_RANGE : 4013254401Scy if (initial) 4014254401Scy na->na_nextip6 = na->na_addr[0].in6; 4015254401Scy break; 4016254401Scy 4017254401Scy case FRI_NONE : 4018254401Scy IP6_ANDASSIGN(&na->na_addr[0].in6, &na->na_addr[1].in6); 4019254401Scy return 0; 4020254401Scy 4021254401Scy case FRI_NORMAL : 4022254401Scy IP6_ANDASSIGN(&na->na_addr[0].in6, &na->na_addr[1].in6); 4023254401Scy break; 4024254401Scy 4025254401Scy default : 4026254401Scy IPFERROR(60074); 4027254401Scy return EINVAL; 4028254401Scy } 4029254401Scy 4030254401Scy if (initial && (na->na_atype == FRI_NORMAL)) { 4031254401Scy if (IP6_ISZERO(&na->na_addr[0].in6)) { 4032254401Scy if (IP6_ISONES(&na->na_addr[1].in6) || 4033254401Scy IP6_ISZERO(&na->na_addr[1].in6)) { 4034254401Scy return 0; 4035254401Scy } 4036254401Scy } 4037254401Scy 4038254401Scy na->na_nextip6 = na->na_addr[0].in6; 4039254401Scy if (!IP6_ISONES(&na->na_addr[1].in6)) { 4040254401Scy IP6_INC(&na->na_nextip6); 4041254401Scy } 4042254401Scy } 4043254401Scy 4044254401Scy return 0; 4045254401Scy} 4046254401Scy 4047254401Scy 4048254401Scy/* ------------------------------------------------------------------------ */ 4049254401Scy/* Function: ipf_nat6_icmpquerytype */ 4050254401Scy/* Returns: int - 1 == success, 0 == failure */ 4051254401Scy/* Parameters: icmptype(I) - ICMP type number */ 4052254401Scy/* */ 4053254401Scy/* Tests to see if the ICMP type number passed is a query/response type or */ 4054254401Scy/* not. */ 4055254401Scy/* ------------------------------------------------------------------------ */ 4056254401Scystatic int 4057254401Scyipf_nat6_icmpquerytype(icmptype) 4058254401Scy int icmptype; 4059254401Scy{ 4060254401Scy 4061254401Scy /* 4062254401Scy * For the ICMP query NAT code, it is essential that both the query 4063254401Scy * and the reply match on the NAT rule. Because the NAT structure 4064254401Scy * does not keep track of the icmptype, and a single NAT structure 4065254401Scy * is used for all icmp types with the same src, dest and id, we 4066254401Scy * simply define the replies as queries as well. The funny thing is, 4067254401Scy * altough it seems silly to call a reply a query, this is exactly 4068254401Scy * as it is defined in the IPv4 specification 4069254401Scy */ 4070254401Scy 4071254401Scy switch (icmptype) 4072254401Scy { 4073254401Scy 4074254401Scy case ICMP6_ECHO_REPLY: 4075254401Scy case ICMP6_ECHO_REQUEST: 4076254401Scy /* route aedvertisement/solliciation is currently unsupported: */ 4077254401Scy /* it would require rewriting the ICMP data section */ 4078254401Scy case ICMP6_MEMBERSHIP_QUERY: 4079254401Scy case ICMP6_MEMBERSHIP_REPORT: 4080254401Scy case ICMP6_MEMBERSHIP_REDUCTION: 4081254401Scy case ICMP6_WRUREQUEST: 4082254401Scy case ICMP6_WRUREPLY: 4083254401Scy case MLD6_MTRACE_RESP: 4084254401Scy case MLD6_MTRACE: 4085254401Scy return 1; 4086254401Scy default: 4087254401Scy return 0; 4088254401Scy } 4089254401Scy} 4090254401Scy#endif /* USE_INET6 */ 4091