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