12393Syz155240 /* 26295San207044 * Copyright (C) 1995-2004 by Darren Reed. 32393Syz155240 * 42393Syz155240 * See the IPFILTER.LICENCE file for details on licencing. 52393Syz155240 * 65828Sjojemann * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 72393Syz155240 * Use is subject to license terms. 82393Syz155240 */ 92393Syz155240 102393Syz155240 #if defined(KERNEL) || defined(_KERNEL) 112393Syz155240 # undef KERNEL 122393Syz155240 # undef _KERNEL 132393Syz155240 # define KERNEL 1 142393Syz155240 # define _KERNEL 1 152393Syz155240 #endif 162393Syz155240 #include <sys/errno.h> 172393Syz155240 #include <sys/types.h> 182393Syz155240 #include <sys/param.h> 192393Syz155240 #include <sys/time.h> 202393Syz155240 #include <sys/file.h> 212393Syz155240 #if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \ 222393Syz155240 defined(_KERNEL) 232393Syz155240 # include "opt_ipfilter_log.h" 242393Syz155240 #endif 252393Syz155240 #if !defined(_KERNEL) 262393Syz155240 # include <stdio.h> 272393Syz155240 # include <string.h> 282393Syz155240 # include <stdlib.h> 292393Syz155240 # define _KERNEL 302393Syz155240 # ifdef __OpenBSD__ 312393Syz155240 struct file; 322393Syz155240 # endif 332393Syz155240 # include <sys/uio.h> 342393Syz155240 # undef _KERNEL 352393Syz155240 #endif 362393Syz155240 #if defined(_KERNEL) && (__FreeBSD_version >= 220000) 372393Syz155240 # include <sys/filio.h> 382393Syz155240 # include <sys/fcntl.h> 392393Syz155240 #else 402393Syz155240 # include <sys/ioctl.h> 412393Syz155240 #endif 422393Syz155240 #if !defined(AIX) 432393Syz155240 # include <sys/fcntl.h> 442393Syz155240 #endif 452393Syz155240 #if !defined(linux) 462393Syz155240 # include <sys/protosw.h> 472393Syz155240 #endif 482393Syz155240 #include <sys/socket.h> 492393Syz155240 #if defined(_KERNEL) 502393Syz155240 # include <sys/systm.h> 512393Syz155240 # if !defined(__SVR4) && !defined(__svr4__) 522393Syz155240 # include <sys/mbuf.h> 532393Syz155240 # endif 542393Syz155240 #endif 552393Syz155240 #if defined(__SVR4) || defined(__svr4__) 562393Syz155240 # include <sys/filio.h> 572393Syz155240 # include <sys/byteorder.h> 582393Syz155240 # ifdef _KERNEL 592393Syz155240 # include <sys/dditypes.h> 602393Syz155240 # endif 612393Syz155240 # include <sys/stream.h> 622393Syz155240 # include <sys/kmem.h> 632393Syz155240 #endif 642393Syz155240 #if __FreeBSD_version >= 300000 652393Syz155240 # include <sys/queue.h> 662393Syz155240 #endif 672393Syz155240 #include <net/if.h> 682393Syz155240 #if __FreeBSD_version >= 300000 692393Syz155240 # include <net/if_var.h> 702393Syz155240 # if defined(_KERNEL) && !defined(IPFILTER_LKM) 712393Syz155240 # include "opt_ipfilter.h" 722393Syz155240 # endif 732393Syz155240 #endif 742393Syz155240 #ifdef sun 752393Syz155240 # include <net/af.h> 762393Syz155240 #endif 772393Syz155240 #include <net/route.h> 782393Syz155240 #include <netinet/in.h> 792393Syz155240 #include <netinet/in_systm.h> 802393Syz155240 #include <netinet/ip.h> 812393Syz155240 822393Syz155240 #ifdef RFC1825 832393Syz155240 # include <vpn/md5.h> 842393Syz155240 # include <vpn/ipsec.h> 852393Syz155240 extern struct ifnet vpnif; 862393Syz155240 #endif 872393Syz155240 882393Syz155240 #if !defined(linux) 892393Syz155240 # include <netinet/ip_var.h> 902393Syz155240 #endif 912393Syz155240 #include <netinet/tcp.h> 922393Syz155240 #include <netinet/udp.h> 932393Syz155240 #include <netinet/ip_icmp.h> 942393Syz155240 #include "netinet/ip_compat.h" 952393Syz155240 #include <netinet/tcpip.h> 962393Syz155240 #include "netinet/ip_fil.h" 972393Syz155240 #include "netinet/ip_nat.h" 982393Syz155240 #include "netinet/ip_frag.h" 992393Syz155240 #include "netinet/ip_state.h" 1002393Syz155240 #include "netinet/ip_proxy.h" 1013448Sdh155122 #include "netinet/ipf_stack.h" 1022393Syz155240 #ifdef IPFILTER_SYNC 1032393Syz155240 #include "netinet/ip_sync.h" 1042393Syz155240 #endif 1052393Syz155240 #if (__FreeBSD_version >= 300000) 1062393Syz155240 # include <sys/malloc.h> 1072393Syz155240 #endif 1082393Syz155240 /* END OF INCLUDES */ 1092393Syz155240 1102393Syz155240 #undef SOCKADDR_IN 1112393Syz155240 #define SOCKADDR_IN struct sockaddr_in 1122393Syz155240 1132393Syz155240 #if !defined(lint) 1142393Syz155240 static const char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed"; 1152393Syz155240 static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.195.2.42 2005/08/11 19:51:36 darrenr Exp $"; 1162393Syz155240 #endif 1172393Syz155240 1182393Syz155240 1192393Syz155240 /* ======================================================================== */ 1202393Syz155240 /* How the NAT is organised and works. */ 1212393Syz155240 /* */ 1222393Syz155240 /* Inside (interface y) NAT Outside (interface x) */ 1232393Syz155240 /* -------------------- -+- ------------------------------------- */ 1242393Syz155240 /* Packet going | out, processsed by fr_checknatout() for x */ 1252393Syz155240 /* ------------> | ------------> */ 1262393Syz155240 /* src=10.1.1.1 | src=192.1.1.1 */ 1272393Syz155240 /* | */ 1282393Syz155240 /* | in, processed by fr_checknatin() for x */ 1292393Syz155240 /* <------------ | <------------ */ 1302393Syz155240 /* dst=10.1.1.1 | dst=192.1.1.1 */ 1312393Syz155240 /* -------------------- -+- ------------------------------------- */ 1322393Syz155240 /* fr_checknatout() - changes ip_src and if required, sport */ 1332393Syz155240 /* - creates a new mapping, if required. */ 1342393Syz155240 /* fr_checknatin() - changes ip_dst and if required, dport */ 1352393Syz155240 /* */ 1362393Syz155240 /* In the NAT table, internal source is recorded as "in" and externally */ 1372393Syz155240 /* seen as "out". */ 1382393Syz155240 /* ======================================================================== */ 1392393Syz155240 1402393Syz155240 1413448Sdh155122 static int nat_clearlist __P((ipf_stack_t *)); 1423448Sdh155122 static void nat_addnat __P((struct ipnat *, ipf_stack_t *)); 1433448Sdh155122 static void nat_addrdr __P((struct ipnat *, ipf_stack_t *)); 1443448Sdh155122 static int fr_natgetent __P((caddr_t, ipf_stack_t *)); 1453448Sdh155122 static int fr_natgetsz __P((caddr_t, ipf_stack_t *)); 1463448Sdh155122 static int fr_natputent __P((caddr_t, int, ipf_stack_t *)); 1473448Sdh155122 static void nat_tabmove __P((nat_t *, ipf_stack_t *)); 1482393Syz155240 static int nat_match __P((fr_info_t *, ipnat_t *)); 1492393Syz155240 static INLINE int nat_newmap __P((fr_info_t *, nat_t *, natinfo_t *)); 1502393Syz155240 static INLINE int nat_newrdr __P((fr_info_t *, nat_t *, natinfo_t *)); 1512393Syz155240 static hostmap_t *nat_hostmap __P((ipnat_t *, struct in_addr, 1523448Sdh155122 struct in_addr, struct in_addr, u_32_t, 1533448Sdh155122 ipf_stack_t *)); 1542393Syz155240 static INLINE int nat_icmpquerytype4 __P((int)); 1557176Syx160601 static int nat_ruleaddrinit __P((ipnat_t *)); 1567176Syx160601 static int nat_siocaddnat __P((ipnat_t *, ipnat_t **, int, ipf_stack_t *)); 1577176Syx160601 static void nat_siocdelnat __P((ipnat_t *, ipnat_t **, int, ipf_stack_t *)); 1583448Sdh155122 static INLINE int nat_icmperrortype4 __P((int)); 1592393Syz155240 static INLINE int nat_finalise __P((fr_info_t *, nat_t *, natinfo_t *, 1602393Syz155240 tcphdr_t *, nat_t **, int)); 1614380Sjojemann static INLINE int nat_resolverule __P((ipnat_t *, ipf_stack_t *)); 1622958Sdr146992 static void nat_mssclamp __P((tcphdr_t *, u_32_t, u_short *)); 1633448Sdh155122 static int nat_getnext __P((ipftoken_t *, ipfgeniter_t *, ipf_stack_t *)); 1643448Sdh155122 static int nat_iterator __P((ipftoken_t *, ipfgeniter_t *, ipf_stack_t *)); 165*8170SJohn.Ojemann@Sun.COM static int nat_flushtable __P((int, ipf_stack_t *)); 1662393Syz155240 1676253San207044 #define NAT_HAS_L4_CHANGED(n) \ 1686295San207044 (((n)->nat_flags & (IPN_TCPUDPICMP)) && \ 1696295San207044 (n)->nat_inport != (n)->nat_outport) 1702393Syz155240 171*8170SJohn.Ojemann@Sun.COM 1722393Syz155240 /* ------------------------------------------------------------------------ */ 1732393Syz155240 /* Function: fr_natinit */ 1742393Syz155240 /* Returns: int - 0 == success, -1 == failure */ 1752393Syz155240 /* Parameters: Nil */ 1762393Syz155240 /* */ 1772393Syz155240 /* Initialise all of the NAT locks, tables and other structures. */ 1782393Syz155240 /* ------------------------------------------------------------------------ */ 1793448Sdh155122 int fr_natinit(ifs) 1803448Sdh155122 ipf_stack_t *ifs; 1812393Syz155240 { 1822393Syz155240 int i; 1832393Syz155240 1843448Sdh155122 KMALLOCS(ifs->ifs_nat_table[0], nat_t **, 1853448Sdh155122 sizeof(nat_t *) * ifs->ifs_ipf_nattable_sz); 1863448Sdh155122 if (ifs->ifs_nat_table[0] != NULL) 1873448Sdh155122 bzero((char *)ifs->ifs_nat_table[0], 1883448Sdh155122 ifs->ifs_ipf_nattable_sz * sizeof(nat_t *)); 1892393Syz155240 else 1902393Syz155240 return -1; 1912393Syz155240 1923448Sdh155122 KMALLOCS(ifs->ifs_nat_table[1], nat_t **, 1933448Sdh155122 sizeof(nat_t *) * ifs->ifs_ipf_nattable_sz); 1943448Sdh155122 if (ifs->ifs_nat_table[1] != NULL) 1953448Sdh155122 bzero((char *)ifs->ifs_nat_table[1], 1963448Sdh155122 ifs->ifs_ipf_nattable_sz * sizeof(nat_t *)); 1972393Syz155240 else 1982393Syz155240 return -2; 1992393Syz155240 2003448Sdh155122 KMALLOCS(ifs->ifs_nat_rules, ipnat_t **, 2013448Sdh155122 sizeof(ipnat_t *) * ifs->ifs_ipf_natrules_sz); 2023448Sdh155122 if (ifs->ifs_nat_rules != NULL) 2033448Sdh155122 bzero((char *)ifs->ifs_nat_rules, 2043448Sdh155122 ifs->ifs_ipf_natrules_sz * sizeof(ipnat_t *)); 2052393Syz155240 else 2062393Syz155240 return -3; 2072393Syz155240 2083448Sdh155122 KMALLOCS(ifs->ifs_rdr_rules, ipnat_t **, 2093448Sdh155122 sizeof(ipnat_t *) * ifs->ifs_ipf_rdrrules_sz); 2103448Sdh155122 if (ifs->ifs_rdr_rules != NULL) 2113448Sdh155122 bzero((char *)ifs->ifs_rdr_rules, 2123448Sdh155122 ifs->ifs_ipf_rdrrules_sz * sizeof(ipnat_t *)); 2132393Syz155240 else 2142393Syz155240 return -4; 2152393Syz155240 2163448Sdh155122 KMALLOCS(ifs->ifs_maptable, hostmap_t **, 2173448Sdh155122 sizeof(hostmap_t *) * ifs->ifs_ipf_hostmap_sz); 2183448Sdh155122 if (ifs->ifs_maptable != NULL) 2193448Sdh155122 bzero((char *)ifs->ifs_maptable, 2203448Sdh155122 sizeof(hostmap_t *) * ifs->ifs_ipf_hostmap_sz); 2212393Syz155240 else 2222393Syz155240 return -5; 2232393Syz155240 2243448Sdh155122 ifs->ifs_ipf_hm_maplist = NULL; 2253448Sdh155122 2263448Sdh155122 KMALLOCS(ifs->ifs_nat_stats.ns_bucketlen[0], u_long *, 2273448Sdh155122 ifs->ifs_ipf_nattable_sz * sizeof(u_long)); 2283448Sdh155122 if (ifs->ifs_nat_stats.ns_bucketlen[0] == NULL) 2293448Sdh155122 return -1; 2303448Sdh155122 bzero((char *)ifs->ifs_nat_stats.ns_bucketlen[0], 2313448Sdh155122 ifs->ifs_ipf_nattable_sz * sizeof(u_long)); 2323448Sdh155122 2333448Sdh155122 KMALLOCS(ifs->ifs_nat_stats.ns_bucketlen[1], u_long *, 2343448Sdh155122 ifs->ifs_ipf_nattable_sz * sizeof(u_long)); 2353448Sdh155122 if (ifs->ifs_nat_stats.ns_bucketlen[1] == NULL) 2363448Sdh155122 return -1; 2373448Sdh155122 bzero((char *)ifs->ifs_nat_stats.ns_bucketlen[1], 2383448Sdh155122 ifs->ifs_ipf_nattable_sz * sizeof(u_long)); 2393448Sdh155122 2403448Sdh155122 if (ifs->ifs_fr_nat_maxbucket == 0) { 2413448Sdh155122 for (i = ifs->ifs_ipf_nattable_sz; i > 0; i >>= 1) 2423448Sdh155122 ifs->ifs_fr_nat_maxbucket++; 2433448Sdh155122 ifs->ifs_fr_nat_maxbucket *= 2; 2442393Syz155240 } 2452393Syz155240 2463448Sdh155122 fr_sttab_init(ifs->ifs_nat_tqb, ifs); 2472393Syz155240 /* 2482393Syz155240 * Increase this because we may have "keep state" following this too 2492393Syz155240 * and packet storms can occur if this is removed too quickly. 2502393Syz155240 */ 2513448Sdh155122 ifs->ifs_nat_tqb[IPF_TCPS_CLOSED].ifq_ttl = ifs->ifs_fr_tcplastack; 2523448Sdh155122 ifs->ifs_nat_tqb[IPF_TCP_NSTATES - 1].ifq_next = &ifs->ifs_nat_udptq; 2533448Sdh155122 ifs->ifs_nat_udptq.ifq_ttl = ifs->ifs_fr_defnatage; 2543448Sdh155122 ifs->ifs_nat_udptq.ifq_ref = 1; 2553448Sdh155122 ifs->ifs_nat_udptq.ifq_head = NULL; 2563448Sdh155122 ifs->ifs_nat_udptq.ifq_tail = &ifs->ifs_nat_udptq.ifq_head; 2573448Sdh155122 MUTEX_INIT(&ifs->ifs_nat_udptq.ifq_lock, "nat ipftq udp tab"); 2583448Sdh155122 ifs->ifs_nat_udptq.ifq_next = &ifs->ifs_nat_icmptq; 2593448Sdh155122 ifs->ifs_nat_icmptq.ifq_ttl = ifs->ifs_fr_defnaticmpage; 2603448Sdh155122 ifs->ifs_nat_icmptq.ifq_ref = 1; 2613448Sdh155122 ifs->ifs_nat_icmptq.ifq_head = NULL; 2623448Sdh155122 ifs->ifs_nat_icmptq.ifq_tail = &ifs->ifs_nat_icmptq.ifq_head; 2633448Sdh155122 MUTEX_INIT(&ifs->ifs_nat_icmptq.ifq_lock, "nat icmp ipftq tab"); 2643448Sdh155122 ifs->ifs_nat_icmptq.ifq_next = &ifs->ifs_nat_iptq; 2653448Sdh155122 ifs->ifs_nat_iptq.ifq_ttl = ifs->ifs_fr_defnatipage; 2663448Sdh155122 ifs->ifs_nat_iptq.ifq_ref = 1; 2673448Sdh155122 ifs->ifs_nat_iptq.ifq_head = NULL; 2683448Sdh155122 ifs->ifs_nat_iptq.ifq_tail = &ifs->ifs_nat_iptq.ifq_head; 2693448Sdh155122 MUTEX_INIT(&ifs->ifs_nat_iptq.ifq_lock, "nat ip ipftq tab"); 2703448Sdh155122 ifs->ifs_nat_iptq.ifq_next = NULL; 2712393Syz155240 2722393Syz155240 for (i = 0; i < IPF_TCP_NSTATES; i++) { 2733448Sdh155122 if (ifs->ifs_nat_tqb[i].ifq_ttl < ifs->ifs_fr_defnaticmpage) 2743448Sdh155122 ifs->ifs_nat_tqb[i].ifq_ttl = ifs->ifs_fr_defnaticmpage; 2752393Syz155240 #ifdef LARGE_NAT 2763448Sdh155122 else if (ifs->ifs_nat_tqb[i].ifq_ttl > ifs->ifs_fr_defnatage) 2773448Sdh155122 ifs->ifs_nat_tqb[i].ifq_ttl = ifs->ifs_fr_defnatage; 2782393Syz155240 #endif 2792393Syz155240 } 2802393Syz155240 2812393Syz155240 /* 2822393Syz155240 * Increase this because we may have "keep state" following 2832393Syz155240 * this too and packet storms can occur if this is removed 2842393Syz155240 * too quickly. 2852393Syz155240 */ 2863448Sdh155122 ifs->ifs_nat_tqb[IPF_TCPS_CLOSED].ifq_ttl = 2873448Sdh155122 ifs->ifs_nat_tqb[IPF_TCPS_LAST_ACK].ifq_ttl; 2883448Sdh155122 2893448Sdh155122 RWLOCK_INIT(&ifs->ifs_ipf_nat, "ipf IP NAT rwlock"); 2903448Sdh155122 RWLOCK_INIT(&ifs->ifs_ipf_natfrag, "ipf IP NAT-Frag rwlock"); 2913448Sdh155122 MUTEX_INIT(&ifs->ifs_ipf_nat_new, "ipf nat new mutex"); 2923448Sdh155122 MUTEX_INIT(&ifs->ifs_ipf_natio, "ipf nat io mutex"); 2933448Sdh155122 2943448Sdh155122 ifs->ifs_fr_nat_init = 1; 295*8170SJohn.Ojemann@Sun.COM ifs->ifs_nat_last_force_flush = ifs->ifs_fr_ticks; 2962393Syz155240 return 0; 2972393Syz155240 } 2982393Syz155240 2992393Syz155240 3002393Syz155240 /* ------------------------------------------------------------------------ */ 3012393Syz155240 /* Function: nat_addrdr */ 3022393Syz155240 /* Returns: Nil */ 3032393Syz155240 /* Parameters: n(I) - pointer to NAT rule to add */ 3042393Syz155240 /* */ 3052393Syz155240 /* Adds a redirect rule to the hash table of redirect rules and the list of */ 3062393Syz155240 /* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ 3072393Syz155240 /* use by redirect rules. */ 3082393Syz155240 /* ------------------------------------------------------------------------ */ 3093448Sdh155122 static void nat_addrdr(n, ifs) 3102393Syz155240 ipnat_t *n; 3113448Sdh155122 ipf_stack_t *ifs; 3122393Syz155240 { 3132393Syz155240 ipnat_t **np; 3142393Syz155240 u_32_t j; 3152393Syz155240 u_int hv; 3162393Syz155240 int k; 3172393Syz155240 3182393Syz155240 k = count4bits(n->in_outmsk); 3192393Syz155240 if ((k >= 0) && (k != 32)) 3203448Sdh155122 ifs->ifs_rdr_masks |= 1 << k; 3212393Syz155240 j = (n->in_outip & n->in_outmsk); 3223448Sdh155122 hv = NAT_HASH_FN(j, 0, ifs->ifs_ipf_rdrrules_sz); 3233448Sdh155122 np = ifs->ifs_rdr_rules + hv; 3242393Syz155240 while (*np != NULL) 3252393Syz155240 np = &(*np)->in_rnext; 3262393Syz155240 n->in_rnext = NULL; 3272393Syz155240 n->in_prnext = np; 3282393Syz155240 n->in_hv = hv; 3292393Syz155240 *np = n; 3302393Syz155240 } 3312393Syz155240 3322393Syz155240 3332393Syz155240 /* ------------------------------------------------------------------------ */ 3342393Syz155240 /* Function: nat_addnat */ 3352393Syz155240 /* Returns: Nil */ 3362393Syz155240 /* Parameters: n(I) - pointer to NAT rule to add */ 3372393Syz155240 /* */ 3382393Syz155240 /* Adds a NAT map rule to the hash table of rules and the list of loaded */ 3392393Syz155240 /* NAT rules. Updates the bitmask indicating which netmasks are in use by */ 3402393Syz155240 /* redirect rules. */ 3412393Syz155240 /* ------------------------------------------------------------------------ */ 3423448Sdh155122 static void nat_addnat(n, ifs) 3432393Syz155240 ipnat_t *n; 3443448Sdh155122 ipf_stack_t *ifs; 3452393Syz155240 { 3462393Syz155240 ipnat_t **np; 3472393Syz155240 u_32_t j; 3482393Syz155240 u_int hv; 3492393Syz155240 int k; 3502393Syz155240 3512393Syz155240 k = count4bits(n->in_inmsk); 3522393Syz155240 if ((k >= 0) && (k != 32)) 3533448Sdh155122 ifs->ifs_nat_masks |= 1 << k; 3542393Syz155240 j = (n->in_inip & n->in_inmsk); 3553448Sdh155122 hv = NAT_HASH_FN(j, 0, ifs->ifs_ipf_natrules_sz); 3563448Sdh155122 np = ifs->ifs_nat_rules + hv; 3572393Syz155240 while (*np != NULL) 3582393Syz155240 np = &(*np)->in_mnext; 3592393Syz155240 n->in_mnext = NULL; 3602393Syz155240 n->in_pmnext = np; 3612393Syz155240 n->in_hv = hv; 3622393Syz155240 *np = n; 3632393Syz155240 } 3642393Syz155240 3652393Syz155240 3662393Syz155240 /* ------------------------------------------------------------------------ */ 3672393Syz155240 /* Function: nat_delrdr */ 3682393Syz155240 /* Returns: Nil */ 3692393Syz155240 /* Parameters: n(I) - pointer to NAT rule to delete */ 3702393Syz155240 /* */ 3712393Syz155240 /* Removes a redirect rule from the hash table of redirect rules. */ 3722393Syz155240 /* ------------------------------------------------------------------------ */ 3737176Syx160601 void nat_delrdr(n) 3742393Syz155240 ipnat_t *n; 3752393Syz155240 { 3762393Syz155240 if (n->in_rnext) 3772393Syz155240 n->in_rnext->in_prnext = n->in_prnext; 3782393Syz155240 *n->in_prnext = n->in_rnext; 3792393Syz155240 } 3802393Syz155240 3812393Syz155240 3822393Syz155240 /* ------------------------------------------------------------------------ */ 3832393Syz155240 /* Function: nat_delnat */ 3842393Syz155240 /* Returns: Nil */ 3852393Syz155240 /* Parameters: n(I) - pointer to NAT rule to delete */ 3862393Syz155240 /* */ 3872393Syz155240 /* Removes a NAT map rule from the hash table of NAT map rules. */ 3882393Syz155240 /* ------------------------------------------------------------------------ */ 3897176Syx160601 void nat_delnat(n) 3902393Syz155240 ipnat_t *n; 3912393Syz155240 { 3922393Syz155240 if (n->in_mnext != NULL) 3932393Syz155240 n->in_mnext->in_pmnext = n->in_pmnext; 3942393Syz155240 *n->in_pmnext = n->in_mnext; 3952393Syz155240 } 3962393Syz155240 3972393Syz155240 3982393Syz155240 /* ------------------------------------------------------------------------ */ 3992393Syz155240 /* Function: nat_hostmap */ 4002393Syz155240 /* Returns: struct hostmap* - NULL if no hostmap could be created, */ 4012393Syz155240 /* else a pointer to the hostmapping to use */ 4022393Syz155240 /* Parameters: np(I) - pointer to NAT rule */ 4032393Syz155240 /* real(I) - real IP address */ 4042393Syz155240 /* map(I) - mapped IP address */ 4052393Syz155240 /* port(I) - destination port number */ 4062393Syz155240 /* Write Locks: ipf_nat */ 4072393Syz155240 /* */ 4082393Syz155240 /* Check if an ip address has already been allocated for a given mapping */ 4092393Syz155240 /* that is not doing port based translation. If is not yet allocated, then */ 4102393Syz155240 /* create a new entry if a non-NULL NAT rule pointer has been supplied. */ 4112393Syz155240 /* ------------------------------------------------------------------------ */ 4123448Sdh155122 static struct hostmap *nat_hostmap(np, src, dst, map, port, ifs) 4132393Syz155240 ipnat_t *np; 4142393Syz155240 struct in_addr src; 4152393Syz155240 struct in_addr dst; 4162393Syz155240 struct in_addr map; 4172393Syz155240 u_32_t port; 4183448Sdh155122 ipf_stack_t *ifs; 4192393Syz155240 { 4202393Syz155240 hostmap_t *hm; 4212393Syz155240 u_int hv; 4222393Syz155240 4232393Syz155240 hv = (src.s_addr ^ dst.s_addr); 4242393Syz155240 hv += src.s_addr; 4252393Syz155240 hv += dst.s_addr; 4262393Syz155240 hv %= HOSTMAP_SIZE; 4273448Sdh155122 for (hm = ifs->ifs_maptable[hv]; hm; hm = hm->hm_next) 4282393Syz155240 if ((hm->hm_srcip.s_addr == src.s_addr) && 4292393Syz155240 (hm->hm_dstip.s_addr == dst.s_addr) && 4302393Syz155240 ((np == NULL) || (np == hm->hm_ipnat)) && 4312393Syz155240 ((port == 0) || (port == hm->hm_port))) { 4322393Syz155240 hm->hm_ref++; 4332393Syz155240 return hm; 4342393Syz155240 } 4352393Syz155240 4362393Syz155240 if (np == NULL) 4372393Syz155240 return NULL; 4382393Syz155240 4392393Syz155240 KMALLOC(hm, hostmap_t *); 4402393Syz155240 if (hm) { 4413448Sdh155122 hm->hm_hnext = ifs->ifs_ipf_hm_maplist; 4423448Sdh155122 hm->hm_phnext = &ifs->ifs_ipf_hm_maplist; 4433448Sdh155122 if (ifs->ifs_ipf_hm_maplist != NULL) 4443448Sdh155122 ifs->ifs_ipf_hm_maplist->hm_phnext = &hm->hm_hnext; 4453448Sdh155122 ifs->ifs_ipf_hm_maplist = hm; 4463448Sdh155122 4473448Sdh155122 hm->hm_next = ifs->ifs_maptable[hv]; 4483448Sdh155122 hm->hm_pnext = ifs->ifs_maptable + hv; 4493448Sdh155122 if (ifs->ifs_maptable[hv] != NULL) 4503448Sdh155122 ifs->ifs_maptable[hv]->hm_pnext = &hm->hm_next; 4513448Sdh155122 ifs->ifs_maptable[hv] = hm; 4522393Syz155240 hm->hm_ipnat = np; 4532393Syz155240 hm->hm_srcip = src; 4542393Syz155240 hm->hm_dstip = dst; 4552393Syz155240 hm->hm_mapip = map; 4562393Syz155240 hm->hm_ref = 1; 4572393Syz155240 hm->hm_port = port; 4587176Syx160601 hm->hm_v = 4; 4592393Syz155240 } 4602393Syz155240 return hm; 4612393Syz155240 } 4622393Syz155240 4632393Syz155240 4642393Syz155240 /* ------------------------------------------------------------------------ */ 4655417Sjojemann /* Function: fr_hostmapdel */ 4662393Syz155240 /* Returns: Nil */ 4675417Sjojemann /* Parameters: hmp(I) - pointer to pointer to hostmap structure */ 4682393Syz155240 /* Write Locks: ipf_nat */ 4692393Syz155240 /* */ 4702393Syz155240 /* Decrement the references to this hostmap structure by one. If this */ 4712393Syz155240 /* reaches zero then remove it and free it. */ 4722393Syz155240 /* ------------------------------------------------------------------------ */ 4735417Sjojemann void fr_hostmapdel(hmp) 4745417Sjojemann struct hostmap **hmp; 4752393Syz155240 { 4765417Sjojemann struct hostmap *hm; 4775417Sjojemann 4785417Sjojemann hm = *hmp; 4795417Sjojemann *hmp = NULL; 4805417Sjojemann 4812393Syz155240 hm->hm_ref--; 4822393Syz155240 if (hm->hm_ref == 0) { 4832393Syz155240 if (hm->hm_next) 4842393Syz155240 hm->hm_next->hm_pnext = hm->hm_pnext; 4852393Syz155240 *hm->hm_pnext = hm->hm_next; 4863448Sdh155122 if (hm->hm_hnext) 4873448Sdh155122 hm->hm_hnext->hm_phnext = hm->hm_phnext; 4883448Sdh155122 *hm->hm_phnext = hm->hm_hnext; 4892393Syz155240 KFREE(hm); 4902393Syz155240 } 4912393Syz155240 } 4922393Syz155240 4932393Syz155240 4942393Syz155240 /* ------------------------------------------------------------------------ */ 4952393Syz155240 /* Function: fix_outcksum */ 4962393Syz155240 /* Returns: Nil */ 4972958Sdr146992 /* Parameters: sp(I) - location of 16bit checksum to update */ 4982393Syz155240 /* n((I) - amount to adjust checksum by */ 4992393Syz155240 /* */ 5002393Syz155240 /* Adjusts the 16bit checksum by "n" for packets going out. */ 5012393Syz155240 /* ------------------------------------------------------------------------ */ 5022958Sdr146992 void fix_outcksum(sp, n) 5032393Syz155240 u_short *sp; 5042393Syz155240 u_32_t n; 5052393Syz155240 { 5062393Syz155240 u_short sumshort; 5072393Syz155240 u_32_t sum1; 5082393Syz155240 5092393Syz155240 if (n == 0) 5102393Syz155240 return; 5112393Syz155240 5122393Syz155240 sum1 = (~ntohs(*sp)) & 0xffff; 5132393Syz155240 sum1 += (n); 5142393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 5152393Syz155240 /* Again */ 5162393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 5172393Syz155240 sumshort = ~(u_short)sum1; 5182393Syz155240 *(sp) = htons(sumshort); 5192393Syz155240 } 5202393Syz155240 5212393Syz155240 5222393Syz155240 /* ------------------------------------------------------------------------ */ 5232393Syz155240 /* Function: fix_incksum */ 5242393Syz155240 /* Returns: Nil */ 5252958Sdr146992 /* Parameters: sp(I) - location of 16bit checksum to update */ 5262393Syz155240 /* n((I) - amount to adjust checksum by */ 5272393Syz155240 /* */ 5282393Syz155240 /* Adjusts the 16bit checksum by "n" for packets going in. */ 5292393Syz155240 /* ------------------------------------------------------------------------ */ 5302958Sdr146992 void fix_incksum(sp, n) 5312393Syz155240 u_short *sp; 5322393Syz155240 u_32_t n; 5332393Syz155240 { 5342393Syz155240 u_short sumshort; 5352393Syz155240 u_32_t sum1; 5362393Syz155240 5372393Syz155240 if (n == 0) 5382393Syz155240 return; 5392393Syz155240 5402393Syz155240 sum1 = (~ntohs(*sp)) & 0xffff; 5412393Syz155240 sum1 += ~(n) & 0xffff; 5422393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 5432393Syz155240 /* Again */ 5442393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 5452393Syz155240 sumshort = ~(u_short)sum1; 5462393Syz155240 *(sp) = htons(sumshort); 5472393Syz155240 } 5482393Syz155240 5492393Syz155240 5502393Syz155240 /* ------------------------------------------------------------------------ */ 5512393Syz155240 /* Function: fix_datacksum */ 5522393Syz155240 /* Returns: Nil */ 5532393Syz155240 /* Parameters: sp(I) - location of 16bit checksum to update */ 5542393Syz155240 /* n((I) - amount to adjust checksum by */ 5552393Syz155240 /* */ 5562393Syz155240 /* Fix_datacksum is used *only* for the adjustments of checksums in the */ 5572393Syz155240 /* data section of an IP packet. */ 5582393Syz155240 /* */ 5592393Syz155240 /* The only situation in which you need to do this is when NAT'ing an */ 5602393Syz155240 /* ICMP error message. Such a message, contains in its body the IP header */ 5612393Syz155240 /* of the original IP packet, that causes the error. */ 5622393Syz155240 /* */ 5632393Syz155240 /* You can't use fix_incksum or fix_outcksum in that case, because for the */ 5642393Syz155240 /* kernel the data section of the ICMP error is just data, and no special */ 5652393Syz155240 /* processing like hardware cksum or ntohs processing have been done by the */ 5662393Syz155240 /* kernel on the data section. */ 5672393Syz155240 /* ------------------------------------------------------------------------ */ 5682393Syz155240 void fix_datacksum(sp, n) 5692393Syz155240 u_short *sp; 5702393Syz155240 u_32_t n; 5712393Syz155240 { 5722393Syz155240 u_short sumshort; 5732393Syz155240 u_32_t sum1; 5742393Syz155240 5752393Syz155240 if (n == 0) 5762393Syz155240 return; 5772393Syz155240 5782393Syz155240 sum1 = (~ntohs(*sp)) & 0xffff; 5792393Syz155240 sum1 += (n); 5802393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 5812393Syz155240 /* Again */ 5822393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 5832393Syz155240 sumshort = ~(u_short)sum1; 5842393Syz155240 *(sp) = htons(sumshort); 5852393Syz155240 } 5862393Syz155240 5872393Syz155240 5882393Syz155240 /* ------------------------------------------------------------------------ */ 5892393Syz155240 /* Function: fr_nat_ioctl */ 5902393Syz155240 /* Returns: int - 0 == success, != 0 == failure */ 5912393Syz155240 /* Parameters: data(I) - pointer to ioctl data */ 5922393Syz155240 /* cmd(I) - ioctl command integer */ 5932393Syz155240 /* mode(I) - file mode bits used with open */ 594*8170SJohn.Ojemann@Sun.COM /* uid(I) - uid of caller */ 595*8170SJohn.Ojemann@Sun.COM /* ctx(I) - pointer to give the uid context */ 596*8170SJohn.Ojemann@Sun.COM /* ifs - ipf stack instance */ 5972393Syz155240 /* */ 5982393Syz155240 /* Processes an ioctl call made to operate on the IP Filter NAT device. */ 5992393Syz155240 /* ------------------------------------------------------------------------ */ 6003448Sdh155122 int fr_nat_ioctl(data, cmd, mode, uid, ctx, ifs) 6012393Syz155240 ioctlcmd_t cmd; 6022393Syz155240 caddr_t data; 6033448Sdh155122 int mode, uid; 6043448Sdh155122 void *ctx; 6053448Sdh155122 ipf_stack_t *ifs; 6062393Syz155240 { 6072393Syz155240 ipnat_t *nat, *nt, *n = NULL, **np = NULL; 6082393Syz155240 int error = 0, ret, arg, getlock; 6092393Syz155240 ipnat_t natd; 6102393Syz155240 6112393Syz155240 #if (BSD >= 199306) && defined(_KERNEL) 6122393Syz155240 if ((securelevel >= 2) && (mode & FWRITE)) 6132393Syz155240 return EPERM; 6142393Syz155240 #endif 6152393Syz155240 6162393Syz155240 #if defined(__osf__) && defined(_KERNEL) 6172393Syz155240 getlock = 0; 6182393Syz155240 #else 6192393Syz155240 getlock = (mode & NAT_LOCKHELD) ? 0 : 1; 6202393Syz155240 #endif 6212393Syz155240 6222393Syz155240 nat = NULL; /* XXX gcc -Wuninitialized */ 6232393Syz155240 if (cmd == (ioctlcmd_t)SIOCADNAT) { 6242393Syz155240 KMALLOC(nt, ipnat_t *); 6252393Syz155240 } else { 6262393Syz155240 nt = NULL; 6272393Syz155240 } 6282393Syz155240 6292393Syz155240 if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) { 6302393Syz155240 if (mode & NAT_SYSSPACE) { 6312393Syz155240 bcopy(data, (char *)&natd, sizeof(natd)); 6322393Syz155240 error = 0; 6332393Syz155240 } else { 6342393Syz155240 error = fr_inobj(data, &natd, IPFOBJ_IPNAT); 6352393Syz155240 } 6362393Syz155240 6372393Syz155240 } 6382393Syz155240 6392393Syz155240 if (error != 0) 6402393Syz155240 goto done; 6412393Syz155240 6422393Syz155240 /* 6432393Syz155240 * For add/delete, look to see if the NAT entry is already present 6442393Syz155240 */ 6452393Syz155240 if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) { 6462393Syz155240 nat = &natd; 6472393Syz155240 if (nat->in_v == 0) /* For backward compat. */ 6482393Syz155240 nat->in_v = 4; 6492393Syz155240 nat->in_flags &= IPN_USERFLAGS; 6502393Syz155240 if ((nat->in_redir & NAT_MAPBLK) == 0) { 6512393Syz155240 if ((nat->in_flags & IPN_SPLIT) == 0) 6522393Syz155240 nat->in_inip &= nat->in_inmsk; 6532393Syz155240 if ((nat->in_flags & IPN_IPRANGE) == 0) 6542393Syz155240 nat->in_outip &= nat->in_outmsk; 6552393Syz155240 } 6563448Sdh155122 MUTEX_ENTER(&ifs->ifs_ipf_natio); 6573448Sdh155122 for (np = &ifs->ifs_nat_list; ((n = *np) != NULL); 6583448Sdh155122 np = &n->in_next) 6595828Sjojemann if (bcmp((char *)&nat->in_flags, (char *)&n->in_flags, 6605828Sjojemann IPN_CMPSIZ) == 0) { 6615828Sjojemann if (nat->in_redir == NAT_REDIRECT && 6625828Sjojemann nat->in_pnext != n->in_pnext) 6635828Sjojemann continue; 6642393Syz155240 break; 6655828Sjojemann } 6662393Syz155240 } 6672393Syz155240 6682393Syz155240 switch (cmd) 6692393Syz155240 { 6703448Sdh155122 case SIOCGENITER : 6713448Sdh155122 { 6723448Sdh155122 ipfgeniter_t iter; 6733448Sdh155122 ipftoken_t *token; 6743448Sdh155122 6753448Sdh155122 error = fr_inobj(data, &iter, IPFOBJ_GENITER); 6763448Sdh155122 if (error != 0) 6773448Sdh155122 break; 6783448Sdh155122 6793448Sdh155122 token = ipf_findtoken(iter.igi_type, uid, ctx, ifs); 6803448Sdh155122 if (token != NULL) 6813448Sdh155122 error = nat_iterator(token, &iter, ifs); 6823448Sdh155122 else 6833448Sdh155122 error = ESRCH; 6843448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_tokens); 6853448Sdh155122 break; 6863448Sdh155122 } 6872393Syz155240 #ifdef IPFILTER_LOG 6882393Syz155240 case SIOCIPFFB : 6892393Syz155240 { 6902393Syz155240 int tmp; 6912393Syz155240 6922393Syz155240 if (!(mode & FWRITE)) 6932393Syz155240 error = EPERM; 6942393Syz155240 else { 6953448Sdh155122 tmp = ipflog_clear(IPL_LOGNAT, ifs); 6967433SJohn.Ojemann@Sun.COM error = BCOPYOUT((char *)&tmp, (char *)data, 6977433SJohn.Ojemann@Sun.COM sizeof(tmp)); 6987433SJohn.Ojemann@Sun.COM if (error != 0) 6997433SJohn.Ojemann@Sun.COM error = EFAULT; 7002393Syz155240 } 7012393Syz155240 break; 7022393Syz155240 } 7032393Syz155240 case SIOCSETLG : 7047433SJohn.Ojemann@Sun.COM if (!(mode & FWRITE)) { 7052393Syz155240 error = EPERM; 7067433SJohn.Ojemann@Sun.COM } else { 7077433SJohn.Ojemann@Sun.COM error = BCOPYIN((char *)data, 7087433SJohn.Ojemann@Sun.COM (char *)&ifs->ifs_nat_logging, 7097433SJohn.Ojemann@Sun.COM sizeof(ifs->ifs_nat_logging)); 7107433SJohn.Ojemann@Sun.COM if (error != 0) 7117433SJohn.Ojemann@Sun.COM error = EFAULT; 7122393Syz155240 } 7132393Syz155240 break; 7142393Syz155240 case SIOCGETLG : 7157433SJohn.Ojemann@Sun.COM error = BCOPYOUT((char *)&ifs->ifs_nat_logging, (char *)data, 7167433SJohn.Ojemann@Sun.COM sizeof(ifs->ifs_nat_logging)); 7177433SJohn.Ojemann@Sun.COM if (error != 0) 7187433SJohn.Ojemann@Sun.COM error = EFAULT; 7192393Syz155240 break; 7202393Syz155240 case FIONREAD : 7213448Sdh155122 arg = ifs->ifs_iplused[IPL_LOGNAT]; 7227433SJohn.Ojemann@Sun.COM error = BCOPYOUT(&arg, data, sizeof(arg)); 7237433SJohn.Ojemann@Sun.COM if (error != 0) 7247433SJohn.Ojemann@Sun.COM error = EFAULT; 7252393Syz155240 break; 7262393Syz155240 #endif 7272393Syz155240 case SIOCADNAT : 7282393Syz155240 if (!(mode & FWRITE)) { 7292393Syz155240 error = EPERM; 7302393Syz155240 } else if (n != NULL) { 7312393Syz155240 error = EEXIST; 7322393Syz155240 } else if (nt == NULL) { 7332393Syz155240 error = ENOMEM; 7342393Syz155240 } 7352393Syz155240 if (error != 0) { 7363448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_natio); 7372393Syz155240 break; 7382393Syz155240 } 7392393Syz155240 bcopy((char *)nat, (char *)nt, sizeof(*n)); 7403448Sdh155122 error = nat_siocaddnat(nt, np, getlock, ifs); 7413448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_natio); 7422393Syz155240 if (error == 0) 7432393Syz155240 nt = NULL; 7442393Syz155240 break; 7452393Syz155240 case SIOCRMNAT : 7462393Syz155240 if (!(mode & FWRITE)) { 7472393Syz155240 error = EPERM; 7482393Syz155240 n = NULL; 7492393Syz155240 } else if (n == NULL) { 7502393Syz155240 error = ESRCH; 7512393Syz155240 } 7522393Syz155240 7532393Syz155240 if (error != 0) { 7543448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_natio); 7552393Syz155240 break; 7562393Syz155240 } 7573448Sdh155122 nat_siocdelnat(n, np, getlock, ifs); 7583448Sdh155122 7593448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_natio); 7602393Syz155240 n = NULL; 7612393Syz155240 break; 7622393Syz155240 case SIOCGNATS : 7633448Sdh155122 ifs->ifs_nat_stats.ns_table[0] = ifs->ifs_nat_table[0]; 7643448Sdh155122 ifs->ifs_nat_stats.ns_table[1] = ifs->ifs_nat_table[1]; 7653448Sdh155122 ifs->ifs_nat_stats.ns_list = ifs->ifs_nat_list; 7663448Sdh155122 ifs->ifs_nat_stats.ns_maptable = ifs->ifs_maptable; 7673448Sdh155122 ifs->ifs_nat_stats.ns_maplist = ifs->ifs_ipf_hm_maplist; 7683448Sdh155122 ifs->ifs_nat_stats.ns_nattab_max = ifs->ifs_ipf_nattable_max; 7693448Sdh155122 ifs->ifs_nat_stats.ns_nattab_sz = ifs->ifs_ipf_nattable_sz; 7703448Sdh155122 ifs->ifs_nat_stats.ns_rultab_sz = ifs->ifs_ipf_natrules_sz; 7713448Sdh155122 ifs->ifs_nat_stats.ns_rdrtab_sz = ifs->ifs_ipf_rdrrules_sz; 7723448Sdh155122 ifs->ifs_nat_stats.ns_hostmap_sz = ifs->ifs_ipf_hostmap_sz; 7733448Sdh155122 ifs->ifs_nat_stats.ns_instances = ifs->ifs_nat_instances; 7743448Sdh155122 ifs->ifs_nat_stats.ns_apslist = ifs->ifs_ap_sess_list; 7753448Sdh155122 error = fr_outobj(data, &ifs->ifs_nat_stats, IPFOBJ_NATSTAT); 7762393Syz155240 break; 7772393Syz155240 case SIOCGNATL : 7782393Syz155240 { 7792393Syz155240 natlookup_t nl; 7802393Syz155240 7812393Syz155240 if (getlock) { 7823448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 7832393Syz155240 } 7842393Syz155240 error = fr_inobj(data, &nl, IPFOBJ_NATLOOKUP); 7857176Syx160601 if (nl.nl_v != 6) 7867176Syx160601 nl.nl_v = 4; 7872393Syz155240 if (error == 0) { 7887176Syx160601 void *ptr; 7897176Syx160601 7907176Syx160601 switch (nl.nl_v) 7917176Syx160601 { 7927176Syx160601 case 4: 7937176Syx160601 ptr = nat_lookupredir(&nl, ifs); 7947176Syx160601 break; 7957176Syx160601 #ifdef USE_INET6 7967176Syx160601 case 6: 7977176Syx160601 ptr = nat6_lookupredir(&nl, ifs); 7987176Syx160601 break; 7997176Syx160601 #endif 8007176Syx160601 default: 8017176Syx160601 ptr = NULL; 8027176Syx160601 break; 8037176Syx160601 } 8047176Syx160601 8057176Syx160601 if (ptr != NULL) { 8062393Syz155240 error = fr_outobj(data, &nl, IPFOBJ_NATLOOKUP); 8072393Syz155240 } else { 8082393Syz155240 error = ESRCH; 8092393Syz155240 } 8102393Syz155240 } 8112393Syz155240 if (getlock) { 8123448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 8132393Syz155240 } 8142393Syz155240 break; 8152393Syz155240 } 8162393Syz155240 case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */ 8172393Syz155240 if (!(mode & FWRITE)) { 8182393Syz155240 error = EPERM; 8192393Syz155240 break; 8202393Syz155240 } 8212393Syz155240 if (getlock) { 8223448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 8232393Syz155240 } 8247433SJohn.Ojemann@Sun.COM error = BCOPYIN(data, &arg, sizeof(arg)); 8257433SJohn.Ojemann@Sun.COM if (error != 0) { 8267433SJohn.Ojemann@Sun.COM error = EFAULT; 8277433SJohn.Ojemann@Sun.COM } else { 828*8170SJohn.Ojemann@Sun.COM if (arg == FLUSH_LIST) 8297433SJohn.Ojemann@Sun.COM ret = nat_clearlist(ifs); 830*8170SJohn.Ojemann@Sun.COM else if (VALID_TABLE_FLUSH_OPT(arg)) 831*8170SJohn.Ojemann@Sun.COM ret = nat_flushtable(arg, ifs); 8327433SJohn.Ojemann@Sun.COM else 8337433SJohn.Ojemann@Sun.COM error = EINVAL; 8347433SJohn.Ojemann@Sun.COM } 8352393Syz155240 if (getlock) { 8363448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 8372393Syz155240 } 8382393Syz155240 if (error == 0) { 8397433SJohn.Ojemann@Sun.COM error = BCOPYOUT(&ret, data, sizeof(ret)); 8407433SJohn.Ojemann@Sun.COM if (error != 0) 8417433SJohn.Ojemann@Sun.COM error = EFAULT; 8422393Syz155240 } 8432393Syz155240 break; 8442393Syz155240 case SIOCPROXY : 8453448Sdh155122 error = appr_ioctl(data, cmd, mode, ifs); 8462393Syz155240 break; 8472393Syz155240 case SIOCSTLCK : 8482393Syz155240 if (!(mode & FWRITE)) { 8492393Syz155240 error = EPERM; 8502393Syz155240 } else { 8517433SJohn.Ojemann@Sun.COM error = fr_lock(data, &ifs->ifs_fr_nat_lock); 8522393Syz155240 } 8532393Syz155240 break; 8542393Syz155240 case SIOCSTPUT : 8552761Sjojemann if ((mode & FWRITE) != 0) { 8563448Sdh155122 error = fr_natputent(data, getlock, ifs); 8572393Syz155240 } else { 8582393Syz155240 error = EACCES; 8592393Syz155240 } 8602393Syz155240 break; 8612393Syz155240 case SIOCSTGSZ : 8623448Sdh155122 if (ifs->ifs_fr_nat_lock) { 8632393Syz155240 if (getlock) { 8643448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 8652393Syz155240 } 8663448Sdh155122 error = fr_natgetsz(data, ifs); 8672393Syz155240 if (getlock) { 8683448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 8692393Syz155240 } 8702393Syz155240 } else 8712393Syz155240 error = EACCES; 8722393Syz155240 break; 8732393Syz155240 case SIOCSTGET : 8743448Sdh155122 if (ifs->ifs_fr_nat_lock) { 8752393Syz155240 if (getlock) { 8763448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 8772393Syz155240 } 8783448Sdh155122 error = fr_natgetent(data, ifs); 8792393Syz155240 if (getlock) { 8803448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 8812393Syz155240 } 8822393Syz155240 } else 8832393Syz155240 error = EACCES; 8842393Syz155240 break; 8853448Sdh155122 case SIOCIPFDELTOK : 8867433SJohn.Ojemann@Sun.COM error = BCOPYIN((caddr_t)data, (caddr_t)&arg, sizeof(arg)); 8877433SJohn.Ojemann@Sun.COM if (error != 0) { 8887433SJohn.Ojemann@Sun.COM error = EFAULT; 8897433SJohn.Ojemann@Sun.COM } else { 8907433SJohn.Ojemann@Sun.COM error = ipf_deltoken(arg, uid, ctx, ifs); 8917433SJohn.Ojemann@Sun.COM } 8923448Sdh155122 break; 8932393Syz155240 default : 8942393Syz155240 error = EINVAL; 8952393Syz155240 break; 8962393Syz155240 } 8972393Syz155240 done: 8982393Syz155240 if (nt) 8992393Syz155240 KFREE(nt); 9002393Syz155240 return error; 9012393Syz155240 } 9022393Syz155240 9032393Syz155240 9042393Syz155240 /* ------------------------------------------------------------------------ */ 9052393Syz155240 /* Function: nat_siocaddnat */ 9062393Syz155240 /* Returns: int - 0 == success, != 0 == failure */ 9072393Syz155240 /* Parameters: n(I) - pointer to new NAT rule */ 9082393Syz155240 /* np(I) - pointer to where to insert new NAT rule */ 9092393Syz155240 /* getlock(I) - flag indicating if lock on ipf_nat is held */ 9102393Syz155240 /* Mutex Locks: ipf_natio */ 9112393Syz155240 /* */ 9122393Syz155240 /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 9132393Syz155240 /* from information passed to the kernel, then add it to the appropriate */ 9142393Syz155240 /* NAT rule table(s). */ 9152393Syz155240 /* ------------------------------------------------------------------------ */ 9163448Sdh155122 static int nat_siocaddnat(n, np, getlock, ifs) 9172393Syz155240 ipnat_t *n, **np; 9182393Syz155240 int getlock; 9193448Sdh155122 ipf_stack_t *ifs; 9202393Syz155240 { 9212393Syz155240 int error = 0, i, j; 9222393Syz155240 9234380Sjojemann if (nat_resolverule(n, ifs) != 0) 9244380Sjojemann return ENOENT; 9252393Syz155240 9262393Syz155240 if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) 9272393Syz155240 return EINVAL; 9282393Syz155240 9292393Syz155240 n->in_use = 0; 9302393Syz155240 if (n->in_redir & NAT_MAPBLK) 9312393Syz155240 n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk); 9322393Syz155240 else if (n->in_flags & IPN_AUTOPORTMAP) 9332393Syz155240 n->in_space = USABLE_PORTS * ~ntohl(n->in_inmsk); 9342393Syz155240 else if (n->in_flags & IPN_IPRANGE) 9352393Syz155240 n->in_space = ntohl(n->in_outmsk) - ntohl(n->in_outip); 9362393Syz155240 else if (n->in_flags & IPN_SPLIT) 9372393Syz155240 n->in_space = 2; 9382393Syz155240 else if (n->in_outmsk != 0) 9392393Syz155240 n->in_space = ~ntohl(n->in_outmsk); 9402393Syz155240 else 9412393Syz155240 n->in_space = 1; 9422393Syz155240 9432393Syz155240 /* 9442393Syz155240 * Calculate the number of valid IP addresses in the output 9452393Syz155240 * mapping range. In all cases, the range is inclusive of 9462393Syz155240 * the start and ending IP addresses. 9472393Syz155240 * If to a CIDR address, lose 2: broadcast + network address 9482393Syz155240 * (so subtract 1) 9492393Syz155240 * If to a range, add one. 9502393Syz155240 * If to a single IP address, set to 1. 9512393Syz155240 */ 9522393Syz155240 if (n->in_space) { 9532393Syz155240 if ((n->in_flags & IPN_IPRANGE) != 0) 9542393Syz155240 n->in_space += 1; 9552393Syz155240 else 9562393Syz155240 n->in_space -= 1; 9572393Syz155240 } else 9582393Syz155240 n->in_space = 1; 9592393Syz155240 9607176Syx160601 #ifdef USE_INET6 9617176Syx160601 if (n->in_v == 6 && (n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0 && 9627176Syx160601 !IP6_ISONES(&n->in_out[1]) && !IP6_ISZERO(&n->in_out[1])) 9637176Syx160601 IP6_ADD(&n->in_out[0], 1, &n->in_next6) 9647176Syx160601 else if (n->in_v == 6 && 9657176Syx160601 (n->in_flags & IPN_SPLIT) && (n->in_redir & NAT_REDIRECT)) 9667176Syx160601 n->in_next6 = n->in_in[0]; 9677176Syx160601 else if (n->in_v == 6) 9687176Syx160601 n->in_next6 = n->in_out[0]; 9697176Syx160601 else 9707176Syx160601 #endif 9712393Syz155240 if ((n->in_outmsk != 0xffffffff) && (n->in_outmsk != 0) && 9722393Syz155240 ((n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0)) 9732393Syz155240 n->in_nip = ntohl(n->in_outip) + 1; 9742393Syz155240 else if ((n->in_flags & IPN_SPLIT) && 9752393Syz155240 (n->in_redir & NAT_REDIRECT)) 9762393Syz155240 n->in_nip = ntohl(n->in_inip); 9772393Syz155240 else 9782393Syz155240 n->in_nip = ntohl(n->in_outip); 9797176Syx160601 9802393Syz155240 if (n->in_redir & NAT_MAP) { 9812393Syz155240 n->in_pnext = ntohs(n->in_pmin); 9822393Syz155240 /* 9832393Syz155240 * Multiply by the number of ports made available. 9842393Syz155240 */ 9852393Syz155240 if (ntohs(n->in_pmax) >= ntohs(n->in_pmin)) { 9862393Syz155240 n->in_space *= (ntohs(n->in_pmax) - 9872393Syz155240 ntohs(n->in_pmin) + 1); 9882393Syz155240 /* 9892393Syz155240 * Because two different sources can map to 9902393Syz155240 * different destinations but use the same 9912393Syz155240 * local IP#/port #. 9922393Syz155240 * If the result is smaller than in_space, then 9932393Syz155240 * we may have wrapped around 32bits. 9942393Syz155240 */ 9952393Syz155240 i = n->in_inmsk; 9962393Syz155240 if ((i != 0) && (i != 0xffffffff)) { 9972393Syz155240 j = n->in_space * (~ntohl(i) + 1); 9982393Syz155240 if (j >= n->in_space) 9992393Syz155240 n->in_space = j; 10002393Syz155240 else 10012393Syz155240 n->in_space = 0xffffffff; 10022393Syz155240 } 10032393Syz155240 } 10042393Syz155240 /* 10052393Syz155240 * If no protocol is specified, multiple by 256 to allow for 10062393Syz155240 * at least one IP:IP mapping per protocol. 10072393Syz155240 */ 10082393Syz155240 if ((n->in_flags & IPN_TCPUDPICMP) == 0) { 10092393Syz155240 j = n->in_space * 256; 10102393Syz155240 if (j >= n->in_space) 10112393Syz155240 n->in_space = j; 10122393Syz155240 else 10132393Syz155240 n->in_space = 0xffffffff; 10142393Syz155240 } 10152393Syz155240 } 10162393Syz155240 10172393Syz155240 /* Otherwise, these fields are preset */ 10182393Syz155240 10192393Syz155240 if (getlock) { 10203448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 10212393Syz155240 } 10222393Syz155240 n->in_next = NULL; 10232393Syz155240 *np = n; 10242393Syz155240 10252393Syz155240 if (n->in_age[0] != 0) 10263448Sdh155122 n->in_tqehead[0] = fr_addtimeoutqueue(&ifs->ifs_nat_utqe, 10273448Sdh155122 n->in_age[0], ifs); 10282393Syz155240 10292393Syz155240 if (n->in_age[1] != 0) 10303448Sdh155122 n->in_tqehead[1] = fr_addtimeoutqueue(&ifs->ifs_nat_utqe, 10313448Sdh155122 n->in_age[1], ifs); 10322393Syz155240 10332393Syz155240 if (n->in_redir & NAT_REDIRECT) { 10342393Syz155240 n->in_flags &= ~IPN_NOTDST; 10357176Syx160601 switch (n->in_v) 10367176Syx160601 { 10377176Syx160601 case 4 : 10387176Syx160601 nat_addrdr(n, ifs); 10397176Syx160601 break; 10407176Syx160601 #ifdef USE_INET6 10417176Syx160601 case 6 : 10427176Syx160601 nat6_addrdr(n, ifs); 10437176Syx160601 break; 10447176Syx160601 #endif 10457176Syx160601 default : 10467176Syx160601 break; 10477176Syx160601 } 10482393Syz155240 } 10492393Syz155240 if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { 10502393Syz155240 n->in_flags &= ~IPN_NOTSRC; 10517176Syx160601 switch (n->in_v) 10527176Syx160601 { 10537176Syx160601 case 4 : 10547176Syx160601 nat_addnat(n, ifs); 10557176Syx160601 break; 10567176Syx160601 #ifdef USE_INET6 10577176Syx160601 case 6 : 10587176Syx160601 nat6_addnat(n, ifs); 10597176Syx160601 break; 10607176Syx160601 #endif 10617176Syx160601 default : 10627176Syx160601 break; 10637176Syx160601 } 10642393Syz155240 } 10652393Syz155240 n = NULL; 10663448Sdh155122 ifs->ifs_nat_stats.ns_rules++; 10672393Syz155240 if (getlock) { 10683448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); /* WRITE */ 10692393Syz155240 } 10702393Syz155240 10712393Syz155240 return error; 10722393Syz155240 } 10732393Syz155240 10742393Syz155240 10752393Syz155240 /* ------------------------------------------------------------------------ */ 10762393Syz155240 /* Function: nat_resolvrule */ 10774380Sjojemann /* Returns: int - 0 == success, -1 == failure */ 10782393Syz155240 /* Parameters: n(I) - pointer to NAT rule */ 10792393Syz155240 /* */ 10804380Sjojemann /* Resolve some of the details inside the NAT rule. Includes resolving */ 10814380Sjojemann /* any specified interfaces and proxy labels, and determines whether or not */ 10824380Sjojemann /* all proxy labels are correctly specified. */ 10834380Sjojemann /* */ 10844380Sjojemann /* Called by nat_siocaddnat() (SIOCADNAT) and fr_natputent (SIOCSTPUT). */ 10852393Syz155240 /* ------------------------------------------------------------------------ */ 10864380Sjojemann static int nat_resolverule(n, ifs) 10872393Syz155240 ipnat_t *n; 10883448Sdh155122 ipf_stack_t *ifs; 10892393Syz155240 { 10902393Syz155240 n->in_ifnames[0][LIFNAMSIZ - 1] = '\0'; 10917176Syx160601 n->in_ifps[0] = fr_resolvenic(n->in_ifnames[0], n->in_v, ifs); 10922393Syz155240 10932393Syz155240 n->in_ifnames[1][LIFNAMSIZ - 1] = '\0'; 10942393Syz155240 if (n->in_ifnames[1][0] == '\0') { 10952393Syz155240 (void) strncpy(n->in_ifnames[1], n->in_ifnames[0], LIFNAMSIZ); 10962393Syz155240 n->in_ifps[1] = n->in_ifps[0]; 10972393Syz155240 } else { 10987176Syx160601 n->in_ifps[1] = fr_resolvenic(n->in_ifnames[1], n->in_v, ifs); 10992393Syz155240 } 11002393Syz155240 11012393Syz155240 if (n->in_plabel[0] != '\0') { 11023448Sdh155122 n->in_apr = appr_lookup(n->in_p, n->in_plabel, ifs); 11034380Sjojemann if (n->in_apr == NULL) 11044380Sjojemann return -1; 11052393Syz155240 } 11064380Sjojemann return 0; 11072393Syz155240 } 11082393Syz155240 11092393Syz155240 11102393Syz155240 /* ------------------------------------------------------------------------ */ 11112393Syz155240 /* Function: nat_siocdelnat */ 11122393Syz155240 /* Returns: int - 0 == success, != 0 == failure */ 11132393Syz155240 /* Parameters: n(I) - pointer to new NAT rule */ 11142393Syz155240 /* np(I) - pointer to where to insert new NAT rule */ 11152393Syz155240 /* getlock(I) - flag indicating if lock on ipf_nat is held */ 11162393Syz155240 /* Mutex Locks: ipf_natio */ 11172393Syz155240 /* */ 11182393Syz155240 /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 11192393Syz155240 /* from information passed to the kernel, then add it to the appropriate */ 11202393Syz155240 /* NAT rule table(s). */ 11212393Syz155240 /* ------------------------------------------------------------------------ */ 11223448Sdh155122 static void nat_siocdelnat(n, np, getlock, ifs) 11232393Syz155240 ipnat_t *n, **np; 11242393Syz155240 int getlock; 11253448Sdh155122 ipf_stack_t *ifs; 11262393Syz155240 { 11277176Syx160601 int i; 11287176Syx160601 11292393Syz155240 if (getlock) { 11303448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 11312393Syz155240 } 11322393Syz155240 if (n->in_redir & NAT_REDIRECT) 11332393Syz155240 nat_delrdr(n); 11342393Syz155240 if (n->in_redir & (NAT_MAPBLK|NAT_MAP)) 11352393Syz155240 nat_delnat(n); 11363448Sdh155122 if (ifs->ifs_nat_list == NULL) { 11373448Sdh155122 ifs->ifs_nat_masks = 0; 11383448Sdh155122 ifs->ifs_rdr_masks = 0; 11397176Syx160601 for (i = 0; i < 4; i++) { 11407176Syx160601 ifs->ifs_nat6_masks[i] = 0; 11417176Syx160601 ifs->ifs_rdr6_masks[i] = 0; 11427176Syx160601 } 11432393Syz155240 } 11442393Syz155240 11452393Syz155240 if (n->in_tqehead[0] != NULL) { 11462393Syz155240 if (fr_deletetimeoutqueue(n->in_tqehead[0]) == 0) { 11474817San207044 fr_freetimeoutqueue(n->in_tqehead[0], ifs); 11482393Syz155240 } 11492393Syz155240 } 11502393Syz155240 11512393Syz155240 if (n->in_tqehead[1] != NULL) { 11522393Syz155240 if (fr_deletetimeoutqueue(n->in_tqehead[1]) == 0) { 11533448Sdh155122 fr_freetimeoutqueue(n->in_tqehead[1], ifs); 11542393Syz155240 } 11552393Syz155240 } 11562393Syz155240 11572393Syz155240 *np = n->in_next; 11582393Syz155240 11592393Syz155240 if (n->in_use == 0) { 11602393Syz155240 if (n->in_apr) 11612393Syz155240 appr_free(n->in_apr); 11622393Syz155240 KFREE(n); 11633448Sdh155122 ifs->ifs_nat_stats.ns_rules--; 11642393Syz155240 } else { 11652393Syz155240 n->in_flags |= IPN_DELETE; 11662393Syz155240 n->in_next = NULL; 11672393Syz155240 } 11682393Syz155240 if (getlock) { 11693448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); /* READ/WRITE */ 11702393Syz155240 } 11712393Syz155240 } 11722393Syz155240 11732393Syz155240 11742393Syz155240 /* ------------------------------------------------------------------------ */ 11752393Syz155240 /* Function: fr_natgetsz */ 11762393Syz155240 /* Returns: int - 0 == success, != 0 is the error value. */ 11772393Syz155240 /* Parameters: data(I) - pointer to natget structure with kernel pointer */ 11782393Syz155240 /* get the size of. */ 11792393Syz155240 /* */ 11802393Syz155240 /* Handle SIOCSTGSZ. */ 11812393Syz155240 /* Return the size of the nat list entry to be copied back to user space. */ 11822393Syz155240 /* The size of the entry is stored in the ng_sz field and the enture natget */ 11832393Syz155240 /* structure is copied back to the user. */ 11842393Syz155240 /* ------------------------------------------------------------------------ */ 11853448Sdh155122 static int fr_natgetsz(data, ifs) 11862393Syz155240 caddr_t data; 11873448Sdh155122 ipf_stack_t *ifs; 11882393Syz155240 { 11892393Syz155240 ap_session_t *aps; 11902393Syz155240 nat_t *nat, *n; 11912393Syz155240 natget_t ng; 11927433SJohn.Ojemann@Sun.COM int err; 11937433SJohn.Ojemann@Sun.COM 11947433SJohn.Ojemann@Sun.COM err = BCOPYIN(data, &ng, sizeof(ng)); 11957433SJohn.Ojemann@Sun.COM if (err != 0) 11967433SJohn.Ojemann@Sun.COM return EFAULT; 11972393Syz155240 11982393Syz155240 nat = ng.ng_ptr; 11992393Syz155240 if (!nat) { 12003448Sdh155122 nat = ifs->ifs_nat_instances; 12012393Syz155240 ng.ng_sz = 0; 12022393Syz155240 /* 12032393Syz155240 * Empty list so the size returned is 0. Simple. 12042393Syz155240 */ 12052393Syz155240 if (nat == NULL) { 12067433SJohn.Ojemann@Sun.COM err = BCOPYOUT(&ng, data, sizeof(ng)); 12077433SJohn.Ojemann@Sun.COM if (err != 0) { 12087433SJohn.Ojemann@Sun.COM return EFAULT; 12097433SJohn.Ojemann@Sun.COM } else { 12107433SJohn.Ojemann@Sun.COM return 0; 12117433SJohn.Ojemann@Sun.COM } 12122393Syz155240 } 12132393Syz155240 } else { 12142393Syz155240 /* 12152393Syz155240 * Make sure the pointer we're copying from exists in the 12162393Syz155240 * current list of entries. Security precaution to prevent 12172393Syz155240 * copying of random kernel data. 12182393Syz155240 */ 12193448Sdh155122 for (n = ifs->ifs_nat_instances; n; n = n->nat_next) 12202393Syz155240 if (n == nat) 12212393Syz155240 break; 12222393Syz155240 if (!n) 12232393Syz155240 return ESRCH; 12242393Syz155240 } 12252393Syz155240 12262393Syz155240 /* 12272393Syz155240 * Incluse any space required for proxy data structures. 12282393Syz155240 */ 12292393Syz155240 ng.ng_sz = sizeof(nat_save_t); 12302393Syz155240 aps = nat->nat_aps; 12312393Syz155240 if (aps != NULL) { 12322393Syz155240 ng.ng_sz += sizeof(ap_session_t) - 4; 12332393Syz155240 if (aps->aps_data != 0) 12342393Syz155240 ng.ng_sz += aps->aps_psiz; 12352393Syz155240 } 12362393Syz155240 12377433SJohn.Ojemann@Sun.COM err = BCOPYOUT(&ng, data, sizeof(ng)); 12387433SJohn.Ojemann@Sun.COM if (err != 0) 12397433SJohn.Ojemann@Sun.COM return EFAULT; 12402393Syz155240 return 0; 12412393Syz155240 } 12422393Syz155240 12432393Syz155240 12442393Syz155240 /* ------------------------------------------------------------------------ */ 12452393Syz155240 /* Function: fr_natgetent */ 12462393Syz155240 /* Returns: int - 0 == success, != 0 is the error value. */ 12472393Syz155240 /* Parameters: data(I) - pointer to natget structure with kernel pointer */ 12482393Syz155240 /* to NAT structure to copy out. */ 12492393Syz155240 /* */ 12502393Syz155240 /* Handle SIOCSTGET. */ 12512393Syz155240 /* Copies out NAT entry to user space. Any additional data held for a */ 12522393Syz155240 /* proxy is also copied, as to is the NAT rule which was responsible for it */ 12532393Syz155240 /* ------------------------------------------------------------------------ */ 12543448Sdh155122 static int fr_natgetent(data, ifs) 12552393Syz155240 caddr_t data; 12563448Sdh155122 ipf_stack_t *ifs; 12572393Syz155240 { 12582393Syz155240 int error, outsize; 12592393Syz155240 ap_session_t *aps; 12602393Syz155240 nat_save_t *ipn, ipns; 12612393Syz155240 nat_t *n, *nat; 12622393Syz155240 12632393Syz155240 error = fr_inobj(data, &ipns, IPFOBJ_NATSAVE); 12642393Syz155240 if (error != 0) 12652393Syz155240 return error; 12662393Syz155240 12672393Syz155240 if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) 12682393Syz155240 return EINVAL; 12692393Syz155240 12702393Syz155240 KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize); 12712393Syz155240 if (ipn == NULL) 12722393Syz155240 return ENOMEM; 12732393Syz155240 12742393Syz155240 ipn->ipn_dsize = ipns.ipn_dsize; 12752393Syz155240 nat = ipns.ipn_next; 12762393Syz155240 if (nat == NULL) { 12773448Sdh155122 nat = ifs->ifs_nat_instances; 12782393Syz155240 if (nat == NULL) { 12793448Sdh155122 if (ifs->ifs_nat_instances == NULL) 12802393Syz155240 error = ENOENT; 12812393Syz155240 goto finished; 12822393Syz155240 } 12832393Syz155240 } else { 12842393Syz155240 /* 12852393Syz155240 * Make sure the pointer we're copying from exists in the 12862393Syz155240 * current list of entries. Security precaution to prevent 12872393Syz155240 * copying of random kernel data. 12882393Syz155240 */ 12893448Sdh155122 for (n = ifs->ifs_nat_instances; n; n = n->nat_next) 12902393Syz155240 if (n == nat) 12912393Syz155240 break; 12922393Syz155240 if (n == NULL) { 12932393Syz155240 error = ESRCH; 12942393Syz155240 goto finished; 12952393Syz155240 } 12962393Syz155240 } 12972393Syz155240 ipn->ipn_next = nat->nat_next; 12982393Syz155240 12992393Syz155240 /* 13002393Syz155240 * Copy the NAT structure. 13012393Syz155240 */ 13022393Syz155240 bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat)); 13032393Syz155240 13042393Syz155240 /* 13052393Syz155240 * If we have a pointer to the NAT rule it belongs to, save that too. 13062393Syz155240 */ 13072393Syz155240 if (nat->nat_ptr != NULL) 13082393Syz155240 bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat, 13092393Syz155240 sizeof(ipn->ipn_ipnat)); 13102393Syz155240 13112393Syz155240 /* 13122393Syz155240 * If we also know the NAT entry has an associated filter rule, 13132393Syz155240 * save that too. 13142393Syz155240 */ 13152393Syz155240 if (nat->nat_fr != NULL) 13162393Syz155240 bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr, 13172393Syz155240 sizeof(ipn->ipn_fr)); 13182393Syz155240 13192393Syz155240 /* 13202393Syz155240 * Last but not least, if there is an application proxy session set 13212393Syz155240 * up for this NAT entry, then copy that out too, including any 13222393Syz155240 * private data saved along side it by the proxy. 13232393Syz155240 */ 13242393Syz155240 aps = nat->nat_aps; 13252393Syz155240 outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data); 13262393Syz155240 if (aps != NULL) { 13272393Syz155240 char *s; 13282393Syz155240 13292393Syz155240 if (outsize < sizeof(*aps)) { 13302393Syz155240 error = ENOBUFS; 13312393Syz155240 goto finished; 13322393Syz155240 } 13332393Syz155240 13342393Syz155240 s = ipn->ipn_data; 13352393Syz155240 bcopy((char *)aps, s, sizeof(*aps)); 13362393Syz155240 s += sizeof(*aps); 13372393Syz155240 outsize -= sizeof(*aps); 13382393Syz155240 if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz)) 13392393Syz155240 bcopy(aps->aps_data, s, aps->aps_psiz); 13402393Syz155240 else 13412393Syz155240 error = ENOBUFS; 13422393Syz155240 } 13432393Syz155240 if (error == 0) { 13442393Syz155240 error = fr_outobjsz(data, ipn, IPFOBJ_NATSAVE, ipns.ipn_dsize); 13452393Syz155240 } 13462393Syz155240 13472393Syz155240 finished: 13482393Syz155240 if (ipn != NULL) { 13492393Syz155240 KFREES(ipn, ipns.ipn_dsize); 13502393Syz155240 } 13512393Syz155240 return error; 13522393Syz155240 } 13532393Syz155240 13546253San207044 /* ------------------------------------------------------------------------ */ 13556253San207044 /* Function: nat_calc_chksum_diffs */ 13566253San207044 /* Returns: void */ 13576253San207044 /* Parameters: nat - pointer to NAT table entry */ 13586253San207044 /* */ 13596253San207044 /* Function calculates chksum deltas for IP header (nat_ipsumd) and TCP/UDP */ 13606253San207044 /* headers (nat_sumd). The things for L4 (UDP/TCP) get complicated when */ 13616253San207044 /* we are dealing with partial chksum offload. For these cases we need to */ 13626253San207044 /* compute a 'partial chksum delta'. The 'partial chksum delta'is stored */ 13636253San207044 /* into nat_sumd[1], while ordinary chksum delta for TCP/UDP is in */ 13646253San207044 /* nat_sumd[0]. */ 13656253San207044 /* */ 13666253San207044 /* The function accepts initialized NAT table entry and computes the deltas */ 13676253San207044 /* from nat_inip/nat_outip members. The function is called right before */ 13686253San207044 /* the new entry is inserted into the table. */ 13696253San207044 /* */ 13706253San207044 /* The ipsumd (IP hedaer chksum delta adjustment) is computed as a chksum */ 13716253San207044 /* of delta between original and new IP addresses. */ 13726253San207044 /* */ 13736253San207044 /* the nat_sumd[0] (TCP/UDP header chksum delta adjustment) is computed as */ 13746253San207044 /* a chkusm of delta between original an new IP addrress:port tupples. */ 13756253San207044 /* */ 13766253San207044 /* Some facts about chksum, we should remember: */ 13776253San207044 /* IP header chksum covers IP header only */ 13786253San207044 /* */ 13796253San207044 /* TCP/UDP chksum covers data payload and so called pseudo header */ 13806253San207044 /* SRC, DST IP address */ 13816253San207044 /* SRC, DST Port */ 13826253San207044 /* length of payload */ 13836253San207044 /* */ 13846253San207044 /* The partial chksum delta (nat_sumd[1] is used to adjust db_ckusm16 */ 13856253San207044 /* member of dblk_t structure. The db_ckusm16 member is not part of */ 13866253San207044 /* IP/UDP/TCP header it is 16 bit value computed by NIC driver with partial */ 13876253San207044 /* chksum offload capacbility for every inbound packet. The db_cksum16 is */ 13886253San207044 /* stored along with other IP packet data in dblk_t structure and used in */ 13896253San207044 /* for IP/UDP/TCP chksum validation later in ip.c. */ 13906253San207044 /* */ 13916253San207044 /* The partial chksum delta (adjustment, nat_sumd[1]) is computed as chksum */ 13926253San207044 /* of delta between new and orig address. NOTE: the order of operands for */ 13936253San207044 /* partial delta operation is swapped compared to computing the IP/TCP/UDP */ 13946253San207044 /* header adjustment. It is by design see (IP_CKSUM_RECV() macro in ip.c). */ 13956253San207044 /* */ 13966253San207044 /* ------------------------------------------------------------------------ */ 13977176Syx160601 void nat_calc_chksum_diffs(nat) 13986253San207044 nat_t *nat; 13996253San207044 { 14006253San207044 u_32_t sum_orig = 0; 14016253San207044 u_32_t sum_changed = 0; 14026253San207044 u_32_t sumd; 14036253San207044 u_32_t ipsum_orig = 0; 14046253San207044 u_32_t ipsum_changed = 0; 14056253San207044 14067176Syx160601 if (nat->nat_v != 4 && nat->nat_v != 6) 14077176Syx160601 return; 14087176Syx160601 14096253San207044 /* 14106253San207044 * the switch calculates operands for CALC_SUMD(), 14116253San207044 * which will compute the partial chksum delta. 14126253San207044 */ 14136253San207044 switch (nat->nat_dir) 14146253San207044 { 14156253San207044 case NAT_INBOUND: 14166253San207044 /* 14176253San207044 * we are dealing with RDR rule (DST address gets 14186253San207044 * modified on packet from client) 14196253San207044 */ 14207176Syx160601 if (nat->nat_v == 4) { 14217176Syx160601 sum_changed = LONG_SUM(ntohl(nat->nat_inip.s_addr)); 14227176Syx160601 sum_orig = LONG_SUM(ntohl(nat->nat_outip.s_addr)); 14237176Syx160601 } else { 14247176Syx160601 sum_changed = LONG_SUM6(&nat->nat_inip6); 14257176Syx160601 sum_orig = LONG_SUM6(&nat->nat_outip6); 14267176Syx160601 } 14276253San207044 break; 14286253San207044 case NAT_OUTBOUND: 14296253San207044 /* 14306253San207044 * we are dealing with MAP rule (SRC address gets 14316253San207044 * modified on packet from client) 14326253San207044 */ 14337176Syx160601 if (nat->nat_v == 4) { 14347176Syx160601 sum_changed = LONG_SUM(ntohl(nat->nat_outip.s_addr)); 14357176Syx160601 sum_orig = LONG_SUM(ntohl(nat->nat_inip.s_addr)); 14367176Syx160601 } else { 14377176Syx160601 sum_changed = LONG_SUM6(&nat->nat_outip6); 14387176Syx160601 sum_orig = LONG_SUM6(&nat->nat_inip6); 14397176Syx160601 } 14406253San207044 break; 14416253San207044 default: ; 14426253San207044 break; 14436253San207044 } 14446253San207044 14456253San207044 /* 14466253San207044 * we also preserve CALC_SUMD() operands here, for IP chksum delta 14476253San207044 * calculation, which happens at the end of function. 14486253San207044 */ 14496253San207044 ipsum_changed = sum_changed; 14506253San207044 ipsum_orig = sum_orig; 14516253San207044 /* 14526253San207044 * NOTE: the order of operands for partial chksum adjustment 14536253San207044 * computation has to be swapped! 14546253San207044 */ 14556253San207044 CALC_SUMD(sum_changed, sum_orig, sumd); 14566253San207044 nat->nat_sumd[1] = (sumd & 0xffff) + (sumd >> 16); 14576253San207044 14586295San207044 if (nat->nat_flags & (IPN_TCPUDP | IPN_ICMPQUERY)) { 14596253San207044 14606253San207044 /* 14616253San207044 * switch calculates operands for CALC_SUMD(), which will 14626253San207044 * compute the full chksum delta. 14636253San207044 */ 14646253San207044 switch (nat->nat_dir) 14656253San207044 { 14666253San207044 case NAT_INBOUND: 14677176Syx160601 if (nat->nat_v == 4) { 14687176Syx160601 sum_changed = LONG_SUM( 14697176Syx160601 ntohl(nat->nat_inip.s_addr) + 14707176Syx160601 ntohs(nat->nat_inport)); 14717176Syx160601 sum_orig = LONG_SUM( 14727176Syx160601 ntohl(nat->nat_outip.s_addr) + 14737176Syx160601 ntohs(nat->nat_outport)); 14747176Syx160601 } else { 14757176Syx160601 sum_changed = LONG_SUM6(&nat->nat_inip6) + 14767176Syx160601 ntohs(nat->nat_inport); 14777176Syx160601 sum_orig = LONG_SUM6(&nat->nat_outip6) + 14787176Syx160601 ntohs(nat->nat_outport); 14797176Syx160601 } 14806253San207044 break; 14816253San207044 case NAT_OUTBOUND: 14827176Syx160601 if (nat->nat_v == 4) { 14837176Syx160601 sum_changed = LONG_SUM( 14847176Syx160601 ntohl(nat->nat_outip.s_addr) + 14857176Syx160601 ntohs(nat->nat_outport)); 14867176Syx160601 sum_orig = LONG_SUM( 14877176Syx160601 ntohl(nat->nat_inip.s_addr) + 14887176Syx160601 ntohs(nat->nat_inport)); 14897176Syx160601 } else { 14907176Syx160601 sum_changed = LONG_SUM6(&nat->nat_outip6) + 14917176Syx160601 ntohs(nat->nat_outport); 14927176Syx160601 sum_orig = LONG_SUM6(&nat->nat_inip6) + 14937176Syx160601 ntohs(nat->nat_inport); 14947176Syx160601 } 14956253San207044 break; 14966253San207044 default: ; 14976253San207044 break; 14986253San207044 } 14996253San207044 15006253San207044 CALC_SUMD(sum_orig, sum_changed, sumd); 15016253San207044 nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 15026295San207044 15036295San207044 if (!(nat->nat_flags & IPN_TCPUDP)) { 15046295San207044 /* 15056295San207044 * partial HW chksum offload works for TCP/UDP headers only, 15066295San207044 * so we need to enforce full chksum adjustment for ICMP 15076295San207044 */ 15086295San207044 nat->nat_sumd[1] = nat->nat_sumd[0]; 15096295San207044 } 15106253San207044 } 15116253San207044 else 15126253San207044 nat->nat_sumd[0] = nat->nat_sumd[1]; 15136253San207044 15146253San207044 /* 15156253San207044 * we may reuse the already computed nat_sumd[0] for IP header chksum 15166253San207044 * adjustment in case the L4 (TCP/UDP header) is not changed by NAT. 15176253San207044 */ 15187176Syx160601 if (nat->nat_v == 4) { 15197176Syx160601 if (NAT_HAS_L4_CHANGED(nat)) { 15207176Syx160601 /* 15217176Syx160601 * bad luck, NAT changes also the L4 header, use IP 15227176Syx160601 * addresses to compute chksum adjustment for IP header. 15237176Syx160601 */ 15247176Syx160601 CALC_SUMD(ipsum_orig, ipsum_changed, sumd); 15257176Syx160601 nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); 15267176Syx160601 } else { 15277176Syx160601 /* 15287176Syx160601 * the NAT does not change L4 hdr -> reuse chksum 15297176Syx160601 * adjustment for IP hdr. 15307176Syx160601 */ 15317176Syx160601 nat->nat_ipsumd = nat->nat_sumd[0]; 15327176Syx160601 15337176Syx160601 /* 15347176Syx160601 * if L4 header does not use chksum - zero out deltas 15357176Syx160601 */ 15367176Syx160601 if (!(nat->nat_flags & IPN_TCPUDP)) { 15377176Syx160601 nat->nat_sumd[0] = 0; 15387176Syx160601 nat->nat_sumd[1] = 0; 15397176Syx160601 } 15406295San207044 } 15416253San207044 } 15426253San207044 15436253San207044 return; 15446253San207044 } 15452393Syz155240 15462393Syz155240 /* ------------------------------------------------------------------------ */ 15472393Syz155240 /* Function: fr_natputent */ 15482393Syz155240 /* Returns: int - 0 == success, != 0 is the error value. */ 1549*8170SJohn.Ojemann@Sun.COM /* Parameters: data(I) - pointer to natget structure with NAT */ 1550*8170SJohn.Ojemann@Sun.COM /* structure information to load into the kernel */ 15512393Syz155240 /* getlock(I) - flag indicating whether or not a write lock */ 15522393Syz155240 /* on ipf_nat is already held. */ 1553*8170SJohn.Ojemann@Sun.COM /* ifs - ipf stack instance */ 15542393Syz155240 /* */ 15552393Syz155240 /* Handle SIOCSTPUT. */ 15562393Syz155240 /* Loads a NAT table entry from user space, including a NAT rule, proxy and */ 15572393Syz155240 /* firewall rule data structures, if pointers to them indicate so. */ 15582393Syz155240 /* ------------------------------------------------------------------------ */ 15593448Sdh155122 static int fr_natputent(data, getlock, ifs) 15602393Syz155240 caddr_t data; 15612393Syz155240 int getlock; 15623448Sdh155122 ipf_stack_t *ifs; 15632393Syz155240 { 15642393Syz155240 nat_save_t ipn, *ipnn; 15652393Syz155240 ap_session_t *aps; 15662393Syz155240 nat_t *n, *nat; 15672393Syz155240 frentry_t *fr; 15682393Syz155240 fr_info_t fin; 15692393Syz155240 ipnat_t *in; 15702393Syz155240 int error; 15712393Syz155240 15722393Syz155240 error = fr_inobj(data, &ipn, IPFOBJ_NATSAVE); 15732393Syz155240 if (error != 0) 15742393Syz155240 return error; 15752393Syz155240 15762393Syz155240 /* 1577*8170SJohn.Ojemann@Sun.COM * Trigger automatic call to nat_flushtable() if the 15784817San207044 * table has reached capcity specified by hi watermark. 15794817San207044 */ 1580*8170SJohn.Ojemann@Sun.COM if (NAT_TAB_WATER_LEVEL(ifs) > ifs->ifs_nat_flush_level_hi) 15814817San207044 ifs->ifs_nat_doflush = 1; 15824817San207044 15834817San207044 /* 15847432SJohn.Ojemann@Sun.COM * If automatic flushing did not do its job, and the table 15857432SJohn.Ojemann@Sun.COM * has filled up, don't try to create a new entry. 15867432SJohn.Ojemann@Sun.COM */ 15877432SJohn.Ojemann@Sun.COM if (ifs->ifs_nat_stats.ns_inuse >= ifs->ifs_ipf_nattable_max) { 15887432SJohn.Ojemann@Sun.COM ifs->ifs_nat_stats.ns_memfail++; 15897432SJohn.Ojemann@Sun.COM return ENOMEM; 15907432SJohn.Ojemann@Sun.COM } 15917432SJohn.Ojemann@Sun.COM 15927432SJohn.Ojemann@Sun.COM /* 15932393Syz155240 * Initialise early because of code at junkput label. 15942393Syz155240 */ 15952393Syz155240 in = NULL; 15962393Syz155240 aps = NULL; 15972393Syz155240 nat = NULL; 15982393Syz155240 ipnn = NULL; 15992393Syz155240 16002393Syz155240 /* 16012393Syz155240 * New entry, copy in the rest of the NAT entry if it's size is more 16022393Syz155240 * than just the nat_t structure. 16032393Syz155240 */ 16042393Syz155240 fr = NULL; 16052393Syz155240 if (ipn.ipn_dsize > sizeof(ipn)) { 16062393Syz155240 if (ipn.ipn_dsize > 81920) { 16072393Syz155240 error = ENOMEM; 16082393Syz155240 goto junkput; 16092393Syz155240 } 16102393Syz155240 16112393Syz155240 KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize); 16122393Syz155240 if (ipnn == NULL) 16132393Syz155240 return ENOMEM; 16142393Syz155240 16152393Syz155240 error = fr_inobjsz(data, ipnn, IPFOBJ_NATSAVE, ipn.ipn_dsize); 16162393Syz155240 if (error != 0) { 16172393Syz155240 error = EFAULT; 16182393Syz155240 goto junkput; 16192393Syz155240 } 16202393Syz155240 } else 16212393Syz155240 ipnn = &ipn; 16222393Syz155240 16232393Syz155240 KMALLOC(nat, nat_t *); 16242393Syz155240 if (nat == NULL) { 16252393Syz155240 error = ENOMEM; 16262393Syz155240 goto junkput; 16272393Syz155240 } 16282393Syz155240 16292393Syz155240 bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat)); 16302393Syz155240 /* 16312393Syz155240 * Initialize all these so that nat_delete() doesn't cause a crash. 16322393Syz155240 */ 16332393Syz155240 bzero((char *)nat, offsetof(struct nat, nat_tqe)); 16342393Syz155240 nat->nat_tqe.tqe_pnext = NULL; 16352393Syz155240 nat->nat_tqe.tqe_next = NULL; 16362393Syz155240 nat->nat_tqe.tqe_ifq = NULL; 16372393Syz155240 nat->nat_tqe.tqe_parent = nat; 16382393Syz155240 16392393Syz155240 /* 16402393Syz155240 * Restore the rule associated with this nat session 16412393Syz155240 */ 16422393Syz155240 in = ipnn->ipn_nat.nat_ptr; 16432393Syz155240 if (in != NULL) { 16442393Syz155240 KMALLOC(in, ipnat_t *); 16452393Syz155240 nat->nat_ptr = in; 16462393Syz155240 if (in == NULL) { 16472393Syz155240 error = ENOMEM; 16482393Syz155240 goto junkput; 16492393Syz155240 } 16502393Syz155240 bzero((char *)in, offsetof(struct ipnat, in_next6)); 16512393Syz155240 bcopy((char *)&ipnn->ipn_ipnat, (char *)in, sizeof(*in)); 16522393Syz155240 in->in_use = 1; 16532393Syz155240 in->in_flags |= IPN_DELETE; 16542393Syz155240 16553448Sdh155122 ATOMIC_INC(ifs->ifs_nat_stats.ns_rules); 16563448Sdh155122 16574380Sjojemann if (nat_resolverule(in, ifs) != 0) { 16584380Sjojemann error = ESRCH; 16594380Sjojemann goto junkput; 16604380Sjojemann } 16612393Syz155240 } 16622393Syz155240 16632393Syz155240 /* 16642393Syz155240 * Check that the NAT entry doesn't already exist in the kernel. 16652393Syz155240 */ 16667176Syx160601 if (nat->nat_v != 6) 16677176Syx160601 nat->nat_v = 4; 16682393Syz155240 bzero((char *)&fin, sizeof(fin)); 16692393Syz155240 fin.fin_p = nat->nat_p; 16704414Sjojemann fin.fin_ifs = ifs; 16712393Syz155240 if (nat->nat_dir == NAT_OUTBOUND) { 16722393Syz155240 fin.fin_data[0] = ntohs(nat->nat_oport); 16732393Syz155240 fin.fin_data[1] = ntohs(nat->nat_outport); 16742508Syz155240 fin.fin_ifp = nat->nat_ifps[0]; 16752393Syz155240 if (getlock) { 16763448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 16772393Syz155240 } 16787176Syx160601 16797176Syx160601 switch (nat->nat_v) 16807176Syx160601 { 16817176Syx160601 case 4: 16827176Syx160601 fin.fin_v = nat->nat_v; 16837176Syx160601 n = nat_inlookup(&fin, nat->nat_flags, fin.fin_p, 16847176Syx160601 nat->nat_oip, nat->nat_outip); 16857176Syx160601 break; 16867176Syx160601 #ifdef USE_INET6 16877176Syx160601 case 6: 16887176Syx160601 n = nat6_inlookup(&fin, nat->nat_flags, fin.fin_p, 16897176Syx160601 &nat->nat_oip6.in6, &nat->nat_outip6.in6); 16907176Syx160601 break; 16917176Syx160601 #endif 16927176Syx160601 default: 16937176Syx160601 n = NULL; 16947176Syx160601 break; 16957176Syx160601 } 16967176Syx160601 16972393Syz155240 if (getlock) { 16983448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 16992393Syz155240 } 17002393Syz155240 if (n != NULL) { 17012393Syz155240 error = EEXIST; 17022393Syz155240 goto junkput; 17032393Syz155240 } 17042393Syz155240 } else if (nat->nat_dir == NAT_INBOUND) { 17052393Syz155240 fin.fin_data[0] = ntohs(nat->nat_inport); 17062393Syz155240 fin.fin_data[1] = ntohs(nat->nat_oport); 17072508Syz155240 fin.fin_ifp = nat->nat_ifps[1]; 17082393Syz155240 if (getlock) { 17093448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 17102393Syz155240 } 17117176Syx160601 17127176Syx160601 switch (nat->nat_v) 17137176Syx160601 { 17147176Syx160601 case 4: 17157176Syx160601 n = nat_outlookup(&fin, nat->nat_flags, fin.fin_p, 17167176Syx160601 nat->nat_inip, nat->nat_oip); 17177176Syx160601 break; 17187176Syx160601 #ifdef USE_INET6 17197176Syx160601 case 6: 17207176Syx160601 n = nat6_outlookup(&fin, nat->nat_flags, fin.fin_p, 17217176Syx160601 &nat->nat_inip6.in6, &nat->nat_oip6.in6); 17227176Syx160601 break; 17237176Syx160601 #endif 17247176Syx160601 default: 17257176Syx160601 n = NULL; 17267176Syx160601 break; 17277176Syx160601 } 17287176Syx160601 17292393Syz155240 if (getlock) { 17303448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 17312393Syz155240 } 17322393Syz155240 if (n != NULL) { 17332393Syz155240 error = EEXIST; 17342393Syz155240 goto junkput; 17352393Syz155240 } 17362393Syz155240 } else { 17372393Syz155240 error = EINVAL; 17382393Syz155240 goto junkput; 17392393Syz155240 } 17402393Syz155240 17412393Syz155240 /* 17422393Syz155240 * Restore ap_session_t structure. Include the private data allocated 17432393Syz155240 * if it was there. 17442393Syz155240 */ 17452393Syz155240 aps = nat->nat_aps; 17462393Syz155240 if (aps != NULL) { 17472393Syz155240 KMALLOC(aps, ap_session_t *); 17482393Syz155240 nat->nat_aps = aps; 17492393Syz155240 if (aps == NULL) { 17502393Syz155240 error = ENOMEM; 17512393Syz155240 goto junkput; 17522393Syz155240 } 17532393Syz155240 bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps)); 17542393Syz155240 if (in != NULL) 17552393Syz155240 aps->aps_apr = in->in_apr; 17562393Syz155240 else 17572393Syz155240 aps->aps_apr = NULL; 17582393Syz155240 if (aps->aps_psiz != 0) { 17592393Syz155240 if (aps->aps_psiz > 81920) { 17602393Syz155240 error = ENOMEM; 17612393Syz155240 goto junkput; 17622393Syz155240 } 17632393Syz155240 KMALLOCS(aps->aps_data, void *, aps->aps_psiz); 17642393Syz155240 if (aps->aps_data == NULL) { 17652393Syz155240 error = ENOMEM; 17662393Syz155240 goto junkput; 17672393Syz155240 } 17682393Syz155240 bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data, 17692393Syz155240 aps->aps_psiz); 17702393Syz155240 } else { 17712393Syz155240 aps->aps_psiz = 0; 17722393Syz155240 aps->aps_data = NULL; 17732393Syz155240 } 17742393Syz155240 } 17752393Syz155240 17762393Syz155240 /* 17772393Syz155240 * If there was a filtering rule associated with this entry then 17782393Syz155240 * build up a new one. 17792393Syz155240 */ 17802393Syz155240 fr = nat->nat_fr; 17812393Syz155240 if (fr != NULL) { 17822393Syz155240 if ((nat->nat_flags & SI_NEWFR) != 0) { 17832393Syz155240 KMALLOC(fr, frentry_t *); 17842393Syz155240 nat->nat_fr = fr; 17852393Syz155240 if (fr == NULL) { 17862393Syz155240 error = ENOMEM; 17872393Syz155240 goto junkput; 17882393Syz155240 } 17892393Syz155240 ipnn->ipn_nat.nat_fr = fr; 17902393Syz155240 (void) fr_outobj(data, ipnn, IPFOBJ_NATSAVE); 17912393Syz155240 bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr)); 17924380Sjojemann 17934380Sjojemann fr->fr_ref = 1; 17944380Sjojemann fr->fr_dsize = 0; 17954380Sjojemann fr->fr_data = NULL; 17964380Sjojemann fr->fr_type = FR_T_NONE; 17974380Sjojemann 17982393Syz155240 MUTEX_NUKE(&fr->fr_lock); 17992393Syz155240 MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock"); 18002393Syz155240 } else { 18014380Sjojemann if (getlock) { 18024380Sjojemann READ_ENTER(&ifs->ifs_ipf_nat); 18034380Sjojemann } 18043448Sdh155122 for (n = ifs->ifs_nat_instances; n; n = n->nat_next) 18052393Syz155240 if (n->nat_fr == fr) 18062393Syz155240 break; 18072393Syz155240 18082393Syz155240 if (n != NULL) { 18092393Syz155240 MUTEX_ENTER(&fr->fr_lock); 18102393Syz155240 fr->fr_ref++; 18112393Syz155240 MUTEX_EXIT(&fr->fr_lock); 18122393Syz155240 } 18134380Sjojemann if (getlock) { 18144380Sjojemann RWLOCK_EXIT(&ifs->ifs_ipf_nat); 18154380Sjojemann } 18162393Syz155240 if (!n) { 18172393Syz155240 error = ESRCH; 18182393Syz155240 goto junkput; 18192393Syz155240 } 18202393Syz155240 } 18212393Syz155240 } 18222393Syz155240 18232393Syz155240 if (ipnn != &ipn) { 18242393Syz155240 KFREES(ipnn, ipn.ipn_dsize); 18252393Syz155240 ipnn = NULL; 18262393Syz155240 } 18272393Syz155240 18286253San207044 nat_calc_chksum_diffs(nat); 18296253San207044 18302393Syz155240 if (getlock) { 18313448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 18322393Syz155240 } 18337176Syx160601 18347176Syx160601 nat_calc_chksum_diffs(nat); 18357176Syx160601 18367176Syx160601 switch (nat->nat_v) 18377176Syx160601 { 18387176Syx160601 case 4 : 18397176Syx160601 error = nat_insert(nat, nat->nat_rev, ifs); 18407176Syx160601 break; 18417176Syx160601 #ifdef USE_INET6 18427176Syx160601 case 6 : 18437176Syx160601 error = nat6_insert(nat, nat->nat_rev, ifs); 18447176Syx160601 break; 18457176Syx160601 #endif 18467176Syx160601 default : 18477176Syx160601 break; 18487176Syx160601 } 18497176Syx160601 18502393Syz155240 if ((error == 0) && (aps != NULL)) { 18513448Sdh155122 aps->aps_next = ifs->ifs_ap_sess_list; 18523448Sdh155122 ifs->ifs_ap_sess_list = aps; 18532393Syz155240 } 18542393Syz155240 if (getlock) { 18553448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 18562393Syz155240 } 18572393Syz155240 18582393Syz155240 if (error == 0) 18592393Syz155240 return 0; 18602393Syz155240 18612393Syz155240 error = ENOMEM; 18622393Syz155240 18632393Syz155240 junkput: 18642393Syz155240 if (fr != NULL) 18653448Sdh155122 (void) fr_derefrule(&fr, ifs); 18662393Syz155240 18672393Syz155240 if ((ipnn != NULL) && (ipnn != &ipn)) { 18682393Syz155240 KFREES(ipnn, ipn.ipn_dsize); 18692393Syz155240 } 18702393Syz155240 if (nat != NULL) { 18712393Syz155240 if (aps != NULL) { 18722393Syz155240 if (aps->aps_data != NULL) { 18732393Syz155240 KFREES(aps->aps_data, aps->aps_psiz); 18742393Syz155240 } 18752393Syz155240 KFREE(aps); 18762393Syz155240 } 18772393Syz155240 if (in != NULL) { 18782393Syz155240 if (in->in_apr) 18792393Syz155240 appr_free(in->in_apr); 18802393Syz155240 KFREE(in); 18812393Syz155240 } 18822393Syz155240 KFREE(nat); 18832393Syz155240 } 18842393Syz155240 return error; 18852393Syz155240 } 18862393Syz155240 18872393Syz155240 18882393Syz155240 /* ------------------------------------------------------------------------ */ 18892393Syz155240 /* Function: nat_delete */ 1890*8170SJohn.Ojemann@Sun.COM /* Returns: int - 0 if entry deleted. Otherwise, ref count on entry */ 1891*8170SJohn.Ojemann@Sun.COM /* Parameters: nat - pointer to the NAT entry to delete */ 1892*8170SJohn.Ojemann@Sun.COM /* logtype - type of LOG record to create before deleting */ 1893*8170SJohn.Ojemann@Sun.COM /* ifs - ipf stack instance */ 18942393Syz155240 /* Write Lock: ipf_nat */ 18952393Syz155240 /* */ 18962393Syz155240 /* Delete a nat entry from the various lists and table. If NAT logging is */ 18972393Syz155240 /* enabled then generate a NAT log record for this event. */ 18982393Syz155240 /* ------------------------------------------------------------------------ */ 1899*8170SJohn.Ojemann@Sun.COM int nat_delete(nat, logtype, ifs) 19002393Syz155240 struct nat *nat; 19012393Syz155240 int logtype; 19023448Sdh155122 ipf_stack_t *ifs; 19032393Syz155240 { 19042393Syz155240 struct ipnat *ipn; 19057432SJohn.Ojemann@Sun.COM int removed = 0; 19062393Syz155240 19073448Sdh155122 if (logtype != 0 && ifs->ifs_nat_logging != 0) 19083448Sdh155122 nat_log(nat, logtype, ifs); 19093448Sdh155122 19102393Syz155240 /* 19117432SJohn.Ojemann@Sun.COM * Start by removing the entry from the hash table of nat entries 19127432SJohn.Ojemann@Sun.COM * so it will not be "used" again. 19137432SJohn.Ojemann@Sun.COM * 19147432SJohn.Ojemann@Sun.COM * It will remain in the "list" of nat entries until all references 19157432SJohn.Ojemann@Sun.COM * have been accounted for. 19162393Syz155240 */ 19177432SJohn.Ojemann@Sun.COM if ((nat->nat_phnext[0] != NULL) && (nat->nat_phnext[1] != NULL)) { 19187432SJohn.Ojemann@Sun.COM removed = 1; 19197432SJohn.Ojemann@Sun.COM 19203448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--; 19213448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--; 19222393Syz155240 19232393Syz155240 *nat->nat_phnext[0] = nat->nat_hnext[0]; 19242393Syz155240 if (nat->nat_hnext[0] != NULL) { 19252393Syz155240 nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 19262393Syz155240 nat->nat_hnext[0] = NULL; 19272393Syz155240 } 19282393Syz155240 nat->nat_phnext[0] = NULL; 19292393Syz155240 19302393Syz155240 *nat->nat_phnext[1] = nat->nat_hnext[1]; 19312393Syz155240 if (nat->nat_hnext[1] != NULL) { 19322393Syz155240 nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 19332393Syz155240 nat->nat_hnext[1] = NULL; 19342393Syz155240 } 19352393Syz155240 nat->nat_phnext[1] = NULL; 19362393Syz155240 19372393Syz155240 if ((nat->nat_flags & SI_WILDP) != 0) 19383448Sdh155122 ifs->ifs_nat_stats.ns_wilds--; 19392393Syz155240 } 19402393Syz155240 19417432SJohn.Ojemann@Sun.COM /* 19427432SJohn.Ojemann@Sun.COM * Next, remove it from the timeout queue it is in. 19437432SJohn.Ojemann@Sun.COM */ 19447432SJohn.Ojemann@Sun.COM fr_deletequeueentry(&nat->nat_tqe); 19457432SJohn.Ojemann@Sun.COM 19462393Syz155240 if (nat->nat_me != NULL) { 19472393Syz155240 *nat->nat_me = NULL; 19482393Syz155240 nat->nat_me = NULL; 19492393Syz155240 } 19502393Syz155240 19515055Sdr146992 MUTEX_ENTER(&nat->nat_lock); 19527432SJohn.Ojemann@Sun.COM if (logtype == NL_DESTROY) { 19537432SJohn.Ojemann@Sun.COM /* 19547432SJohn.Ojemann@Sun.COM * NL_DESTROY should only be passed when nat_ref >= 2. 19557432SJohn.Ojemann@Sun.COM * This happens when a nat'd packet is blocked, we have 19567432SJohn.Ojemann@Sun.COM * just created the nat table entry (reason why the ref 19577432SJohn.Ojemann@Sun.COM * count is 2 or higher), but and we want to throw away 19587432SJohn.Ojemann@Sun.COM * that NAT session as result of the blocked packet. 19597432SJohn.Ojemann@Sun.COM */ 19607432SJohn.Ojemann@Sun.COM if (nat->nat_ref > 2) { 19617432SJohn.Ojemann@Sun.COM nat->nat_ref -= 2; 19627432SJohn.Ojemann@Sun.COM MUTEX_EXIT(&nat->nat_lock); 19637432SJohn.Ojemann@Sun.COM if (removed) 19647432SJohn.Ojemann@Sun.COM ifs->ifs_nat_stats.ns_orphans++; 1965*8170SJohn.Ojemann@Sun.COM return (nat->nat_ref); 19667432SJohn.Ojemann@Sun.COM } 19677432SJohn.Ojemann@Sun.COM } else if (nat->nat_ref > 1) { 19685055Sdr146992 nat->nat_ref--; 19695055Sdr146992 MUTEX_EXIT(&nat->nat_lock); 19707432SJohn.Ojemann@Sun.COM if (removed) 19717432SJohn.Ojemann@Sun.COM ifs->ifs_nat_stats.ns_orphans++; 1972*8170SJohn.Ojemann@Sun.COM return (nat->nat_ref); 19732393Syz155240 } 19745055Sdr146992 MUTEX_EXIT(&nat->nat_lock); 19755055Sdr146992 19767432SJohn.Ojemann@Sun.COM nat->nat_ref = 0; 19777432SJohn.Ojemann@Sun.COM 19785055Sdr146992 /* 19797432SJohn.Ojemann@Sun.COM * If entry had already been removed, 19807432SJohn.Ojemann@Sun.COM * it means we're cleaning up an orphan. 19815055Sdr146992 */ 19827432SJohn.Ojemann@Sun.COM if (!removed) 19837432SJohn.Ojemann@Sun.COM ifs->ifs_nat_stats.ns_orphans--; 19842393Syz155240 19852393Syz155240 #ifdef IPFILTER_SYNC 19862393Syz155240 if (nat->nat_sync) 19872393Syz155240 ipfsync_del(nat->nat_sync); 19882393Syz155240 #endif 19892393Syz155240 19907432SJohn.Ojemann@Sun.COM /* 19917432SJohn.Ojemann@Sun.COM * Now remove it from master list of nat table entries 19927432SJohn.Ojemann@Sun.COM */ 19937432SJohn.Ojemann@Sun.COM if (nat->nat_pnext != NULL) { 19947432SJohn.Ojemann@Sun.COM *nat->nat_pnext = nat->nat_next; 19957432SJohn.Ojemann@Sun.COM if (nat->nat_next != NULL) { 19967432SJohn.Ojemann@Sun.COM nat->nat_next->nat_pnext = nat->nat_pnext; 19977432SJohn.Ojemann@Sun.COM nat->nat_next = NULL; 19987432SJohn.Ojemann@Sun.COM } 19997432SJohn.Ojemann@Sun.COM nat->nat_pnext = NULL; 20007432SJohn.Ojemann@Sun.COM } 2001*8170SJohn.Ojemann@Sun.COM 20022393Syz155240 if (nat->nat_fr != NULL) 20033448Sdh155122 (void)fr_derefrule(&nat->nat_fr, ifs); 20042393Syz155240 20052393Syz155240 if (nat->nat_hm != NULL) 20065417Sjojemann fr_hostmapdel(&nat->nat_hm); 20072393Syz155240 20082393Syz155240 /* 20092393Syz155240 * If there is an active reference from the nat entry to its parent 20102393Syz155240 * rule, decrement the rule's reference count and free it too if no 20112393Syz155240 * longer being used. 20122393Syz155240 */ 20132393Syz155240 ipn = nat->nat_ptr; 20142393Syz155240 if (ipn != NULL) { 20152393Syz155240 ipn->in_space++; 20162393Syz155240 ipn->in_use--; 20172393Syz155240 if (ipn->in_use == 0 && (ipn->in_flags & IPN_DELETE)) { 20182393Syz155240 if (ipn->in_apr) 20192393Syz155240 appr_free(ipn->in_apr); 20202393Syz155240 KFREE(ipn); 20213448Sdh155122 ifs->ifs_nat_stats.ns_rules--; 20222393Syz155240 } 20232393Syz155240 } 20242393Syz155240 20252393Syz155240 MUTEX_DESTROY(&nat->nat_lock); 20262393Syz155240 20273448Sdh155122 aps_free(nat->nat_aps, ifs); 20283448Sdh155122 ifs->ifs_nat_stats.ns_inuse--; 20292393Syz155240 20302393Syz155240 /* 20312393Syz155240 * If there's a fragment table entry too for this nat entry, then 20322393Syz155240 * dereference that as well. This is after nat_lock is released 20332393Syz155240 * because of Tru64. 20342393Syz155240 */ 20353448Sdh155122 fr_forgetnat((void *)nat, ifs); 20362393Syz155240 20372393Syz155240 KFREE(nat); 2038*8170SJohn.Ojemann@Sun.COM 2039*8170SJohn.Ojemann@Sun.COM return (0); 20402393Syz155240 } 20412393Syz155240 20422393Syz155240 20432393Syz155240 /* ------------------------------------------------------------------------ */ 20442393Syz155240 /* Function: nat_clearlist */ 20452393Syz155240 /* Returns: int - number of NAT/RDR rules deleted */ 20462393Syz155240 /* Parameters: Nil */ 20472393Syz155240 /* */ 20482393Syz155240 /* Delete all rules in the current list of rules. There is nothing elegant */ 20492393Syz155240 /* about this cleanup: simply free all entries on the list of rules and */ 20502393Syz155240 /* clear out the tables used for hashed NAT rule lookups. */ 20512393Syz155240 /* ------------------------------------------------------------------------ */ 20523448Sdh155122 static int nat_clearlist(ifs) 20533448Sdh155122 ipf_stack_t *ifs; 20542393Syz155240 { 20553448Sdh155122 ipnat_t *n, **np = &ifs->ifs_nat_list; 20562393Syz155240 int i = 0; 20572393Syz155240 20583448Sdh155122 if (ifs->ifs_nat_rules != NULL) 20593448Sdh155122 bzero((char *)ifs->ifs_nat_rules, 20603448Sdh155122 sizeof(*ifs->ifs_nat_rules) * ifs->ifs_ipf_natrules_sz); 20613448Sdh155122 if (ifs->ifs_rdr_rules != NULL) 20623448Sdh155122 bzero((char *)ifs->ifs_rdr_rules, 20633448Sdh155122 sizeof(*ifs->ifs_rdr_rules) * ifs->ifs_ipf_rdrrules_sz); 20642393Syz155240 20652393Syz155240 while ((n = *np) != NULL) { 20662393Syz155240 *np = n->in_next; 20672393Syz155240 if (n->in_use == 0) { 20682393Syz155240 if (n->in_apr != NULL) 20692393Syz155240 appr_free(n->in_apr); 20702393Syz155240 KFREE(n); 20713448Sdh155122 ifs->ifs_nat_stats.ns_rules--; 20722393Syz155240 } else { 20732393Syz155240 n->in_flags |= IPN_DELETE; 20742393Syz155240 n->in_next = NULL; 20752393Syz155240 } 20762393Syz155240 i++; 20772393Syz155240 } 20783448Sdh155122 ifs->ifs_nat_masks = 0; 20793448Sdh155122 ifs->ifs_rdr_masks = 0; 20807176Syx160601 for (i = 0; i < 4; i++) { 20817176Syx160601 ifs->ifs_nat6_masks[i] = 0; 20827176Syx160601 ifs->ifs_rdr6_masks[i] = 0; 20837176Syx160601 } 20842393Syz155240 return i; 20852393Syz155240 } 20862393Syz155240 20872393Syz155240 20882393Syz155240 /* ------------------------------------------------------------------------ */ 20892393Syz155240 /* Function: nat_newmap */ 20902393Syz155240 /* Returns: int - -1 == error, 0 == success */ 20912393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 20922393Syz155240 /* nat(I) - pointer to NAT entry */ 20932393Syz155240 /* ni(I) - pointer to structure with misc. information needed */ 20942393Syz155240 /* to create new NAT entry. */ 20952393Syz155240 /* */ 20962393Syz155240 /* Given an empty NAT structure, populate it with new information about a */ 20972393Syz155240 /* new NAT session, as defined by the matching NAT rule. */ 20982393Syz155240 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 20992393Syz155240 /* to the new IP address for the translation. */ 21002393Syz155240 /* ------------------------------------------------------------------------ */ 21012393Syz155240 static INLINE int nat_newmap(fin, nat, ni) 21022393Syz155240 fr_info_t *fin; 21032393Syz155240 nat_t *nat; 21042393Syz155240 natinfo_t *ni; 21052393Syz155240 { 21062393Syz155240 u_short st_port, dport, sport, port, sp, dp; 21072393Syz155240 struct in_addr in, inb; 21082393Syz155240 hostmap_t *hm; 21092393Syz155240 u_32_t flags; 21102393Syz155240 u_32_t st_ip; 21112393Syz155240 ipnat_t *np; 21122393Syz155240 nat_t *natl; 21132393Syz155240 int l; 21143448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 21152393Syz155240 21162393Syz155240 /* 21172393Syz155240 * If it's an outbound packet which doesn't match any existing 21182393Syz155240 * record, then create a new port 21192393Syz155240 */ 21202393Syz155240 l = 0; 21212393Syz155240 hm = NULL; 21222393Syz155240 np = ni->nai_np; 21232393Syz155240 st_ip = np->in_nip; 21242393Syz155240 st_port = np->in_pnext; 21252393Syz155240 flags = ni->nai_flags; 21262393Syz155240 sport = ni->nai_sport; 21272393Syz155240 dport = ni->nai_dport; 21282393Syz155240 21292393Syz155240 /* 21302393Syz155240 * Do a loop until we either run out of entries to try or we find 21312393Syz155240 * a NAT mapping that isn't currently being used. This is done 21322393Syz155240 * because the change to the source is not (usually) being fixed. 21332393Syz155240 */ 21342393Syz155240 do { 21352393Syz155240 port = 0; 21362393Syz155240 in.s_addr = htonl(np->in_nip); 21372393Syz155240 if (l == 0) { 21382393Syz155240 /* 21392393Syz155240 * Check to see if there is an existing NAT 21402393Syz155240 * setup for this IP address pair. 21412393Syz155240 */ 21422393Syz155240 hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, 21433448Sdh155122 in, 0, ifs); 21442393Syz155240 if (hm != NULL) 21452393Syz155240 in.s_addr = hm->hm_mapip.s_addr; 21462393Syz155240 } else if ((l == 1) && (hm != NULL)) { 21475417Sjojemann fr_hostmapdel(&hm); 21482393Syz155240 } 21492393Syz155240 in.s_addr = ntohl(in.s_addr); 21502393Syz155240 21512393Syz155240 nat->nat_hm = hm; 21522393Syz155240 21532393Syz155240 if ((np->in_outmsk == 0xffffffff) && (np->in_pnext == 0)) { 21542393Syz155240 if (l > 0) 21552393Syz155240 return -1; 21562393Syz155240 } 21572393Syz155240 21582393Syz155240 if (np->in_redir == NAT_BIMAP && 21592393Syz155240 np->in_inmsk == np->in_outmsk) { 21602393Syz155240 /* 21612393Syz155240 * map the address block in a 1:1 fashion 21622393Syz155240 */ 21632393Syz155240 in.s_addr = np->in_outip; 21642393Syz155240 in.s_addr |= fin->fin_saddr & ~np->in_inmsk; 21652393Syz155240 in.s_addr = ntohl(in.s_addr); 21662393Syz155240 21672393Syz155240 } else if (np->in_redir & NAT_MAPBLK) { 21682393Syz155240 if ((l >= np->in_ppip) || ((l > 0) && 21692393Syz155240 !(flags & IPN_TCPUDP))) 21702393Syz155240 return -1; 21712393Syz155240 /* 21722393Syz155240 * map-block - Calculate destination address. 21732393Syz155240 */ 21742393Syz155240 in.s_addr = ntohl(fin->fin_saddr); 21752393Syz155240 in.s_addr &= ntohl(~np->in_inmsk); 21762393Syz155240 inb.s_addr = in.s_addr; 21772393Syz155240 in.s_addr /= np->in_ippip; 21782393Syz155240 in.s_addr &= ntohl(~np->in_outmsk); 21792393Syz155240 in.s_addr += ntohl(np->in_outip); 21802393Syz155240 /* 21812393Syz155240 * Calculate destination port. 21822393Syz155240 */ 21832393Syz155240 if ((flags & IPN_TCPUDP) && 21842393Syz155240 (np->in_ppip != 0)) { 21852393Syz155240 port = ntohs(sport) + l; 21862393Syz155240 port %= np->in_ppip; 21872393Syz155240 port += np->in_ppip * 21882393Syz155240 (inb.s_addr % np->in_ippip); 21892393Syz155240 port += MAPBLK_MINPORT; 21902393Syz155240 port = htons(port); 21912393Syz155240 } 21922393Syz155240 21932393Syz155240 } else if ((np->in_outip == 0) && 21942393Syz155240 (np->in_outmsk == 0xffffffff)) { 21952393Syz155240 /* 21962393Syz155240 * 0/32 - use the interface's IP address. 21972393Syz155240 */ 21982393Syz155240 if ((l > 0) || 21992393Syz155240 fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp, 22003448Sdh155122 &in, NULL, fin->fin_ifs) == -1) 22012393Syz155240 return -1; 22022393Syz155240 in.s_addr = ntohl(in.s_addr); 22032393Syz155240 22042393Syz155240 } else if ((np->in_outip == 0) && (np->in_outmsk == 0)) { 22052393Syz155240 /* 22062393Syz155240 * 0/0 - use the original source address/port. 22072393Syz155240 */ 22082393Syz155240 if (l > 0) 22092393Syz155240 return -1; 22102393Syz155240 in.s_addr = ntohl(fin->fin_saddr); 22112393Syz155240 22122393Syz155240 } else if ((np->in_outmsk != 0xffffffff) && 22132393Syz155240 (np->in_pnext == 0) && ((l > 0) || (hm == NULL))) 22142393Syz155240 np->in_nip++; 22152393Syz155240 22162393Syz155240 natl = NULL; 22172393Syz155240 22182393Syz155240 if ((flags & IPN_TCPUDP) && 22192393Syz155240 ((np->in_redir & NAT_MAPBLK) == 0) && 22202393Syz155240 (np->in_flags & IPN_AUTOPORTMAP)) { 22212393Syz155240 /* 22222393Syz155240 * "ports auto" (without map-block) 22232393Syz155240 */ 22242393Syz155240 if ((l > 0) && (l % np->in_ppip == 0)) { 22252393Syz155240 if (l > np->in_space) { 22262393Syz155240 return -1; 22272393Syz155240 } else if ((l > np->in_ppip) && 22282393Syz155240 np->in_outmsk != 0xffffffff) 22292393Syz155240 np->in_nip++; 22302393Syz155240 } 22312393Syz155240 if (np->in_ppip != 0) { 22322393Syz155240 port = ntohs(sport); 22332393Syz155240 port += (l % np->in_ppip); 22342393Syz155240 port %= np->in_ppip; 22352393Syz155240 port += np->in_ppip * 22362393Syz155240 (ntohl(fin->fin_saddr) % 22372393Syz155240 np->in_ippip); 22382393Syz155240 port += MAPBLK_MINPORT; 22392393Syz155240 port = htons(port); 22402393Syz155240 } 22412393Syz155240 22422393Syz155240 } else if (((np->in_redir & NAT_MAPBLK) == 0) && 22432393Syz155240 (flags & IPN_TCPUDPICMP) && (np->in_pnext != 0)) { 22442393Syz155240 /* 22452393Syz155240 * Standard port translation. Select next port. 22462393Syz155240 */ 22477259Sdr146992 if (np->in_flags & IPN_SEQUENTIAL) { 22487259Sdr146992 port = np->in_pnext; 22497259Sdr146992 } else { 22507259Sdr146992 port = ipf_random() % (ntohs(np->in_pmax) - 22517259Sdr146992 ntohs(np->in_pmin)); 22527259Sdr146992 port += ntohs(np->in_pmin); 22537259Sdr146992 } 22547259Sdr146992 port = htons(port); 22557259Sdr146992 np->in_pnext++; 22562393Syz155240 22572393Syz155240 if (np->in_pnext > ntohs(np->in_pmax)) { 22582393Syz155240 np->in_pnext = ntohs(np->in_pmin); 22592393Syz155240 if (np->in_outmsk != 0xffffffff) 22602393Syz155240 np->in_nip++; 22612393Syz155240 } 22622393Syz155240 } 22632393Syz155240 22642393Syz155240 if (np->in_flags & IPN_IPRANGE) { 22652393Syz155240 if (np->in_nip > ntohl(np->in_outmsk)) 22662393Syz155240 np->in_nip = ntohl(np->in_outip); 22672393Syz155240 } else { 22682393Syz155240 if ((np->in_outmsk != 0xffffffff) && 22692393Syz155240 ((np->in_nip + 1) & ntohl(np->in_outmsk)) > 22702393Syz155240 ntohl(np->in_outip)) 22712393Syz155240 np->in_nip = ntohl(np->in_outip) + 1; 22722393Syz155240 } 22732393Syz155240 22742393Syz155240 if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) 22752393Syz155240 port = sport; 22762393Syz155240 22772393Syz155240 /* 22782393Syz155240 * Here we do a lookup of the connection as seen from 22792393Syz155240 * the outside. If an IP# pair already exists, try 22802393Syz155240 * again. So if you have A->B becomes C->B, you can 22812393Syz155240 * also have D->E become C->E but not D->B causing 22822393Syz155240 * another C->B. Also take protocol and ports into 22832393Syz155240 * account when determining whether a pre-existing 22842393Syz155240 * NAT setup will cause an external conflict where 22852393Syz155240 * this is appropriate. 22862393Syz155240 */ 22872393Syz155240 inb.s_addr = htonl(in.s_addr); 22882393Syz155240 sp = fin->fin_data[0]; 22892393Syz155240 dp = fin->fin_data[1]; 22902393Syz155240 fin->fin_data[0] = fin->fin_data[1]; 22912393Syz155240 fin->fin_data[1] = htons(port); 22922393Syz155240 natl = nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 22932393Syz155240 (u_int)fin->fin_p, fin->fin_dst, inb); 22942393Syz155240 fin->fin_data[0] = sp; 22952393Syz155240 fin->fin_data[1] = dp; 22962393Syz155240 22972393Syz155240 /* 22982393Syz155240 * Has the search wrapped around and come back to the 22992393Syz155240 * start ? 23002393Syz155240 */ 23012393Syz155240 if ((natl != NULL) && 23022393Syz155240 (np->in_pnext != 0) && (st_port == np->in_pnext) && 23032393Syz155240 (np->in_nip != 0) && (st_ip == np->in_nip)) 23042393Syz155240 return -1; 23052393Syz155240 l++; 23062393Syz155240 } while (natl != NULL); 23072393Syz155240 23082393Syz155240 if (np->in_space > 0) 23092393Syz155240 np->in_space--; 23102393Syz155240 23112393Syz155240 /* Setup the NAT table */ 23122393Syz155240 nat->nat_inip = fin->fin_src; 23132393Syz155240 nat->nat_outip.s_addr = htonl(in.s_addr); 23142393Syz155240 nat->nat_oip = fin->fin_dst; 23152393Syz155240 if (nat->nat_hm == NULL) 23162393Syz155240 nat->nat_hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, 23173448Sdh155122 nat->nat_outip, 0, ifs); 23182393Syz155240 23192393Syz155240 if (flags & IPN_TCPUDP) { 23202393Syz155240 nat->nat_inport = sport; 23212393Syz155240 nat->nat_outport = port; /* sport */ 23222393Syz155240 nat->nat_oport = dport; 23232393Syz155240 ((tcphdr_t *)fin->fin_dp)->th_sport = port; 23242393Syz155240 } else if (flags & IPN_ICMPQUERY) { 23252393Syz155240 ((icmphdr_t *)fin->fin_dp)->icmp_id = port; 23262393Syz155240 nat->nat_inport = port; 23272393Syz155240 nat->nat_outport = port; 23282393Syz155240 } 23292393Syz155240 23302393Syz155240 ni->nai_ip.s_addr = in.s_addr; 23312393Syz155240 ni->nai_port = port; 23322393Syz155240 ni->nai_nport = dport; 23332393Syz155240 return 0; 23342393Syz155240 } 23352393Syz155240 23362393Syz155240 23372393Syz155240 /* ------------------------------------------------------------------------ */ 23382393Syz155240 /* Function: nat_newrdr */ 23392393Syz155240 /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ 23402393Syz155240 /* allow rule to be moved if IPN_ROUNDR is set. */ 23412393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 23422393Syz155240 /* nat(I) - pointer to NAT entry */ 23432393Syz155240 /* ni(I) - pointer to structure with misc. information needed */ 23442393Syz155240 /* to create new NAT entry. */ 23452393Syz155240 /* */ 23462393Syz155240 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 23472393Syz155240 /* to the new IP address for the translation. */ 23482393Syz155240 /* ------------------------------------------------------------------------ */ 23492393Syz155240 static INLINE int nat_newrdr(fin, nat, ni) 23502393Syz155240 fr_info_t *fin; 23512393Syz155240 nat_t *nat; 23522393Syz155240 natinfo_t *ni; 23532393Syz155240 { 23542393Syz155240 u_short nport, dport, sport; 23555828Sjojemann struct in_addr in, inb; 23565828Sjojemann u_short sp, dp; 23572393Syz155240 hostmap_t *hm; 23582393Syz155240 u_32_t flags; 23592393Syz155240 ipnat_t *np; 23605828Sjojemann nat_t *natl; 23612393Syz155240 int move; 23623448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 23632393Syz155240 23642393Syz155240 move = 1; 23652393Syz155240 hm = NULL; 23662393Syz155240 in.s_addr = 0; 23672393Syz155240 np = ni->nai_np; 23682393Syz155240 flags = ni->nai_flags; 23692393Syz155240 sport = ni->nai_sport; 23702393Syz155240 dport = ni->nai_dport; 23712393Syz155240 23722393Syz155240 /* 23732393Syz155240 * If the matching rule has IPN_STICKY set, then we want to have the 23742393Syz155240 * same rule kick in as before. Why would this happen? If you have 23752393Syz155240 * a collection of rdr rules with "round-robin sticky", the current 23762393Syz155240 * packet might match a different one to the previous connection but 23772393Syz155240 * we want the same destination to be used. 23782393Syz155240 */ 23792393Syz155240 if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == 23802393Syz155240 (IPN_ROUNDR|IPN_STICKY)) { 23812393Syz155240 hm = nat_hostmap(NULL, fin->fin_src, fin->fin_dst, in, 23823448Sdh155122 (u_32_t)dport, ifs); 23832393Syz155240 if (hm != NULL) { 23842393Syz155240 in.s_addr = ntohl(hm->hm_mapip.s_addr); 23852393Syz155240 np = hm->hm_ipnat; 23862393Syz155240 ni->nai_np = np; 23872393Syz155240 move = 0; 23882393Syz155240 } 23892393Syz155240 } 23902393Syz155240 23912393Syz155240 /* 23922393Syz155240 * Otherwise, it's an inbound packet. Most likely, we don't 23932393Syz155240 * want to rewrite source ports and source addresses. Instead, 23942393Syz155240 * we want to rewrite to a fixed internal address and fixed 23952393Syz155240 * internal port. 23962393Syz155240 */ 23972393Syz155240 if (np->in_flags & IPN_SPLIT) { 23982393Syz155240 in.s_addr = np->in_nip; 23992393Syz155240 24002393Syz155240 if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { 24012393Syz155240 hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, 24023448Sdh155122 in, (u_32_t)dport, ifs); 24032393Syz155240 if (hm != NULL) { 24042393Syz155240 in.s_addr = hm->hm_mapip.s_addr; 24052393Syz155240 move = 0; 24062393Syz155240 } 24072393Syz155240 } 24082393Syz155240 24092393Syz155240 if (hm == NULL || hm->hm_ref == 1) { 24102393Syz155240 if (np->in_inip == htonl(in.s_addr)) { 24112393Syz155240 np->in_nip = ntohl(np->in_inmsk); 24122393Syz155240 move = 0; 24132393Syz155240 } else { 24142393Syz155240 np->in_nip = ntohl(np->in_inip); 24152393Syz155240 } 24162393Syz155240 } 24172393Syz155240 24182393Syz155240 } else if ((np->in_inip == 0) && (np->in_inmsk == 0xffffffff)) { 24192393Syz155240 /* 24202393Syz155240 * 0/32 - use the interface's IP address. 24212393Syz155240 */ 24223448Sdh155122 if (fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp, &in, NULL, 24233448Sdh155122 fin->fin_ifs) == -1) 24242393Syz155240 return -1; 24252393Syz155240 in.s_addr = ntohl(in.s_addr); 24262393Syz155240 24272393Syz155240 } else if ((np->in_inip == 0) && (np->in_inmsk== 0)) { 24282393Syz155240 /* 24292393Syz155240 * 0/0 - use the original destination address/port. 24302393Syz155240 */ 24312393Syz155240 in.s_addr = ntohl(fin->fin_daddr); 24322393Syz155240 24332393Syz155240 } else if (np->in_redir == NAT_BIMAP && 24342393Syz155240 np->in_inmsk == np->in_outmsk) { 24352393Syz155240 /* 24362393Syz155240 * map the address block in a 1:1 fashion 24372393Syz155240 */ 24382393Syz155240 in.s_addr = np->in_inip; 24392393Syz155240 in.s_addr |= fin->fin_daddr & ~np->in_inmsk; 24402393Syz155240 in.s_addr = ntohl(in.s_addr); 24412393Syz155240 } else { 24422393Syz155240 in.s_addr = ntohl(np->in_inip); 24432393Syz155240 } 24442393Syz155240 24452393Syz155240 if ((np->in_pnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) 24462393Syz155240 nport = dport; 24472393Syz155240 else { 24482393Syz155240 /* 24492393Syz155240 * Whilst not optimized for the case where 24502393Syz155240 * pmin == pmax, the gain is not significant. 24512393Syz155240 */ 24522393Syz155240 if (((np->in_flags & IPN_FIXEDDPORT) == 0) && 24532393Syz155240 (np->in_pmin != np->in_pmax)) { 24542393Syz155240 nport = ntohs(dport) - ntohs(np->in_pmin) + 24552393Syz155240 ntohs(np->in_pnext); 24562393Syz155240 nport = htons(nport); 24572393Syz155240 } else 24582393Syz155240 nport = np->in_pnext; 24592393Syz155240 } 24602393Syz155240 24612393Syz155240 /* 24622393Syz155240 * When the redirect-to address is set to 0.0.0.0, just 24632393Syz155240 * assume a blank `forwarding' of the packet. We don't 24642393Syz155240 * setup any translation for this either. 24652393Syz155240 */ 24662393Syz155240 if (in.s_addr == 0) { 24672393Syz155240 if (nport == dport) 24682393Syz155240 return -1; 24692393Syz155240 in.s_addr = ntohl(fin->fin_daddr); 24702393Syz155240 } 24712393Syz155240 24725828Sjojemann /* 24735828Sjojemann * Check to see if this redirect mapping already exists and if 24745828Sjojemann * it does, return "failure" (allowing it to be created will just 24755828Sjojemann * cause one or both of these "connections" to stop working.) 24765828Sjojemann */ 24775828Sjojemann inb.s_addr = htonl(in.s_addr); 24785828Sjojemann sp = fin->fin_data[0]; 24795828Sjojemann dp = fin->fin_data[1]; 24805828Sjojemann fin->fin_data[1] = fin->fin_data[0]; 24815828Sjojemann fin->fin_data[0] = ntohs(nport); 24825828Sjojemann natl = nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 24835828Sjojemann (u_int)fin->fin_p, inb, fin->fin_src); 24845828Sjojemann fin->fin_data[0] = sp; 24855828Sjojemann fin->fin_data[1] = dp; 24865828Sjojemann if (natl != NULL) 24875828Sjojemann return (-1); 24885828Sjojemann 24892393Syz155240 nat->nat_inip.s_addr = htonl(in.s_addr); 24902393Syz155240 nat->nat_outip = fin->fin_dst; 24912393Syz155240 nat->nat_oip = fin->fin_src; 24922393Syz155240 24932393Syz155240 ni->nai_ip.s_addr = in.s_addr; 24942393Syz155240 ni->nai_nport = nport; 24952393Syz155240 ni->nai_port = sport; 24962393Syz155240 24972393Syz155240 if (flags & IPN_TCPUDP) { 24982393Syz155240 nat->nat_inport = nport; 24992393Syz155240 nat->nat_outport = dport; 25002393Syz155240 nat->nat_oport = sport; 25012393Syz155240 ((tcphdr_t *)fin->fin_dp)->th_dport = nport; 25022393Syz155240 } else if (flags & IPN_ICMPQUERY) { 25032393Syz155240 ((icmphdr_t *)fin->fin_dp)->icmp_id = nport; 25042393Syz155240 nat->nat_inport = nport; 25052393Syz155240 nat->nat_outport = nport; 25062393Syz155240 } 25072393Syz155240 25082393Syz155240 return move; 25092393Syz155240 } 25102393Syz155240 25112393Syz155240 /* ------------------------------------------------------------------------ */ 25122393Syz155240 /* Function: nat_new */ 25132393Syz155240 /* Returns: nat_t* - NULL == failure to create new NAT structure, */ 25142393Syz155240 /* else pointer to new NAT structure */ 25152393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 25162393Syz155240 /* np(I) - pointer to NAT rule */ 25172393Syz155240 /* natsave(I) - pointer to where to store NAT struct pointer */ 25182393Syz155240 /* flags(I) - flags describing the current packet */ 25192393Syz155240 /* direction(I) - direction of packet (in/out) */ 25202393Syz155240 /* Write Lock: ipf_nat */ 25212393Syz155240 /* */ 25222393Syz155240 /* Attempts to create a new NAT entry. Does not actually change the packet */ 25232393Syz155240 /* in any way. */ 25242393Syz155240 /* */ 25252393Syz155240 /* This fucntion is in three main parts: (1) deal with creating a new NAT */ 25262393Syz155240 /* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */ 25272393Syz155240 /* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */ 25282393Syz155240 /* and (3) building that structure and putting it into the NAT table(s). */ 25292393Syz155240 /* ------------------------------------------------------------------------ */ 25302393Syz155240 nat_t *nat_new(fin, np, natsave, flags, direction) 25312393Syz155240 fr_info_t *fin; 25322393Syz155240 ipnat_t *np; 25332393Syz155240 nat_t **natsave; 25342393Syz155240 u_int flags; 25352393Syz155240 int direction; 25362393Syz155240 { 25372393Syz155240 tcphdr_t *tcp = NULL; 25382393Syz155240 hostmap_t *hm = NULL; 25392393Syz155240 nat_t *nat, *natl; 25402393Syz155240 u_int nflags; 25412393Syz155240 natinfo_t ni; 25422393Syz155240 int move; 25433448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 25443448Sdh155122 25454817San207044 /* 2546*8170SJohn.Ojemann@Sun.COM * Trigger automatic call to nat_flushtable() if the 25474817San207044 * table has reached capcity specified by hi watermark. 25484817San207044 */ 2549*8170SJohn.Ojemann@Sun.COM if (NAT_TAB_WATER_LEVEL(ifs) > ifs->ifs_nat_flush_level_hi) 25504817San207044 ifs->ifs_nat_doflush = 1; 25514817San207044 25527432SJohn.Ojemann@Sun.COM /* 25537432SJohn.Ojemann@Sun.COM * If automatic flushing did not do its job, and the table 25547432SJohn.Ojemann@Sun.COM * has filled up, don't try to create a new entry. 25557432SJohn.Ojemann@Sun.COM */ 25563448Sdh155122 if (ifs->ifs_nat_stats.ns_inuse >= ifs->ifs_ipf_nattable_max) { 25573448Sdh155122 ifs->ifs_nat_stats.ns_memfail++; 25582393Syz155240 return NULL; 25592393Syz155240 } 25602393Syz155240 25612393Syz155240 move = 1; 25622393Syz155240 nflags = np->in_flags & flags; 25632393Syz155240 nflags &= NAT_FROMRULE; 25642393Syz155240 25652393Syz155240 ni.nai_np = np; 25662393Syz155240 ni.nai_nflags = nflags; 25672393Syz155240 ni.nai_flags = flags; 25682393Syz155240 25692393Syz155240 /* Give me a new nat */ 25702393Syz155240 KMALLOC(nat, nat_t *); 25712393Syz155240 if (nat == NULL) { 25723448Sdh155122 ifs->ifs_nat_stats.ns_memfail++; 25732393Syz155240 /* 25742393Syz155240 * Try to automatically tune the max # of entries in the 25752393Syz155240 * table allowed to be less than what will cause kmem_alloc() 25762393Syz155240 * to fail and try to eliminate panics due to out of memory 25772393Syz155240 * conditions arising. 25782393Syz155240 */ 25793448Sdh155122 if (ifs->ifs_ipf_nattable_max > ifs->ifs_ipf_nattable_sz) { 25803448Sdh155122 ifs->ifs_ipf_nattable_max = ifs->ifs_nat_stats.ns_inuse - 100; 25812393Syz155240 printf("ipf_nattable_max reduced to %d\n", 25823448Sdh155122 ifs->ifs_ipf_nattable_max); 25832393Syz155240 } 25842393Syz155240 return NULL; 25852393Syz155240 } 25862393Syz155240 25872393Syz155240 if (flags & IPN_TCPUDP) { 25882393Syz155240 tcp = fin->fin_dp; 25892393Syz155240 ni.nai_sport = htons(fin->fin_sport); 25902393Syz155240 ni.nai_dport = htons(fin->fin_dport); 25912393Syz155240 } else if (flags & IPN_ICMPQUERY) { 25922393Syz155240 /* 25932393Syz155240 * In the ICMP query NAT code, we translate the ICMP id fields 25942393Syz155240 * to make them unique. This is indepedent of the ICMP type 25952393Syz155240 * (e.g. in the unlikely event that a host sends an echo and 25962393Syz155240 * an tstamp request with the same id, both packets will have 25972393Syz155240 * their ip address/id field changed in the same way). 25982393Syz155240 */ 25992393Syz155240 /* The icmp_id field is used by the sender to identify the 26002393Syz155240 * process making the icmp request. (the receiver justs 26012393Syz155240 * copies it back in its response). So, it closely matches 26022393Syz155240 * the concept of source port. We overlay sport, so we can 26032393Syz155240 * maximally reuse the existing code. 26042393Syz155240 */ 26052393Syz155240 ni.nai_sport = ((icmphdr_t *)fin->fin_dp)->icmp_id; 26062393Syz155240 ni.nai_dport = ni.nai_sport; 26072393Syz155240 } 26082393Syz155240 26092393Syz155240 bzero((char *)nat, sizeof(*nat)); 26102393Syz155240 nat->nat_flags = flags; 26113448Sdh155122 nat->nat_redir = np->in_redir; 26122393Syz155240 26132393Syz155240 if ((flags & NAT_SLAVE) == 0) { 26143448Sdh155122 MUTEX_ENTER(&ifs->ifs_ipf_nat_new); 26152393Syz155240 } 26162393Syz155240 26172393Syz155240 /* 26182393Syz155240 * Search the current table for a match. 26192393Syz155240 */ 26202393Syz155240 if (direction == NAT_OUTBOUND) { 26212393Syz155240 /* 26222393Syz155240 * We can now arrange to call this for the same connection 26232393Syz155240 * because ipf_nat_new doesn't protect the code path into 26242393Syz155240 * this function. 26252393Syz155240 */ 26262393Syz155240 natl = nat_outlookup(fin, nflags, (u_int)fin->fin_p, 26272393Syz155240 fin->fin_src, fin->fin_dst); 26282393Syz155240 if (natl != NULL) { 26294380Sjojemann KFREE(nat); 26302393Syz155240 nat = natl; 26312393Syz155240 goto done; 26322393Syz155240 } 26332393Syz155240 26342393Syz155240 move = nat_newmap(fin, nat, &ni); 26352393Syz155240 if (move == -1) 26362393Syz155240 goto badnat; 26372393Syz155240 26382393Syz155240 np = ni.nai_np; 26392393Syz155240 } else { 26402393Syz155240 /* 26412393Syz155240 * NAT_INBOUND is used only for redirects rules 26422393Syz155240 */ 26432393Syz155240 natl = nat_inlookup(fin, nflags, (u_int)fin->fin_p, 26442393Syz155240 fin->fin_src, fin->fin_dst); 26452393Syz155240 if (natl != NULL) { 26464380Sjojemann KFREE(nat); 26472393Syz155240 nat = natl; 26482393Syz155240 goto done; 26492393Syz155240 } 26502393Syz155240 26512393Syz155240 move = nat_newrdr(fin, nat, &ni); 26522393Syz155240 if (move == -1) 26532393Syz155240 goto badnat; 26542393Syz155240 26552393Syz155240 np = ni.nai_np; 26562393Syz155240 } 26572393Syz155240 26582393Syz155240 if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { 26592393Syz155240 if (np->in_redir == NAT_REDIRECT) { 26602393Syz155240 nat_delrdr(np); 26613448Sdh155122 nat_addrdr(np, ifs); 26622393Syz155240 } else if (np->in_redir == NAT_MAP) { 26632393Syz155240 nat_delnat(np); 26643448Sdh155122 nat_addnat(np, ifs); 26652393Syz155240 } 26662393Syz155240 } 26672393Syz155240 26682393Syz155240 if (nat_finalise(fin, nat, &ni, tcp, natsave, direction) == -1) { 26692393Syz155240 goto badnat; 26702393Syz155240 } 26716253San207044 26726253San207044 nat_calc_chksum_diffs(nat); 26736253San207044 26742393Syz155240 if (flags & SI_WILDP) 26753448Sdh155122 ifs->ifs_nat_stats.ns_wilds++; 26767432SJohn.Ojemann@Sun.COM fin->fin_flx |= FI_NEWNAT; 26772393Syz155240 goto done; 26782393Syz155240 badnat: 26793448Sdh155122 ifs->ifs_nat_stats.ns_badnat++; 26802393Syz155240 if ((hm = nat->nat_hm) != NULL) 26815417Sjojemann fr_hostmapdel(&hm); 26822393Syz155240 KFREE(nat); 26832393Syz155240 nat = NULL; 26842393Syz155240 done: 26852393Syz155240 if ((flags & NAT_SLAVE) == 0) { 26863448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_nat_new); 26872393Syz155240 } 26882393Syz155240 return nat; 26892393Syz155240 } 26902393Syz155240 26912393Syz155240 26922393Syz155240 /* ------------------------------------------------------------------------ */ 26932393Syz155240 /* Function: nat_finalise */ 26942393Syz155240 /* Returns: int - 0 == sucess, -1 == failure */ 26952393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 26962393Syz155240 /* nat(I) - pointer to NAT entry */ 26972393Syz155240 /* ni(I) - pointer to structure with misc. information needed */ 26982393Syz155240 /* to create new NAT entry. */ 26992393Syz155240 /* Write Lock: ipf_nat */ 27002393Syz155240 /* */ 27012393Syz155240 /* This is the tail end of constructing a new NAT entry and is the same */ 27022393Syz155240 /* for both IPv4 and IPv6. */ 27032393Syz155240 /* ------------------------------------------------------------------------ */ 27042393Syz155240 /*ARGSUSED*/ 27052393Syz155240 static INLINE int nat_finalise(fin, nat, ni, tcp, natsave, direction) 27062393Syz155240 fr_info_t *fin; 27072393Syz155240 nat_t *nat; 27082393Syz155240 natinfo_t *ni; 27092393Syz155240 tcphdr_t *tcp; 27102393Syz155240 nat_t **natsave; 27112393Syz155240 int direction; 27122393Syz155240 { 27132393Syz155240 frentry_t *fr; 27142393Syz155240 ipnat_t *np; 27153448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 27162393Syz155240 27172393Syz155240 np = ni->nai_np; 27182393Syz155240 27192958Sdr146992 COPYIFNAME(fin->fin_ifp, nat->nat_ifnames[0], fin->fin_v); 27202958Sdr146992 27212393Syz155240 #ifdef IPFILTER_SYNC 27222393Syz155240 if ((nat->nat_flags & SI_CLONE) == 0) 27232393Syz155240 nat->nat_sync = ipfsync_new(SMC_NAT, fin, nat); 27242393Syz155240 #endif 27252393Syz155240 27262393Syz155240 nat->nat_me = natsave; 27272393Syz155240 nat->nat_dir = direction; 27282508Syz155240 nat->nat_ifps[0] = np->in_ifps[0]; 27292508Syz155240 nat->nat_ifps[1] = np->in_ifps[1]; 27302393Syz155240 nat->nat_ptr = np; 27312393Syz155240 nat->nat_p = fin->fin_p; 27327176Syx160601 nat->nat_v = fin->fin_v; 27332393Syz155240 nat->nat_mssclamp = np->in_mssclamp; 27342393Syz155240 fr = fin->fin_fr; 27352393Syz155240 nat->nat_fr = fr; 27362393Syz155240 27372393Syz155240 if ((np->in_apr != NULL) && ((ni->nai_flags & NAT_SLAVE) == 0)) 27382393Syz155240 if (appr_new(fin, nat) == -1) 27392393Syz155240 return -1; 27402393Syz155240 27413448Sdh155122 if (nat_insert(nat, fin->fin_rev, ifs) == 0) { 27423448Sdh155122 if (ifs->ifs_nat_logging) 27433448Sdh155122 nat_log(nat, (u_int)np->in_redir, ifs); 27442393Syz155240 np->in_use++; 27452393Syz155240 if (fr != NULL) { 27462393Syz155240 MUTEX_ENTER(&fr->fr_lock); 27472393Syz155240 fr->fr_ref++; 27482393Syz155240 MUTEX_EXIT(&fr->fr_lock); 27492393Syz155240 } 27502393Syz155240 return 0; 27512393Syz155240 } 27522393Syz155240 27532393Syz155240 /* 27542393Syz155240 * nat_insert failed, so cleanup time... 27552393Syz155240 */ 27562393Syz155240 return -1; 27572393Syz155240 } 27582393Syz155240 27592393Syz155240 27602393Syz155240 /* ------------------------------------------------------------------------ */ 27612393Syz155240 /* Function: nat_insert */ 27622393Syz155240 /* Returns: int - 0 == sucess, -1 == failure */ 27632393Syz155240 /* Parameters: nat(I) - pointer to NAT structure */ 27642393Syz155240 /* rev(I) - flag indicating forward/reverse direction of packet */ 27652393Syz155240 /* Write Lock: ipf_nat */ 27662393Syz155240 /* */ 27672393Syz155240 /* Insert a NAT entry into the hash tables for searching and add it to the */ 27682393Syz155240 /* list of active NAT entries. Adjust global counters when complete. */ 27692393Syz155240 /* ------------------------------------------------------------------------ */ 27703448Sdh155122 int nat_insert(nat, rev, ifs) 27712393Syz155240 nat_t *nat; 27722393Syz155240 int rev; 27733448Sdh155122 ipf_stack_t *ifs; 27742393Syz155240 { 27752393Syz155240 u_int hv1, hv2; 27762393Syz155240 nat_t **natp; 27772393Syz155240 27782393Syz155240 /* 27792393Syz155240 * Try and return an error as early as possible, so calculate the hash 27802393Syz155240 * entry numbers first and then proceed. 27812393Syz155240 */ 27822393Syz155240 if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { 27832393Syz155240 hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 27842393Syz155240 0xffffffff); 27852393Syz155240 hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1 + nat->nat_oport, 27863448Sdh155122 ifs->ifs_ipf_nattable_sz); 27872393Syz155240 hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, 27882393Syz155240 0xffffffff); 27892393Syz155240 hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2 + nat->nat_oport, 27903448Sdh155122 ifs->ifs_ipf_nattable_sz); 27912393Syz155240 } else { 27922393Syz155240 hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, 0, 0xffffffff); 27933448Sdh155122 hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1, 27943448Sdh155122 ifs->ifs_ipf_nattable_sz); 27952393Syz155240 hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, 0, 0xffffffff); 27963448Sdh155122 hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2, 27973448Sdh155122 ifs->ifs_ipf_nattable_sz); 27982393Syz155240 } 27992393Syz155240 28003448Sdh155122 if (ifs->ifs_nat_stats.ns_bucketlen[0][hv1] >= ifs->ifs_fr_nat_maxbucket || 28013448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[1][hv2] >= ifs->ifs_fr_nat_maxbucket) { 28022393Syz155240 return -1; 28032393Syz155240 } 28042393Syz155240 28052393Syz155240 nat->nat_hv[0] = hv1; 28062393Syz155240 nat->nat_hv[1] = hv2; 28072393Syz155240 28082393Syz155240 MUTEX_INIT(&nat->nat_lock, "nat entry lock"); 28092393Syz155240 28102393Syz155240 nat->nat_rev = rev; 28112393Syz155240 nat->nat_ref = 1; 28122393Syz155240 nat->nat_bytes[0] = 0; 28132393Syz155240 nat->nat_pkts[0] = 0; 28142393Syz155240 nat->nat_bytes[1] = 0; 28152393Syz155240 nat->nat_pkts[1] = 0; 28162393Syz155240 28172393Syz155240 nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; 28183448Sdh155122 nat->nat_ifps[0] = fr_resolvenic(nat->nat_ifnames[0], 4, ifs); 28192393Syz155240 28202393Syz155240 if (nat->nat_ifnames[1][0] !='\0') { 28212393Syz155240 nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 28223448Sdh155122 nat->nat_ifps[1] = fr_resolvenic(nat->nat_ifnames[1], 4, ifs); 28232393Syz155240 } else { 28242393Syz155240 (void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0], 28252393Syz155240 LIFNAMSIZ); 28262393Syz155240 nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 28272393Syz155240 nat->nat_ifps[1] = nat->nat_ifps[0]; 28282393Syz155240 } 28292393Syz155240 28303448Sdh155122 nat->nat_next = ifs->ifs_nat_instances; 28313448Sdh155122 nat->nat_pnext = &ifs->ifs_nat_instances; 28323448Sdh155122 if (ifs->ifs_nat_instances) 28333448Sdh155122 ifs->ifs_nat_instances->nat_pnext = &nat->nat_next; 28343448Sdh155122 ifs->ifs_nat_instances = nat; 28353448Sdh155122 28363448Sdh155122 natp = &ifs->ifs_nat_table[0][hv1]; 28372393Syz155240 if (*natp) 28382393Syz155240 (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 28392393Syz155240 nat->nat_phnext[0] = natp; 28402393Syz155240 nat->nat_hnext[0] = *natp; 28412393Syz155240 *natp = nat; 28423448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[0][hv1]++; 28433448Sdh155122 28443448Sdh155122 natp = &ifs->ifs_nat_table[1][hv2]; 28452393Syz155240 if (*natp) 28462393Syz155240 (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 28472393Syz155240 nat->nat_phnext[1] = natp; 28482393Syz155240 nat->nat_hnext[1] = *natp; 28492393Syz155240 *natp = nat; 28503448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[1][hv2]++; 28513448Sdh155122 28523448Sdh155122 fr_setnatqueue(nat, rev, ifs); 28533448Sdh155122 28543448Sdh155122 ifs->ifs_nat_stats.ns_added++; 28553448Sdh155122 ifs->ifs_nat_stats.ns_inuse++; 28562393Syz155240 return 0; 28572393Syz155240 } 28582393Syz155240 28592393Syz155240 28602393Syz155240 /* ------------------------------------------------------------------------ */ 28612393Syz155240 /* Function: nat_icmperrorlookup */ 28622393Syz155240 /* Returns: nat_t* - point to matching NAT structure */ 28632393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 28642393Syz155240 /* dir(I) - direction of packet (in/out) */ 28652393Syz155240 /* */ 28662393Syz155240 /* Check if the ICMP error message is related to an existing TCP, UDP or */ 28672393Syz155240 /* ICMP query nat entry. It is assumed that the packet is already of the */ 28682393Syz155240 /* the required length. */ 28692393Syz155240 /* ------------------------------------------------------------------------ */ 28702393Syz155240 nat_t *nat_icmperrorlookup(fin, dir) 28712393Syz155240 fr_info_t *fin; 28722393Syz155240 int dir; 28732393Syz155240 { 28742393Syz155240 int flags = 0, minlen; 28752393Syz155240 icmphdr_t *orgicmp; 28762393Syz155240 tcphdr_t *tcp = NULL; 28772393Syz155240 u_short data[2]; 28782393Syz155240 nat_t *nat; 28792393Syz155240 ip_t *oip; 28802393Syz155240 u_int p; 28812393Syz155240 28822393Syz155240 /* 28832393Syz155240 * Does it at least have the return (basic) IP header ? 28842393Syz155240 * Only a basic IP header (no options) should be with an ICMP error 28852393Syz155240 * header. Also, if it's not an error type, then return. 28862393Syz155240 */ 28872393Syz155240 if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) 28882393Syz155240 return NULL; 28892393Syz155240 28902393Syz155240 /* 28912393Syz155240 * Check packet size 28922393Syz155240 */ 28932393Syz155240 oip = (ip_t *)((char *)fin->fin_dp + 8); 28942393Syz155240 minlen = IP_HL(oip) << 2; 28952393Syz155240 if ((minlen < sizeof(ip_t)) || 28962393Syz155240 (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) 28972393Syz155240 return NULL; 28982393Syz155240 /* 28992393Syz155240 * Is the buffer big enough for all of it ? It's the size of the IP 29002393Syz155240 * header claimed in the encapsulated part which is of concern. It 29012393Syz155240 * may be too big to be in this buffer but not so big that it's 29022393Syz155240 * outside the ICMP packet, leading to TCP deref's causing problems. 29032393Syz155240 * This is possible because we don't know how big oip_hl is when we 29042393Syz155240 * do the pullup early in fr_check() and thus can't gaurantee it is 29052393Syz155240 * all here now. 29062393Syz155240 */ 29072393Syz155240 #ifdef _KERNEL 29082393Syz155240 { 29092393Syz155240 mb_t *m; 29102393Syz155240 29112393Syz155240 m = fin->fin_m; 29122393Syz155240 # if defined(MENTAT) 29132393Syz155240 if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr) 29142393Syz155240 return NULL; 29152393Syz155240 # else 29162393Syz155240 if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > 29172393Syz155240 (char *)fin->fin_ip + M_LEN(m)) 29182393Syz155240 return NULL; 29192393Syz155240 # endif 29202393Syz155240 } 29212393Syz155240 #endif 29222393Syz155240 29232393Syz155240 if (fin->fin_daddr != oip->ip_src.s_addr) 29242393Syz155240 return NULL; 29252393Syz155240 29262393Syz155240 p = oip->ip_p; 29272393Syz155240 if (p == IPPROTO_TCP) 29282393Syz155240 flags = IPN_TCP; 29292393Syz155240 else if (p == IPPROTO_UDP) 29302393Syz155240 flags = IPN_UDP; 29312393Syz155240 else if (p == IPPROTO_ICMP) { 29322393Syz155240 orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 29332393Syz155240 29342393Syz155240 /* see if this is related to an ICMP query */ 29352393Syz155240 if (nat_icmpquerytype4(orgicmp->icmp_type)) { 29362393Syz155240 data[0] = fin->fin_data[0]; 29372393Syz155240 data[1] = fin->fin_data[1]; 29382393Syz155240 fin->fin_data[0] = 0; 29392393Syz155240 fin->fin_data[1] = orgicmp->icmp_id; 29402393Syz155240 29412393Syz155240 flags = IPN_ICMPERR|IPN_ICMPQUERY; 29422393Syz155240 /* 29432393Syz155240 * NOTE : dir refers to the direction of the original 29442393Syz155240 * ip packet. By definition the icmp error 29452393Syz155240 * message flows in the opposite direction. 29462393Syz155240 */ 29472393Syz155240 if (dir == NAT_INBOUND) 29482393Syz155240 nat = nat_inlookup(fin, flags, p, oip->ip_dst, 29492393Syz155240 oip->ip_src); 29502393Syz155240 else 29512393Syz155240 nat = nat_outlookup(fin, flags, p, oip->ip_dst, 29522393Syz155240 oip->ip_src); 29532393Syz155240 fin->fin_data[0] = data[0]; 29542393Syz155240 fin->fin_data[1] = data[1]; 29552393Syz155240 return nat; 29562393Syz155240 } 29572393Syz155240 } 29582393Syz155240 29592393Syz155240 if (flags & IPN_TCPUDP) { 29602393Syz155240 minlen += 8; /* + 64bits of data to get ports */ 29612393Syz155240 if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) 29622393Syz155240 return NULL; 29632393Syz155240 29642393Syz155240 data[0] = fin->fin_data[0]; 29652393Syz155240 data[1] = fin->fin_data[1]; 29662393Syz155240 tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 29672393Syz155240 fin->fin_data[0] = ntohs(tcp->th_dport); 29682393Syz155240 fin->fin_data[1] = ntohs(tcp->th_sport); 29692393Syz155240 29702393Syz155240 if (dir == NAT_INBOUND) { 29712393Syz155240 nat = nat_inlookup(fin, flags, p, oip->ip_dst, 29722393Syz155240 oip->ip_src); 29732393Syz155240 } else { 29742393Syz155240 nat = nat_outlookup(fin, flags, p, oip->ip_dst, 29752393Syz155240 oip->ip_src); 29762393Syz155240 } 29772393Syz155240 fin->fin_data[0] = data[0]; 29782393Syz155240 fin->fin_data[1] = data[1]; 29792393Syz155240 return nat; 29802393Syz155240 } 29812393Syz155240 if (dir == NAT_INBOUND) 29822393Syz155240 return nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src); 29832393Syz155240 else 29842393Syz155240 return nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src); 29852393Syz155240 } 29862393Syz155240 29872393Syz155240 29882393Syz155240 /* ------------------------------------------------------------------------ */ 29892393Syz155240 /* Function: nat_icmperror */ 29902393Syz155240 /* Returns: nat_t* - point to matching NAT structure */ 29912393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 29922393Syz155240 /* nflags(I) - NAT flags for this packet */ 29932393Syz155240 /* dir(I) - direction of packet (in/out) */ 29942393Syz155240 /* */ 29952393Syz155240 /* Fix up an ICMP packet which is an error message for an existing NAT */ 29962393Syz155240 /* session. This will correct both packet header data and checksums. */ 29972393Syz155240 /* */ 29982393Syz155240 /* This should *ONLY* be used for incoming ICMP error packets to make sure */ 29992393Syz155240 /* a NAT'd ICMP packet gets correctly recognised. */ 30002393Syz155240 /* ------------------------------------------------------------------------ */ 30012393Syz155240 nat_t *nat_icmperror(fin, nflags, dir) 30022393Syz155240 fr_info_t *fin; 30032393Syz155240 u_int *nflags; 30042393Syz155240 int dir; 30052393Syz155240 { 30062693Sjojemann u_32_t sum1, sum2, sumd, psum1, psum2, psumd, sumd2; 30072393Syz155240 struct in_addr in; 30082693Sjojemann icmphdr_t *icmp, *orgicmp; 30092693Sjojemann int dlen; 30102693Sjojemann udphdr_t *udp; 30112393Syz155240 tcphdr_t *tcp; 30122393Syz155240 nat_t *nat; 30132393Syz155240 ip_t *oip; 30142393Syz155240 if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) 30152393Syz155240 return NULL; 30162693Sjojemann 30172393Syz155240 /* 30182693Sjojemann * nat_icmperrorlookup() looks up nat entry associated with the 30192693Sjojemann * offending IP packet and returns pointer to the entry, or NULL 30202693Sjojemann * if packet wasn't natted or for `defective' packets. 30212393Syz155240 */ 30222693Sjojemann 30232393Syz155240 if ((fin->fin_v != 4) || !(nat = nat_icmperrorlookup(fin, dir))) 30242393Syz155240 return NULL; 30252393Syz155240 30262393Syz155240 sumd2 = 0; 30272393Syz155240 *nflags = IPN_ICMPERR; 30282393Syz155240 icmp = fin->fin_dp; 30292393Syz155240 oip = (ip_t *)&icmp->icmp_ip; 30302693Sjojemann udp = (udphdr_t *)((((char *)oip) + (IP_HL(oip) << 2))); 30312693Sjojemann tcp = (tcphdr_t *)udp; 30322693Sjojemann dlen = fin->fin_plen - ((char *)udp - (char *)fin->fin_ip); 30332393Syz155240 30342393Syz155240 /* 30352393Syz155240 * Need to adjust ICMP header to include the real IP#'s and 30362693Sjojemann * port #'s. There are three steps required. 30372693Sjojemann * 30382393Syz155240 * Step 1 30392693Sjojemann * Fix the IP addresses in the offending IP packet and update 30402693Sjojemann * ip header checksum to compensate for the change. 30412393Syz155240 * 30422693Sjojemann * No update needed here for icmp_cksum because the ICMP checksum 30432693Sjojemann * is calculated over the complete ICMP packet, which includes the 30442693Sjojemann * changed oip IP addresses and oip->ip_sum. These two changes 30452693Sjojemann * cancel each other out (if the delta for the IP address is x, 30462693Sjojemann * then the delta for ip_sum is minus x). 30472393Syz155240 */ 30482393Syz155240 30492393Syz155240 if (oip->ip_dst.s_addr == nat->nat_oip.s_addr) { 30502393Syz155240 sum1 = LONG_SUM(ntohl(oip->ip_src.s_addr)); 30512393Syz155240 in = nat->nat_inip; 30522393Syz155240 oip->ip_src = in; 30532393Syz155240 } else { 30542393Syz155240 sum1 = LONG_SUM(ntohl(oip->ip_dst.s_addr)); 30552393Syz155240 in = nat->nat_outip; 30562393Syz155240 oip->ip_dst = in; 30572393Syz155240 } 30582393Syz155240 30592393Syz155240 sum2 = LONG_SUM(ntohl(in.s_addr)); 30602393Syz155240 CALC_SUMD(sum1, sum2, sumd); 30612393Syz155240 fix_datacksum(&oip->ip_sum, sumd); 30622393Syz155240 30632393Syz155240 /* 30642693Sjojemann * Step 2 30652693Sjojemann * Perform other adjustments based on protocol of offending packet. 30662393Syz155240 */ 30672693Sjojemann 30682693Sjojemann switch (oip->ip_p) { 30692693Sjojemann case IPPROTO_TCP : 30702693Sjojemann case IPPROTO_UDP : 30712393Syz155240 30722393Syz155240 /* 30732693Sjojemann * For offending TCP/UDP IP packets, translate the ports 30742693Sjojemann * based on the NAT specification. 30752693Sjojemann * 30762693Sjojemann * Advance notice : Now it becomes complicated :-) 30772693Sjojemann * 30782693Sjojemann * Since the port and IP addresse fields are both part 30792693Sjojemann * of the TCP/UDP checksum of the offending IP packet, 30802693Sjojemann * we need to adjust that checksum as well. 30812693Sjojemann * 30822693Sjojemann * To further complicate things, the TCP/UDP checksum 30832693Sjojemann * may not be present. We must check to see if the 30842693Sjojemann * length of the data portion is big enough to hold 30852693Sjojemann * the checksum. In the UDP case, a test to determine 30862693Sjojemann * if the checksum is even set is also required. 30872693Sjojemann * 30882693Sjojemann * Any changes to an IP address, port or checksum within 30892693Sjojemann * the ICMP packet requires a change to icmp_cksum. 30902693Sjojemann * 30912693Sjojemann * Be extremely careful here ... The change is dependent 30922693Sjojemann * upon whether or not the TCP/UPD checksum is present. 30932693Sjojemann * 30942693Sjojemann * If TCP/UPD checksum is present, the icmp_cksum must 30952693Sjojemann * compensate for checksum modification resulting from 30962693Sjojemann * IP address change only. Port change and resulting 30972693Sjojemann * data checksum adjustments cancel each other out. 30982693Sjojemann * 30992693Sjojemann * If TCP/UDP checksum is not present, icmp_cksum must 31002693Sjojemann * compensate for port change only. The IP address 31012693Sjojemann * change does not modify anything else in this case. 31022693Sjojemann */ 31032693Sjojemann 31042693Sjojemann psum1 = 0; 31052693Sjojemann psum2 = 0; 31062693Sjojemann psumd = 0; 31072693Sjojemann 31082693Sjojemann if ((tcp->th_dport == nat->nat_oport) && 31092693Sjojemann (tcp->th_sport != nat->nat_inport)) { 31102393Syz155240 31112393Syz155240 /* 31122693Sjojemann * Translate the source port. 31132693Sjojemann */ 31142693Sjojemann 31152693Sjojemann psum1 = ntohs(tcp->th_sport); 31162693Sjojemann psum2 = ntohs(nat->nat_inport); 31172693Sjojemann tcp->th_sport = nat->nat_inport; 31182693Sjojemann 31192693Sjojemann } else if ((tcp->th_sport == nat->nat_oport) && 31202693Sjojemann (tcp->th_dport != nat->nat_outport)) { 31212693Sjojemann 31222693Sjojemann /* 31232693Sjojemann * Translate the destination port. 31242393Syz155240 */ 31252693Sjojemann 31262693Sjojemann psum1 = ntohs(tcp->th_dport); 31272693Sjojemann psum2 = ntohs(nat->nat_outport); 31282693Sjojemann tcp->th_dport = nat->nat_outport; 31292693Sjojemann } 31302693Sjojemann 31312693Sjojemann if ((oip->ip_p == IPPROTO_TCP) && (dlen >= 18)) { 31322693Sjojemann 31332693Sjojemann /* 31342693Sjojemann * TCP checksum present. 31352693Sjojemann * 31362693Sjojemann * Adjust data checksum and icmp checksum to 31372693Sjojemann * compensate for any IP address change. 31382693Sjojemann */ 31392693Sjojemann 31402693Sjojemann sum1 = ntohs(tcp->th_sum); 31412693Sjojemann fix_datacksum(&tcp->th_sum, sumd); 31422693Sjojemann sum2 = ntohs(tcp->th_sum); 31432693Sjojemann sumd2 = sumd << 1; 31442393Syz155240 CALC_SUMD(sum1, sum2, sumd); 31452393Syz155240 sumd2 += sumd; 31462693Sjojemann 31472693Sjojemann /* 31482693Sjojemann * Also make data checksum adjustment to 31492693Sjojemann * compensate for any port change. 31502693Sjojemann */ 31512693Sjojemann 31522693Sjojemann if (psum1 != psum2) { 31532693Sjojemann CALC_SUMD(psum1, psum2, psumd); 31542693Sjojemann fix_datacksum(&tcp->th_sum, psumd); 31552393Syz155240 } 31562693Sjojemann 31572693Sjojemann } else if ((oip->ip_p == IPPROTO_UDP) && 31582693Sjojemann (dlen >= 8) && (udp->uh_sum != 0)) { 31592693Sjojemann 31602693Sjojemann /* 31612693Sjojemann * The UDP checksum is present and set. 31622693Sjojemann * 31632693Sjojemann * Adjust data checksum and icmp checksum to 31642693Sjojemann * compensate for any IP address change. 31652693Sjojemann */ 31662693Sjojemann 31672693Sjojemann sum1 = ntohs(udp->uh_sum); 31682693Sjojemann fix_datacksum(&udp->uh_sum, sumd); 31692693Sjojemann sum2 = ntohs(udp->uh_sum); 31702693Sjojemann sumd2 = sumd << 1; 31712693Sjojemann CALC_SUMD(sum1, sum2, sumd); 31722393Syz155240 sumd2 += sumd; 31732393Syz155240 31742393Syz155240 /* 31752693Sjojemann * Also make data checksum adjustment to 31762693Sjojemann * compensate for any port change. 31772393Syz155240 */ 31782693Sjojemann 31792693Sjojemann if (psum1 != psum2) { 31802693Sjojemann CALC_SUMD(psum1, psum2, psumd); 31812693Sjojemann fix_datacksum(&udp->uh_sum, psumd); 31822693Sjojemann } 31832693Sjojemann 31842693Sjojemann } else { 31852693Sjojemann 31862693Sjojemann /* 31872693Sjojemann * Data checksum was not present. 31882693Sjojemann * 31892693Sjojemann * Compensate for any port change. 31902693Sjojemann */ 31912693Sjojemann 31922693Sjojemann CALC_SUMD(psum2, psum1, psumd); 31932693Sjojemann sumd2 += psumd; 31942393Syz155240 } 31952693Sjojemann break; 31962693Sjojemann 31972693Sjojemann case IPPROTO_ICMP : 31982693Sjojemann 31992693Sjojemann orgicmp = (icmphdr_t *)udp; 32002693Sjojemann 32012693Sjojemann if ((nat->nat_dir == NAT_OUTBOUND) && 32022693Sjojemann (orgicmp->icmp_id != nat->nat_inport) && 32032693Sjojemann (dlen >= 8)) { 32042393Syz155240 32052393Syz155240 /* 32062393Syz155240 * Fix ICMP checksum (of the offening ICMP 32072393Syz155240 * query packet) to compensate the change 32082393Syz155240 * in the ICMP id of the offending ICMP 32092393Syz155240 * packet. 32102393Syz155240 * 32112393Syz155240 * Since you modify orgicmp->icmp_id with 32122393Syz155240 * a delta (say x) and you compensate that 32132393Syz155240 * in origicmp->icmp_cksum with a delta 32142393Syz155240 * minus x, you don't have to adjust the 32152393Syz155240 * overall icmp->icmp_cksum 32162393Syz155240 */ 32172693Sjojemann 32182393Syz155240 sum1 = ntohs(orgicmp->icmp_id); 32192393Syz155240 sum2 = ntohs(nat->nat_inport); 32202393Syz155240 CALC_SUMD(sum1, sum2, sumd); 32212393Syz155240 orgicmp->icmp_id = nat->nat_inport; 32222393Syz155240 fix_datacksum(&orgicmp->icmp_cksum, sumd); 32232693Sjojemann 32242693Sjojemann } /* nat_dir can't be NAT_INBOUND for icmp queries */ 32252693Sjojemann 32262693Sjojemann break; 32272693Sjojemann 32282693Sjojemann default : 32292693Sjojemann 32302693Sjojemann break; 32312693Sjojemann 32322693Sjojemann } /* switch (oip->ip_p) */ 32332693Sjojemann 32342693Sjojemann /* 32352693Sjojemann * Step 3 32362693Sjojemann * Make the adjustments to icmp checksum. 32372693Sjojemann */ 32382693Sjojemann 32392693Sjojemann if (sumd2 != 0) { 32402693Sjojemann sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 32412693Sjojemann sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 32422958Sdr146992 fix_incksum(&icmp->icmp_cksum, sumd2); 32432393Syz155240 } 32442393Syz155240 return nat; 32452393Syz155240 } 32462393Syz155240 32472393Syz155240 32482393Syz155240 /* 32492393Syz155240 * NB: these lookups don't lock access to the list, it assumed that it has 32502393Syz155240 * already been done! 32512393Syz155240 */ 32522393Syz155240 32532393Syz155240 /* ------------------------------------------------------------------------ */ 32542393Syz155240 /* Function: nat_inlookup */ 32552393Syz155240 /* Returns: nat_t* - NULL == no match, */ 32562393Syz155240 /* else pointer to matching NAT entry */ 32572393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 32582393Syz155240 /* flags(I) - NAT flags for this packet */ 32592393Syz155240 /* p(I) - protocol for this packet */ 32602393Syz155240 /* src(I) - source IP address */ 32612393Syz155240 /* mapdst(I) - destination IP address */ 32622393Syz155240 /* */ 32632393Syz155240 /* Lookup a nat entry based on the mapped destination ip address/port and */ 32642393Syz155240 /* real source address/port. We use this lookup when receiving a packet, */ 32652393Syz155240 /* we're looking for a table entry, based on the destination address. */ 32662393Syz155240 /* */ 32672393Syz155240 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 32682393Syz155240 /* */ 32692393Syz155240 /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */ 32702393Syz155240 /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 32712393Syz155240 /* */ 32722393Syz155240 /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 32732393Syz155240 /* the packet is of said protocol */ 32742393Syz155240 /* ------------------------------------------------------------------------ */ 32752393Syz155240 nat_t *nat_inlookup(fin, flags, p, src, mapdst) 32762393Syz155240 fr_info_t *fin; 32772393Syz155240 u_int flags, p; 32782393Syz155240 struct in_addr src , mapdst; 32792393Syz155240 { 32802393Syz155240 u_short sport, dport; 32812393Syz155240 ipnat_t *ipn; 32822393Syz155240 u_int sflags; 32832393Syz155240 nat_t *nat; 32842393Syz155240 int nflags; 32852393Syz155240 u_32_t dst; 32862393Syz155240 void *ifp; 32872393Syz155240 u_int hv; 32883448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 32892393Syz155240 32902393Syz155240 if (fin != NULL) 32912393Syz155240 ifp = fin->fin_ifp; 32922393Syz155240 else 32932393Syz155240 ifp = NULL; 32942393Syz155240 sport = 0; 32952393Syz155240 dport = 0; 32962393Syz155240 dst = mapdst.s_addr; 32972393Syz155240 sflags = flags & NAT_TCPUDPICMP; 32982393Syz155240 32992393Syz155240 switch (p) 33002393Syz155240 { 33012393Syz155240 case IPPROTO_TCP : 33022393Syz155240 case IPPROTO_UDP : 33032393Syz155240 sport = htons(fin->fin_data[0]); 33042393Syz155240 dport = htons(fin->fin_data[1]); 33052393Syz155240 break; 33062393Syz155240 case IPPROTO_ICMP : 33072393Syz155240 if (flags & IPN_ICMPERR) 33082393Syz155240 sport = fin->fin_data[1]; 33092393Syz155240 else 33102393Syz155240 dport = fin->fin_data[1]; 33112393Syz155240 break; 33122393Syz155240 default : 33132393Syz155240 break; 33142393Syz155240 } 33152393Syz155240 33162393Syz155240 33172393Syz155240 if ((flags & SI_WILDP) != 0) 33182393Syz155240 goto find_in_wild_ports; 33192393Syz155240 33202393Syz155240 hv = NAT_HASH_FN(dst, dport, 0xffffffff); 33213448Sdh155122 hv = NAT_HASH_FN(src.s_addr, hv + sport, ifs->ifs_ipf_nattable_sz); 33223448Sdh155122 nat = ifs->ifs_nat_table[1][hv]; 33232393Syz155240 for (; nat; nat = nat->nat_hnext[1]) { 33247176Syx160601 if (nat->nat_v != 4) 33257176Syx160601 continue; 33267176Syx160601 33272508Syz155240 if (nat->nat_ifps[0] != NULL) { 33282508Syz155240 if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 33292508Syz155240 continue; 33302508Syz155240 } else if (ifp != NULL) 33312508Syz155240 nat->nat_ifps[0] = ifp; 33322508Syz155240 33332393Syz155240 nflags = nat->nat_flags; 33342393Syz155240 33352393Syz155240 if (nat->nat_oip.s_addr == src.s_addr && 33362393Syz155240 nat->nat_outip.s_addr == dst && 33372393Syz155240 (((p == 0) && 33382393Syz155240 (sflags == (nat->nat_flags & IPN_TCPUDPICMP))) 33392393Syz155240 || (p == nat->nat_p))) { 33402393Syz155240 switch (p) 33412393Syz155240 { 33422393Syz155240 #if 0 33432393Syz155240 case IPPROTO_GRE : 33442393Syz155240 if (nat->nat_call[1] != fin->fin_data[0]) 33452393Syz155240 continue; 33462393Syz155240 break; 33472393Syz155240 #endif 33482393Syz155240 case IPPROTO_ICMP : 33492393Syz155240 if ((flags & IPN_ICMPERR) != 0) { 33502393Syz155240 if (nat->nat_outport != sport) 33512393Syz155240 continue; 33522393Syz155240 } else { 33532393Syz155240 if (nat->nat_outport != dport) 33542393Syz155240 continue; 33552393Syz155240 } 33562393Syz155240 break; 33572393Syz155240 case IPPROTO_TCP : 33582393Syz155240 case IPPROTO_UDP : 33592393Syz155240 if (nat->nat_oport != sport) 33602393Syz155240 continue; 33612393Syz155240 if (nat->nat_outport != dport) 33622393Syz155240 continue; 33632393Syz155240 break; 33642393Syz155240 default : 33652393Syz155240 break; 33662393Syz155240 } 33672393Syz155240 33682393Syz155240 ipn = nat->nat_ptr; 33692393Syz155240 if ((ipn != NULL) && (nat->nat_aps != NULL)) 33702393Syz155240 if (appr_match(fin, nat) != 0) 33712393Syz155240 continue; 33722393Syz155240 return nat; 33732393Syz155240 } 33742393Syz155240 } 33752393Syz155240 33762393Syz155240 /* 33772393Syz155240 * So if we didn't find it but there are wildcard members in the hash 33782393Syz155240 * table, go back and look for them. We do this search and update here 33792393Syz155240 * because it is modifying the NAT table and we want to do this only 33802393Syz155240 * for the first packet that matches. The exception, of course, is 33812393Syz155240 * for "dummy" (FI_IGNORE) lookups. 33822393Syz155240 */ 33832393Syz155240 find_in_wild_ports: 33842393Syz155240 if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) 33852393Syz155240 return NULL; 33863448Sdh155122 if (ifs->ifs_nat_stats.ns_wilds == 0) 33872393Syz155240 return NULL; 33882393Syz155240 33893448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 33902393Syz155240 33912393Syz155240 hv = NAT_HASH_FN(dst, 0, 0xffffffff); 33923448Sdh155122 hv = NAT_HASH_FN(src.s_addr, hv, ifs->ifs_ipf_nattable_sz); 33933448Sdh155122 33943448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 33953448Sdh155122 33963448Sdh155122 nat = ifs->ifs_nat_table[1][hv]; 33972393Syz155240 for (; nat; nat = nat->nat_hnext[1]) { 33987176Syx160601 if (nat->nat_v != 4) 33997176Syx160601 continue; 34007176Syx160601 34012508Syz155240 if (nat->nat_ifps[0] != NULL) { 34022508Syz155240 if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 34032508Syz155240 continue; 34042508Syz155240 } else if (ifp != NULL) 34052508Syz155240 nat->nat_ifps[0] = ifp; 34062393Syz155240 34072393Syz155240 if (nat->nat_p != fin->fin_p) 34082393Syz155240 continue; 34092393Syz155240 if (nat->nat_oip.s_addr != src.s_addr || 34102393Syz155240 nat->nat_outip.s_addr != dst) 34112393Syz155240 continue; 34122393Syz155240 34132393Syz155240 nflags = nat->nat_flags; 34142393Syz155240 if (!(nflags & (NAT_TCPUDP|SI_WILDP))) 34152393Syz155240 continue; 34162393Syz155240 34172393Syz155240 if (nat_wildok(nat, (int)sport, (int)dport, nflags, 34182393Syz155240 NAT_INBOUND) == 1) { 34192393Syz155240 if ((fin->fin_flx & FI_IGNORE) != 0) 34202393Syz155240 break; 34212393Syz155240 if ((nflags & SI_CLONE) != 0) { 34222393Syz155240 nat = fr_natclone(fin, nat); 34232393Syz155240 if (nat == NULL) 34242393Syz155240 break; 34252393Syz155240 } else { 34263448Sdh155122 MUTEX_ENTER(&ifs->ifs_ipf_nat_new); 34273448Sdh155122 ifs->ifs_nat_stats.ns_wilds--; 34283448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_nat_new); 34292393Syz155240 } 34302393Syz155240 nat->nat_oport = sport; 34312393Syz155240 nat->nat_outport = dport; 34322393Syz155240 nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 34333448Sdh155122 nat_tabmove(nat, ifs); 34342393Syz155240 break; 34352393Syz155240 } 34362393Syz155240 } 34372393Syz155240 34383448Sdh155122 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); 34392393Syz155240 34402393Syz155240 return nat; 34412393Syz155240 } 34422393Syz155240 34432393Syz155240 34442393Syz155240 /* ------------------------------------------------------------------------ */ 34452393Syz155240 /* Function: nat_tabmove */ 34462393Syz155240 /* Returns: Nil */ 34472393Syz155240 /* Parameters: nat(I) - pointer to NAT structure */ 34482393Syz155240 /* Write Lock: ipf_nat */ 34492393Syz155240 /* */ 34502393Syz155240 /* This function is only called for TCP/UDP NAT table entries where the */ 34512393Syz155240 /* original was placed in the table without hashing on the ports and we now */ 34522393Syz155240 /* want to include hashing on port numbers. */ 34532393Syz155240 /* ------------------------------------------------------------------------ */ 34543448Sdh155122 static void nat_tabmove(nat, ifs) 34552393Syz155240 nat_t *nat; 34563448Sdh155122 ipf_stack_t *ifs; 34572393Syz155240 { 34582393Syz155240 nat_t **natp; 34592393Syz155240 u_int hv; 34602393Syz155240 34612393Syz155240 if (nat->nat_flags & SI_CLONE) 34622393Syz155240 return; 34632393Syz155240 34642393Syz155240 /* 34652393Syz155240 * Remove the NAT entry from the old location 34662393Syz155240 */ 34672393Syz155240 if (nat->nat_hnext[0]) 34682393Syz155240 nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 34692393Syz155240 *nat->nat_phnext[0] = nat->nat_hnext[0]; 34703448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--; 34712393Syz155240 34722393Syz155240 if (nat->nat_hnext[1]) 34732393Syz155240 nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 34742393Syz155240 *nat->nat_phnext[1] = nat->nat_hnext[1]; 34753448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--; 34762393Syz155240 34772393Syz155240 /* 34782393Syz155240 * Add into the NAT table in the new position 34792393Syz155240 */ 34802393Syz155240 hv = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 0xffffffff); 34812393Syz155240 hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport, 34823448Sdh155122 ifs->ifs_ipf_nattable_sz); 34832393Syz155240 nat->nat_hv[0] = hv; 34843448Sdh155122 natp = &ifs->ifs_nat_table[0][hv]; 34852393Syz155240 if (*natp) 34862393Syz155240 (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 34872393Syz155240 nat->nat_phnext[0] = natp; 34882393Syz155240 nat->nat_hnext[0] = *natp; 34892393Syz155240 *natp = nat; 34903448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[0][hv]++; 34912393Syz155240 34922393Syz155240 hv = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, 0xffffffff); 34932393Syz155240 hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport, 34943448Sdh155122 ifs->ifs_ipf_nattable_sz); 34952393Syz155240 nat->nat_hv[1] = hv; 34963448Sdh155122 natp = &ifs->ifs_nat_table[1][hv]; 34972393Syz155240 if (*natp) 34982393Syz155240 (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 34992393Syz155240 nat->nat_phnext[1] = natp; 35002393Syz155240 nat->nat_hnext[1] = *natp; 35012393Syz155240 *natp = nat; 35023448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[1][hv]++; 35032393Syz155240 } 35042393Syz155240 35052393Syz155240 35062393Syz155240 /* ------------------------------------------------------------------------ */ 35072393Syz155240 /* Function: nat_outlookup */ 35082393Syz155240 /* Returns: nat_t* - NULL == no match, */ 35092393Syz155240 /* else pointer to matching NAT entry */ 35102393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 35112393Syz155240 /* flags(I) - NAT flags for this packet */ 35122393Syz155240 /* p(I) - protocol for this packet */ 35132393Syz155240 /* src(I) - source IP address */ 35142393Syz155240 /* dst(I) - destination IP address */ 35152393Syz155240 /* rw(I) - 1 == write lock on ipf_nat held, 0 == read lock. */ 35162393Syz155240 /* */ 35172393Syz155240 /* Lookup a nat entry based on the source 'real' ip address/port and */ 35182393Syz155240 /* destination address/port. We use this lookup when sending a packet out, */ 35192393Syz155240 /* we're looking for a table entry, based on the source address. */ 35202393Syz155240 /* */ 35212393Syz155240 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 35222393Syz155240 /* */ 35232393Syz155240 /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */ 35242393Syz155240 /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 35252393Syz155240 /* */ 35262393Syz155240 /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 35272393Syz155240 /* the packet is of said protocol */ 35282393Syz155240 /* ------------------------------------------------------------------------ */ 35292393Syz155240 nat_t *nat_outlookup(fin, flags, p, src, dst) 35302393Syz155240 fr_info_t *fin; 35312393Syz155240 u_int flags, p; 35322393Syz155240 struct in_addr src , dst; 35332393Syz155240 { 35342393Syz155240 u_short sport, dport; 35352393Syz155240 u_int sflags; 35362393Syz155240 ipnat_t *ipn; 35372393Syz155240 u_32_t srcip; 35382393Syz155240 nat_t *nat; 35392393Syz155240 int nflags; 35402393Syz155240 void *ifp; 35412393Syz155240 u_int hv; 35423448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 35432508Syz155240 35443894Sjojemann ifp = fin->fin_ifp; 35452508Syz155240 35462393Syz155240 srcip = src.s_addr; 35472393Syz155240 sflags = flags & IPN_TCPUDPICMP; 35482393Syz155240 sport = 0; 35492393Syz155240 dport = 0; 35502393Syz155240 35512393Syz155240 switch (p) 35522393Syz155240 { 35532393Syz155240 case IPPROTO_TCP : 35542393Syz155240 case IPPROTO_UDP : 35552393Syz155240 sport = htons(fin->fin_data[0]); 35562393Syz155240 dport = htons(fin->fin_data[1]); 35572393Syz155240 break; 35582393Syz155240 case IPPROTO_ICMP : 35592393Syz155240 if (flags & IPN_ICMPERR) 35602393Syz155240 sport = fin->fin_data[1]; 35612393Syz155240 else 35622393Syz155240 dport = fin->fin_data[1]; 35632393Syz155240 break; 35642393Syz155240 default : 35652393Syz155240 break; 35662393Syz155240 } 35672393Syz155240 35682393Syz155240 if ((flags & SI_WILDP) != 0) 35692393Syz155240 goto find_out_wild_ports; 35702393Syz155240 35712393Syz155240 hv = NAT_HASH_FN(srcip, sport, 0xffffffff); 35723448Sdh155122 hv = NAT_HASH_FN(dst.s_addr, hv + dport, ifs->ifs_ipf_nattable_sz); 35733448Sdh155122 nat = ifs->ifs_nat_table[0][hv]; 35742393Syz155240 for (; nat; nat = nat->nat_hnext[0]) { 35757176Syx160601 if (nat->nat_v != 4) 35767176Syx160601 continue; 35777176Syx160601 35782508Syz155240 if (nat->nat_ifps[1] != NULL) { 35792508Syz155240 if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 35802508Syz155240 continue; 35812508Syz155240 } else if (ifp != NULL) 35822508Syz155240 nat->nat_ifps[1] = ifp; 35832508Syz155240 35842393Syz155240 nflags = nat->nat_flags; 35852508Syz155240 35862393Syz155240 if (nat->nat_inip.s_addr == srcip && 35872393Syz155240 nat->nat_oip.s_addr == dst.s_addr && 35882393Syz155240 (((p == 0) && (sflags == (nflags & NAT_TCPUDPICMP))) 35892393Syz155240 || (p == nat->nat_p))) { 35902393Syz155240 switch (p) 35912393Syz155240 { 35922393Syz155240 #if 0 35932393Syz155240 case IPPROTO_GRE : 35942393Syz155240 if (nat->nat_call[1] != fin->fin_data[0]) 35952393Syz155240 continue; 35962393Syz155240 break; 35972393Syz155240 #endif 35982393Syz155240 case IPPROTO_TCP : 35992393Syz155240 case IPPROTO_UDP : 36002393Syz155240 if (nat->nat_oport != dport) 36012393Syz155240 continue; 36022393Syz155240 if (nat->nat_inport != sport) 36032393Syz155240 continue; 36042393Syz155240 break; 36052393Syz155240 default : 36062393Syz155240 break; 36072393Syz155240 } 36082393Syz155240 36092393Syz155240 ipn = nat->nat_ptr; 36102393Syz155240 if ((ipn != NULL) && (nat->nat_aps != NULL)) 36112393Syz155240 if (appr_match(fin, nat) != 0) 36122393Syz155240 continue; 36132393Syz155240 return nat; 36142393Syz155240 } 36152393Syz155240 } 36162393Syz155240 36172393Syz155240 /* 36182393Syz155240 * So if we didn't find it but there are wildcard members in the hash 36192393Syz155240 * table, go back and look for them. We do this search and update here 36202393Syz155240 * because it is modifying the NAT table and we want to do this only 36212393Syz155240 * for the first packet that matches. The exception, of course, is 36222393Syz155240 * for "dummy" (FI_IGNORE) lookups. 36232393Syz155240 */ 36242393Syz155240 find_out_wild_ports: 36252393Syz155240 if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) 36262393Syz155240 return NULL; 36273448Sdh155122 if (ifs->ifs_nat_stats.ns_wilds == 0) 36282393Syz155240 return NULL; 36292393Syz155240 36303448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 36312393Syz155240 36322393Syz155240 hv = NAT_HASH_FN(srcip, 0, 0xffffffff); 36333448Sdh155122 hv = NAT_HASH_FN(dst.s_addr, hv, ifs->ifs_ipf_nattable_sz); 36343448Sdh155122 36353448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 36363448Sdh155122 36373448Sdh155122 nat = ifs->ifs_nat_table[0][hv]; 36382393Syz155240 for (; nat; nat = nat->nat_hnext[0]) { 36397176Syx160601 if (nat->nat_v != 4) 36407176Syx160601 continue; 36417176Syx160601 36422508Syz155240 if (nat->nat_ifps[1] != NULL) { 36432508Syz155240 if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 36443448Sdh155122 continue; 36452508Syz155240 } else if (ifp != NULL) 36462508Syz155240 nat->nat_ifps[1] = ifp; 36472393Syz155240 36482393Syz155240 if (nat->nat_p != fin->fin_p) 36492393Syz155240 continue; 36502393Syz155240 if ((nat->nat_inip.s_addr != srcip) || 36512393Syz155240 (nat->nat_oip.s_addr != dst.s_addr)) 36522393Syz155240 continue; 36532393Syz155240 36542393Syz155240 nflags = nat->nat_flags; 36552393Syz155240 if (!(nflags & (NAT_TCPUDP|SI_WILDP))) 36562393Syz155240 continue; 36572393Syz155240 36582393Syz155240 if (nat_wildok(nat, (int)sport, (int)dport, nflags, 36592393Syz155240 NAT_OUTBOUND) == 1) { 36602393Syz155240 if ((fin->fin_flx & FI_IGNORE) != 0) 36612393Syz155240 break; 36622393Syz155240 if ((nflags & SI_CLONE) != 0) { 36632393Syz155240 nat = fr_natclone(fin, nat); 36642393Syz155240 if (nat == NULL) 36652393Syz155240 break; 36662393Syz155240 } else { 36673448Sdh155122 MUTEX_ENTER(&ifs->ifs_ipf_nat_new); 36683448Sdh155122 ifs->ifs_nat_stats.ns_wilds--; 36693448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_nat_new); 36702393Syz155240 } 36712393Syz155240 nat->nat_inport = sport; 36722393Syz155240 nat->nat_oport = dport; 36732393Syz155240 if (nat->nat_outport == 0) 36742393Syz155240 nat->nat_outport = sport; 36752393Syz155240 nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 36763448Sdh155122 nat_tabmove(nat, ifs); 36772393Syz155240 break; 36782393Syz155240 } 36792393Syz155240 } 36802393Syz155240 36813448Sdh155122 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); 36822393Syz155240 36832393Syz155240 return nat; 36842393Syz155240 } 36852393Syz155240 36862393Syz155240 36872393Syz155240 /* ------------------------------------------------------------------------ */ 36882393Syz155240 /* Function: nat_lookupredir */ 36892393Syz155240 /* Returns: nat_t* - NULL == no match, */ 36902393Syz155240 /* else pointer to matching NAT entry */ 36912393Syz155240 /* Parameters: np(I) - pointer to description of packet to find NAT table */ 36922393Syz155240 /* entry for. */ 36932393Syz155240 /* */ 36942393Syz155240 /* Lookup the NAT tables to search for a matching redirect */ 36952393Syz155240 /* ------------------------------------------------------------------------ */ 36963448Sdh155122 nat_t *nat_lookupredir(np, ifs) 36972393Syz155240 natlookup_t *np; 36983448Sdh155122 ipf_stack_t *ifs; 36992393Syz155240 { 37002393Syz155240 fr_info_t fi; 37012393Syz155240 nat_t *nat; 37022393Syz155240 37032393Syz155240 bzero((char *)&fi, sizeof(fi)); 37042393Syz155240 if (np->nl_flags & IPN_IN) { 37052393Syz155240 fi.fin_data[0] = ntohs(np->nl_realport); 37062393Syz155240 fi.fin_data[1] = ntohs(np->nl_outport); 37072393Syz155240 } else { 37082393Syz155240 fi.fin_data[0] = ntohs(np->nl_inport); 37092393Syz155240 fi.fin_data[1] = ntohs(np->nl_outport); 37102393Syz155240 } 37112393Syz155240 if (np->nl_flags & IPN_TCP) 37122393Syz155240 fi.fin_p = IPPROTO_TCP; 37132393Syz155240 else if (np->nl_flags & IPN_UDP) 37142393Syz155240 fi.fin_p = IPPROTO_UDP; 37152393Syz155240 else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) 37162393Syz155240 fi.fin_p = IPPROTO_ICMP; 37172393Syz155240 37183448Sdh155122 fi.fin_ifs = ifs; 37192393Syz155240 /* 37202393Syz155240 * We can do two sorts of lookups: 37212393Syz155240 * - IPN_IN: we have the `real' and `out' address, look for `in'. 37222393Syz155240 * - default: we have the `in' and `out' address, look for `real'. 37232393Syz155240 */ 37242393Syz155240 if (np->nl_flags & IPN_IN) { 37252393Syz155240 if ((nat = nat_inlookup(&fi, np->nl_flags, fi.fin_p, 37262393Syz155240 np->nl_realip, np->nl_outip))) { 37272393Syz155240 np->nl_inip = nat->nat_inip; 37282393Syz155240 np->nl_inport = nat->nat_inport; 37292393Syz155240 } 37302393Syz155240 } else { 37312393Syz155240 /* 37322393Syz155240 * If nl_inip is non null, this is a lookup based on the real 37332393Syz155240 * ip address. Else, we use the fake. 37342393Syz155240 */ 37352393Syz155240 if ((nat = nat_outlookup(&fi, np->nl_flags, fi.fin_p, 37362393Syz155240 np->nl_inip, np->nl_outip))) { 37372393Syz155240 37382393Syz155240 if ((np->nl_flags & IPN_FINDFORWARD) != 0) { 37392393Syz155240 fr_info_t fin; 37402393Syz155240 bzero((char *)&fin, sizeof(fin)); 37412393Syz155240 fin.fin_p = nat->nat_p; 37422393Syz155240 fin.fin_data[0] = ntohs(nat->nat_outport); 37432393Syz155240 fin.fin_data[1] = ntohs(nat->nat_oport); 37443448Sdh155122 fin.fin_ifs = ifs; 37452393Syz155240 if (nat_inlookup(&fin, np->nl_flags, fin.fin_p, 37462393Syz155240 nat->nat_outip, 37472393Syz155240 nat->nat_oip) != NULL) { 37482393Syz155240 np->nl_flags &= ~IPN_FINDFORWARD; 37492393Syz155240 } 37502393Syz155240 } 37512393Syz155240 37522393Syz155240 np->nl_realip = nat->nat_outip; 37532393Syz155240 np->nl_realport = nat->nat_outport; 37542393Syz155240 } 37552393Syz155240 } 37562393Syz155240 37572393Syz155240 return nat; 37582393Syz155240 } 37592393Syz155240 37602393Syz155240 37612393Syz155240 /* ------------------------------------------------------------------------ */ 37622393Syz155240 /* Function: nat_match */ 37632393Syz155240 /* Returns: int - 0 == no match, 1 == match */ 37642393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 37652393Syz155240 /* np(I) - pointer to NAT rule */ 37662393Syz155240 /* */ 37672393Syz155240 /* Pull the matching of a packet against a NAT rule out of that complex */ 37682393Syz155240 /* loop inside fr_checknatin() and lay it out properly in its own function. */ 37692393Syz155240 /* ------------------------------------------------------------------------ */ 37702393Syz155240 static int nat_match(fin, np) 37712393Syz155240 fr_info_t *fin; 37722393Syz155240 ipnat_t *np; 37732393Syz155240 { 37742393Syz155240 frtuc_t *ft; 37752393Syz155240 37762393Syz155240 if (fin->fin_v != 4) 37772393Syz155240 return 0; 37782393Syz155240 37792393Syz155240 if (np->in_p && fin->fin_p != np->in_p) 37802393Syz155240 return 0; 37812393Syz155240 37822393Syz155240 if (fin->fin_out) { 37832393Syz155240 if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK))) 37842393Syz155240 return 0; 37852393Syz155240 if (((fin->fin_fi.fi_saddr & np->in_inmsk) != np->in_inip) 37862393Syz155240 ^ ((np->in_flags & IPN_NOTSRC) != 0)) 37872393Syz155240 return 0; 37882393Syz155240 if (((fin->fin_fi.fi_daddr & np->in_srcmsk) != np->in_srcip) 37892393Syz155240 ^ ((np->in_flags & IPN_NOTDST) != 0)) 37902393Syz155240 return 0; 37912393Syz155240 } else { 37922393Syz155240 if (!(np->in_redir & NAT_REDIRECT)) 37932393Syz155240 return 0; 37942393Syz155240 if (((fin->fin_fi.fi_saddr & np->in_srcmsk) != np->in_srcip) 37952393Syz155240 ^ ((np->in_flags & IPN_NOTSRC) != 0)) 37962393Syz155240 return 0; 37972393Syz155240 if (((fin->fin_fi.fi_daddr & np->in_outmsk) != np->in_outip) 37982393Syz155240 ^ ((np->in_flags & IPN_NOTDST) != 0)) 37992393Syz155240 return 0; 38002393Syz155240 } 38012393Syz155240 38022393Syz155240 ft = &np->in_tuc; 38032393Syz155240 if (!(fin->fin_flx & FI_TCPUDP) || 38042393Syz155240 (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { 38052393Syz155240 if (ft->ftu_scmp || ft->ftu_dcmp) 38062393Syz155240 return 0; 38072393Syz155240 return 1; 38082393Syz155240 } 38092393Syz155240 38102393Syz155240 return fr_tcpudpchk(fin, ft); 38112393Syz155240 } 38122393Syz155240 38132393Syz155240 38142393Syz155240 /* ------------------------------------------------------------------------ */ 38152393Syz155240 /* Function: nat_update */ 38162393Syz155240 /* Returns: Nil */ 38177333SJohn.Ojemann@Sun.COM /* Parameters: fin(I) - pointer to packet information */ 38187333SJohn.Ojemann@Sun.COM /* nat(I) - pointer to NAT structure */ 38192393Syz155240 /* np(I) - pointer to NAT rule */ 38207333SJohn.Ojemann@Sun.COM /* Locks: nat_lock */ 38212393Syz155240 /* */ 38222393Syz155240 /* Updates the lifetime of a NAT table entry for non-TCP packets. Must be */ 38232393Syz155240 /* called with fin_rev updated - i.e. after calling nat_proto(). */ 38242393Syz155240 /* ------------------------------------------------------------------------ */ 38252393Syz155240 void nat_update(fin, nat, np) 38262393Syz155240 fr_info_t *fin; 38272393Syz155240 nat_t *nat; 38282393Syz155240 ipnat_t *np; 38292393Syz155240 { 38302393Syz155240 ipftq_t *ifq, *ifq2; 38312393Syz155240 ipftqent_t *tqe; 38323448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 38332393Syz155240 38342393Syz155240 tqe = &nat->nat_tqe; 38352393Syz155240 ifq = tqe->tqe_ifq; 38362393Syz155240 38372393Syz155240 /* 38382393Syz155240 * We allow over-riding of NAT timeouts from NAT rules, even for 38392393Syz155240 * TCP, however, if it is TCP and there is no rule timeout set, 38402393Syz155240 * then do not update the timeout here. 38412393Syz155240 */ 38422393Syz155240 if (np != NULL) 38432393Syz155240 ifq2 = np->in_tqehead[fin->fin_rev]; 38442393Syz155240 else 38452393Syz155240 ifq2 = NULL; 38462393Syz155240 38472393Syz155240 if (nat->nat_p == IPPROTO_TCP && ifq2 == NULL) { 38483448Sdh155122 (void) fr_tcp_age(&nat->nat_tqe, fin, ifs->ifs_nat_tqb, 0); 38492393Syz155240 } else { 38502393Syz155240 if (ifq2 == NULL) { 38512393Syz155240 if (nat->nat_p == IPPROTO_UDP) 38523448Sdh155122 ifq2 = &ifs->ifs_nat_udptq; 38532393Syz155240 else if (nat->nat_p == IPPROTO_ICMP) 38543448Sdh155122 ifq2 = &ifs->ifs_nat_icmptq; 38552393Syz155240 else 38563448Sdh155122 ifq2 = &ifs->ifs_nat_iptq; 38572393Syz155240 } 38582393Syz155240 38593448Sdh155122 fr_movequeue(tqe, ifq, ifq2, ifs); 38602393Syz155240 } 38612393Syz155240 } 38622393Syz155240 38632393Syz155240 38642393Syz155240 /* ------------------------------------------------------------------------ */ 38652393Syz155240 /* Function: fr_checknatout */ 38662393Syz155240 /* Returns: int - -1 == packet failed NAT checks so block it, */ 38672393Syz155240 /* 0 == no packet translation occurred, */ 38682393Syz155240 /* 1 == packet was successfully translated. */ 38692393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 38702393Syz155240 /* passp(I) - pointer to filtering result flags */ 38712393Syz155240 /* */ 38722393Syz155240 /* Check to see if an outcoming packet should be changed. ICMP packets are */ 38732393Syz155240 /* first checked to see if they match an existing entry (if an error), */ 38742393Syz155240 /* otherwise a search of the current NAT table is made. If neither results */ 38752393Syz155240 /* in a match then a search for a matching NAT rule is made. Create a new */ 38762393Syz155240 /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 38772393Syz155240 /* packet header(s) as required. */ 38782393Syz155240 /* ------------------------------------------------------------------------ */ 38792393Syz155240 int fr_checknatout(fin, passp) 38802393Syz155240 fr_info_t *fin; 38812393Syz155240 u_32_t *passp; 38822393Syz155240 { 38837131Sdr146992 ipnat_t *np = NULL, *npnext; 38842393Syz155240 struct ifnet *ifp, *sifp; 38852393Syz155240 icmphdr_t *icmp = NULL; 38862393Syz155240 tcphdr_t *tcp = NULL; 38872393Syz155240 int rval, natfailed; 38882393Syz155240 u_int nflags = 0; 38892393Syz155240 u_32_t ipa, iph; 38902393Syz155240 int natadd = 1; 38912393Syz155240 frentry_t *fr; 38922393Syz155240 nat_t *nat; 38933448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 38943448Sdh155122 38957432SJohn.Ojemann@Sun.COM if (ifs->ifs_fr_nat_lock != 0) 38967432SJohn.Ojemann@Sun.COM return 0; 38977432SJohn.Ojemann@Sun.COM if (ifs->ifs_nat_stats.ns_rules == 0 && ifs->ifs_nat_instances == NULL) 38982393Syz155240 return 0; 38992393Syz155240 39002393Syz155240 natfailed = 0; 39012393Syz155240 fr = fin->fin_fr; 39022393Syz155240 sifp = fin->fin_ifp; 39032393Syz155240 if ((fr != NULL) && !(fr->fr_flags & FR_DUP) && 39043894Sjojemann fr->fr_tifs[fin->fin_rev].fd_ifp && 39053894Sjojemann fr->fr_tifs[fin->fin_rev].fd_ifp != (void *)-1) 39063894Sjojemann fin->fin_ifp = fr->fr_tifs[fin->fin_rev].fd_ifp; 39072393Syz155240 ifp = fin->fin_ifp; 39082393Syz155240 39092393Syz155240 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 39102393Syz155240 switch (fin->fin_p) 39112393Syz155240 { 39122393Syz155240 case IPPROTO_TCP : 39132393Syz155240 nflags = IPN_TCP; 39142393Syz155240 break; 39152393Syz155240 case IPPROTO_UDP : 39162393Syz155240 nflags = IPN_UDP; 39172393Syz155240 break; 39182393Syz155240 case IPPROTO_ICMP : 39192393Syz155240 icmp = fin->fin_dp; 39202393Syz155240 39212393Syz155240 /* 39222393Syz155240 * This is an incoming packet, so the destination is 39232393Syz155240 * the icmp_id and the source port equals 0 39242393Syz155240 */ 39252393Syz155240 if (nat_icmpquerytype4(icmp->icmp_type)) 39262393Syz155240 nflags = IPN_ICMPQUERY; 39272393Syz155240 break; 39282393Syz155240 default : 39292393Syz155240 break; 39302393Syz155240 } 39312393Syz155240 39322393Syz155240 if ((nflags & IPN_TCPUDP)) 39332393Syz155240 tcp = fin->fin_dp; 39342393Syz155240 } 39352393Syz155240 39362393Syz155240 ipa = fin->fin_saddr; 39372393Syz155240 39383448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 39392393Syz155240 39402393Syz155240 if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && 39412393Syz155240 (nat = nat_icmperror(fin, &nflags, NAT_OUTBOUND))) 39422393Syz155240 /*EMPTY*/; 39432393Syz155240 else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin))) 39442393Syz155240 natadd = 0; 39452393Syz155240 else if ((nat = nat_outlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, 39462393Syz155240 fin->fin_src, fin->fin_dst))) { 39472393Syz155240 nflags = nat->nat_flags; 39482393Syz155240 } else { 39492393Syz155240 u_32_t hv, msk, nmsk; 39502393Syz155240 39512393Syz155240 /* 39527433SJohn.Ojemann@Sun.COM * There is no current entry in the nat table for this packet. 39537433SJohn.Ojemann@Sun.COM * 39547433SJohn.Ojemann@Sun.COM * If the packet is a fragment, but not the first fragment, 39557433SJohn.Ojemann@Sun.COM * then don't do anything. Otherwise, if there is a matching 39567433SJohn.Ojemann@Sun.COM * nat rule, try to create a new nat entry. 39572393Syz155240 */ 39587433SJohn.Ojemann@Sun.COM if ((fin->fin_off != 0) && (fin->fin_flx & FI_TCPUDP)) 39597433SJohn.Ojemann@Sun.COM goto nonatfrag; 39607433SJohn.Ojemann@Sun.COM 39612393Syz155240 msk = 0xffffffff; 39623448Sdh155122 nmsk = ifs->ifs_nat_masks; 39632393Syz155240 maskloop: 39642393Syz155240 iph = ipa & htonl(msk); 39653448Sdh155122 hv = NAT_HASH_FN(iph, 0, ifs->ifs_ipf_natrules_sz); 39667131Sdr146992 for (np = ifs->ifs_nat_rules[hv]; np; np = npnext) { 39677131Sdr146992 npnext = np->in_mnext; 39682508Syz155240 if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) 39692393Syz155240 continue; 39702393Syz155240 if (np->in_v != fin->fin_v) 39712393Syz155240 continue; 39722393Syz155240 if (np->in_p && (np->in_p != fin->fin_p)) 39732393Syz155240 continue; 39742393Syz155240 if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) 39752393Syz155240 continue; 39762393Syz155240 if (np->in_flags & IPN_FILTER) { 39772393Syz155240 if (!nat_match(fin, np)) 39782393Syz155240 continue; 39792393Syz155240 } else if ((ipa & np->in_inmsk) != np->in_inip) 39802393Syz155240 continue; 39812393Syz155240 39822393Syz155240 if ((fr != NULL) && 39832393Syz155240 !fr_matchtag(&np->in_tag, &fr->fr_nattag)) 39842393Syz155240 continue; 39852393Syz155240 39862393Syz155240 if (*np->in_plabel != '\0') { 39872393Syz155240 if (((np->in_flags & IPN_FILTER) == 0) && 39882393Syz155240 (np->in_dport != tcp->th_dport)) 39892393Syz155240 continue; 39902393Syz155240 if (appr_ok(fin, tcp, np) == 0) 39912393Syz155240 continue; 39922393Syz155240 } 39932393Syz155240 39947131Sdr146992 ATOMIC_INC32(np->in_use); 39957131Sdr146992 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 39967131Sdr146992 WRITE_ENTER(&ifs->ifs_ipf_nat); 39977131Sdr146992 nat = nat_new(fin, np, NULL, nflags, NAT_OUTBOUND); 39987131Sdr146992 if (nat != NULL) { 39997131Sdr146992 np->in_use--; 40002393Syz155240 np->in_hits++; 40017131Sdr146992 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); 40022393Syz155240 break; 40037131Sdr146992 } 40047131Sdr146992 natfailed = -1; 40057131Sdr146992 npnext = np->in_mnext; 40067131Sdr146992 fr_ipnatderef(&np, ifs); 40077131Sdr146992 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); 40082393Syz155240 } 40092393Syz155240 if ((np == NULL) && (nmsk != 0)) { 40102393Syz155240 while (nmsk) { 40112393Syz155240 msk <<= 1; 40122393Syz155240 if (nmsk & 0x80000000) 40132393Syz155240 break; 40142393Syz155240 nmsk <<= 1; 40152393Syz155240 } 40162393Syz155240 if (nmsk != 0) { 40172393Syz155240 nmsk <<= 1; 40182393Syz155240 goto maskloop; 40192393Syz155240 } 40202393Syz155240 } 40212393Syz155240 } 40222393Syz155240 40237433SJohn.Ojemann@Sun.COM nonatfrag: 40242393Syz155240 if (nat != NULL) { 40252393Syz155240 rval = fr_natout(fin, nat, natadd, nflags); 40262393Syz155240 if (rval == 1) { 40272393Syz155240 MUTEX_ENTER(&nat->nat_lock); 40287333SJohn.Ojemann@Sun.COM nat_update(fin, nat, nat->nat_ptr); 40297333SJohn.Ojemann@Sun.COM nat->nat_bytes[1] += fin->fin_plen; 40307333SJohn.Ojemann@Sun.COM nat->nat_pkts[1]++; 40312393Syz155240 nat->nat_ref++; 40322393Syz155240 MUTEX_EXIT(&nat->nat_lock); 40332393Syz155240 fin->fin_nat = nat; 40342393Syz155240 } 40352393Syz155240 } else 40362393Syz155240 rval = natfailed; 40373448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 40382393Syz155240 40392393Syz155240 if (rval == -1) { 40402393Syz155240 if (passp != NULL) 40412393Syz155240 *passp = FR_BLOCK; 40422393Syz155240 fin->fin_flx |= FI_BADNAT; 40432393Syz155240 } 40442393Syz155240 fin->fin_ifp = sifp; 40452393Syz155240 return rval; 40462393Syz155240 } 40472393Syz155240 40482393Syz155240 /* ------------------------------------------------------------------------ */ 40492393Syz155240 /* Function: fr_natout */ 40502393Syz155240 /* Returns: int - -1 == packet failed NAT checks so block it, */ 40512393Syz155240 /* 1 == packet was successfully translated. */ 40522393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 40532393Syz155240 /* nat(I) - pointer to NAT structure */ 40542393Syz155240 /* natadd(I) - flag indicating if it is safe to add frag cache */ 40552393Syz155240 /* nflags(I) - NAT flags set for this packet */ 40562393Syz155240 /* */ 40572393Syz155240 /* Translate a packet coming "out" on an interface. */ 40582393Syz155240 /* ------------------------------------------------------------------------ */ 40592393Syz155240 int fr_natout(fin, nat, natadd, nflags) 40602393Syz155240 fr_info_t *fin; 40612393Syz155240 nat_t *nat; 40622393Syz155240 int natadd; 40632393Syz155240 u_32_t nflags; 40642393Syz155240 { 40652393Syz155240 icmphdr_t *icmp; 40662393Syz155240 u_short *csump; 40672958Sdr146992 u_32_t sumd; 40682393Syz155240 tcphdr_t *tcp; 40692393Syz155240 ipnat_t *np; 40702393Syz155240 int i; 40713448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 40722393Syz155240 40737176Syx160601 if (fin->fin_v == 6) { 40747176Syx160601 #ifdef USE_INET6 40757176Syx160601 return fr_nat6out(fin, nat, natadd, nflags); 40767176Syx160601 #else 40777176Syx160601 return NULL; 40787176Syx160601 #endif 40797176Syx160601 } 40807176Syx160601 40812958Sdr146992 #if SOLARIS && defined(_KERNEL) 40827513SDarren.Reed@Sun.COM net_handle_t net_data_p = ifs->ifs_ipf_ipv4; 40832958Sdr146992 #endif 40842958Sdr146992 40852393Syz155240 tcp = NULL; 40862393Syz155240 icmp = NULL; 40872393Syz155240 csump = NULL; 40882393Syz155240 np = nat->nat_ptr; 40892393Syz155240 40904712Szf203873 if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) 40912393Syz155240 (void) fr_nat_newfrag(fin, 0, nat); 40922958Sdr146992 40932393Syz155240 /* 40942393Syz155240 * Fix up checksums, not by recalculating them, but 40952393Syz155240 * simply computing adjustments. 40962393Syz155240 * This is only done for STREAMS based IP implementations where the 40972393Syz155240 * checksum has already been calculated by IP. In all other cases, 40982393Syz155240 * IPFilter is called before the checksum needs calculating so there 40992393Syz155240 * is no call to modify whatever is in the header now. 41002393Syz155240 */ 41012958Sdr146992 ASSERT(fin->fin_m != NULL); 41022958Sdr146992 if (fin->fin_v == 4 && !NET_IS_HCK_L3_FULL(net_data_p, fin->fin_m)) { 41032393Syz155240 if (nflags == IPN_ICMPERR) { 41042958Sdr146992 u_32_t s1, s2; 41052393Syz155240 41062393Syz155240 s1 = LONG_SUM(ntohl(fin->fin_saddr)); 41072393Syz155240 s2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)); 41082393Syz155240 CALC_SUMD(s1, s2, sumd); 41092958Sdr146992 41102958Sdr146992 fix_outcksum(&fin->fin_ip->ip_sum, sumd); 41112393Syz155240 } 41122393Syz155240 #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 41132393Syz155240 defined(linux) || defined(BRIDGE_IPF) 41142393Syz155240 else { 41152393Syz155240 /* 41162393Syz155240 * Strictly speaking, this isn't necessary on BSD 41172393Syz155240 * kernels because they do checksum calculation after 41182393Syz155240 * this code has run BUT if ipfilter is being used 41192393Syz155240 * to do NAT as a bridge, that code doesn't exist. 41202393Syz155240 */ 41212393Syz155240 if (nat->nat_dir == NAT_OUTBOUND) 41222958Sdr146992 fix_outcksum(&fin->fin_ip->ip_sum, 41232958Sdr146992 nat->nat_ipsumd); 41242393Syz155240 else 41252958Sdr146992 fix_incksum(&fin->fin_ip->ip_sum, 41262958Sdr146992 nat->nat_ipsumd); 41272393Syz155240 } 41282393Syz155240 #endif 41292393Syz155240 } 41302393Syz155240 41312393Syz155240 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 41322393Syz155240 if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) { 41332393Syz155240 tcp = fin->fin_dp; 41342393Syz155240 41352393Syz155240 tcp->th_sport = nat->nat_outport; 41362393Syz155240 fin->fin_data[0] = ntohs(nat->nat_outport); 41372393Syz155240 } 41382393Syz155240 41392393Syz155240 if ((nat->nat_outport != 0) && (nflags & IPN_ICMPQUERY)) { 41402393Syz155240 icmp = fin->fin_dp; 41412393Syz155240 icmp->icmp_id = nat->nat_outport; 41422393Syz155240 } 41432393Syz155240 41442393Syz155240 csump = nat_proto(fin, nat, nflags); 41452393Syz155240 } 41462393Syz155240 41472393Syz155240 fin->fin_ip->ip_src = nat->nat_outip; 41482393Syz155240 41492393Syz155240 /* 41502393Syz155240 * The above comments do not hold for layer 4 (or higher) checksums... 41512393Syz155240 */ 41522958Sdr146992 if (csump != NULL && !NET_IS_HCK_L4_FULL(net_data_p, fin->fin_m)) { 41532958Sdr146992 if (nflags & IPN_TCPUDP && 41542958Sdr146992 NET_IS_HCK_L4_PART(net_data_p, fin->fin_m)) 41552958Sdr146992 sumd = nat->nat_sumd[1]; 41562958Sdr146992 else 41572958Sdr146992 sumd = nat->nat_sumd[0]; 41582958Sdr146992 41592393Syz155240 if (nat->nat_dir == NAT_OUTBOUND) 41602958Sdr146992 fix_outcksum(csump, sumd); 41612393Syz155240 else 41622958Sdr146992 fix_incksum(csump, sumd); 41632393Syz155240 } 41642393Syz155240 #ifdef IPFILTER_SYNC 41652393Syz155240 ipfsync_update(SMC_NAT, fin, nat->nat_sync); 41662393Syz155240 #endif 41672393Syz155240 /* ------------------------------------------------------------- */ 41682393Syz155240 /* A few quick notes: */ 41692393Syz155240 /* Following are test conditions prior to calling the */ 41702393Syz155240 /* appr_check routine. */ 41712393Syz155240 /* */ 41722393Syz155240 /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 41732393Syz155240 /* with a redirect rule, we attempt to match the packet's */ 41742393Syz155240 /* source port against in_dport, otherwise we'd compare the */ 41752393Syz155240 /* packet's destination. */ 41762393Syz155240 /* ------------------------------------------------------------- */ 41772393Syz155240 if ((np != NULL) && (np->in_apr != NULL)) { 41782393Syz155240 i = appr_check(fin, nat); 41792393Syz155240 if (i == 0) 41802393Syz155240 i = 1; 41812393Syz155240 } else 41822393Syz155240 i = 1; 41837131Sdr146992 ifs->ifs_nat_stats.ns_mapped[1]++; 41842393Syz155240 fin->fin_flx |= FI_NATED; 41852393Syz155240 return i; 41862393Syz155240 } 41872393Syz155240 41882393Syz155240 41892393Syz155240 /* ------------------------------------------------------------------------ */ 41902393Syz155240 /* Function: fr_checknatin */ 41912393Syz155240 /* Returns: int - -1 == packet failed NAT checks so block it, */ 41922393Syz155240 /* 0 == no packet translation occurred, */ 41932393Syz155240 /* 1 == packet was successfully translated. */ 41942393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 41952393Syz155240 /* passp(I) - pointer to filtering result flags */ 41962393Syz155240 /* */ 41972393Syz155240 /* Check to see if an incoming packet should be changed. ICMP packets are */ 41982393Syz155240 /* first checked to see if they match an existing entry (if an error), */ 41992393Syz155240 /* otherwise a search of the current NAT table is made. If neither results */ 42002393Syz155240 /* in a match then a search for a matching NAT rule is made. Create a new */ 42012393Syz155240 /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 42022393Syz155240 /* packet header(s) as required. */ 42032393Syz155240 /* ------------------------------------------------------------------------ */ 42042393Syz155240 int fr_checknatin(fin, passp) 42052393Syz155240 fr_info_t *fin; 42062393Syz155240 u_32_t *passp; 42072393Syz155240 { 42082393Syz155240 u_int nflags, natadd; 42097131Sdr146992 ipnat_t *np, *npnext; 42102393Syz155240 int rval, natfailed; 42112393Syz155240 struct ifnet *ifp; 42122393Syz155240 struct in_addr in; 42132393Syz155240 icmphdr_t *icmp; 42142393Syz155240 tcphdr_t *tcp; 42152393Syz155240 u_short dport; 42162393Syz155240 nat_t *nat; 42172393Syz155240 u_32_t iph; 42183448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 42193448Sdh155122 42207432SJohn.Ojemann@Sun.COM if (ifs->ifs_fr_nat_lock != 0) 42217432SJohn.Ojemann@Sun.COM return 0; 42227432SJohn.Ojemann@Sun.COM if (ifs->ifs_nat_stats.ns_rules == 0 && ifs->ifs_nat_instances == NULL) 42232393Syz155240 return 0; 42242393Syz155240 42252393Syz155240 tcp = NULL; 42262393Syz155240 icmp = NULL; 42272393Syz155240 dport = 0; 42282393Syz155240 natadd = 1; 42292393Syz155240 nflags = 0; 42302393Syz155240 natfailed = 0; 42312393Syz155240 ifp = fin->fin_ifp; 42322393Syz155240 42332393Syz155240 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 42342393Syz155240 switch (fin->fin_p) 42352393Syz155240 { 42362393Syz155240 case IPPROTO_TCP : 42372393Syz155240 nflags = IPN_TCP; 42382393Syz155240 break; 42392393Syz155240 case IPPROTO_UDP : 42402393Syz155240 nflags = IPN_UDP; 42412393Syz155240 break; 42422393Syz155240 case IPPROTO_ICMP : 42432393Syz155240 icmp = fin->fin_dp; 42442393Syz155240 42452393Syz155240 /* 42462393Syz155240 * This is an incoming packet, so the destination is 42472393Syz155240 * the icmp_id and the source port equals 0 42482393Syz155240 */ 42492393Syz155240 if (nat_icmpquerytype4(icmp->icmp_type)) { 42502393Syz155240 nflags = IPN_ICMPQUERY; 42512393Syz155240 dport = icmp->icmp_id; 42522393Syz155240 } break; 42532393Syz155240 default : 42542393Syz155240 break; 42552393Syz155240 } 42562393Syz155240 42572393Syz155240 if ((nflags & IPN_TCPUDP)) { 42582393Syz155240 tcp = fin->fin_dp; 42592393Syz155240 dport = tcp->th_dport; 42602393Syz155240 } 42612393Syz155240 } 42622393Syz155240 42632393Syz155240 in = fin->fin_dst; 42642393Syz155240 42653448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 42662393Syz155240 42672393Syz155240 if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && 42682393Syz155240 (nat = nat_icmperror(fin, &nflags, NAT_INBOUND))) 42692393Syz155240 /*EMPTY*/; 42702393Syz155240 else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin))) 42712393Syz155240 natadd = 0; 42722393Syz155240 else if ((nat = nat_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, 42732393Syz155240 fin->fin_src, in))) { 42742393Syz155240 nflags = nat->nat_flags; 42752393Syz155240 } else { 42762393Syz155240 u_32_t hv, msk, rmsk; 42772393Syz155240 42787433SJohn.Ojemann@Sun.COM /* 42797433SJohn.Ojemann@Sun.COM * There is no current entry in the nat table for this packet. 42807433SJohn.Ojemann@Sun.COM * 42817433SJohn.Ojemann@Sun.COM * If the packet is a fragment, but not the first fragment, 42827433SJohn.Ojemann@Sun.COM * then don't do anything. Otherwise, if there is a matching 42837433SJohn.Ojemann@Sun.COM * nat rule, try to create a new nat entry. 42847433SJohn.Ojemann@Sun.COM */ 42857433SJohn.Ojemann@Sun.COM if ((fin->fin_off != 0) && (fin->fin_flx & FI_TCPUDP)) 42867433SJohn.Ojemann@Sun.COM goto nonatfrag; 42877433SJohn.Ojemann@Sun.COM 42883448Sdh155122 rmsk = ifs->ifs_rdr_masks; 42892393Syz155240 msk = 0xffffffff; 42902393Syz155240 maskloop: 42912393Syz155240 iph = in.s_addr & htonl(msk); 42923448Sdh155122 hv = NAT_HASH_FN(iph, 0, ifs->ifs_ipf_rdrrules_sz); 42937131Sdr146992 for (np = ifs->ifs_rdr_rules[hv]; np; np = npnext) { 42947131Sdr146992 npnext = np->in_rnext; 42952393Syz155240 if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) 42962393Syz155240 continue; 42972393Syz155240 if (np->in_v != fin->fin_v) 42982393Syz155240 continue; 42992393Syz155240 if (np->in_p && (np->in_p != fin->fin_p)) 43002393Syz155240 continue; 43012393Syz155240 if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) 43022393Syz155240 continue; 43032393Syz155240 if (np->in_flags & IPN_FILTER) { 43042393Syz155240 if (!nat_match(fin, np)) 43052393Syz155240 continue; 43062393Syz155240 } else { 43072393Syz155240 if ((in.s_addr & np->in_outmsk) != np->in_outip) 43082393Syz155240 continue; 43092393Syz155240 if (np->in_pmin && 43102393Syz155240 ((ntohs(np->in_pmax) < ntohs(dport)) || 43112393Syz155240 (ntohs(dport) < ntohs(np->in_pmin)))) 43122393Syz155240 continue; 43132393Syz155240 } 43142393Syz155240 43152393Syz155240 if (*np->in_plabel != '\0') { 43162393Syz155240 if (!appr_ok(fin, tcp, np)) { 43172393Syz155240 continue; 43182393Syz155240 } 43192393Syz155240 } 43202393Syz155240 43217131Sdr146992 ATOMIC_INC32(np->in_use); 43227131Sdr146992 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 43237131Sdr146992 WRITE_ENTER(&ifs->ifs_ipf_nat); 43242393Syz155240 nat = nat_new(fin, np, NULL, nflags, NAT_INBOUND); 43252393Syz155240 if (nat != NULL) { 43267131Sdr146992 np->in_use--; 43272393Syz155240 np->in_hits++; 43287131Sdr146992 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); 43292393Syz155240 break; 43307131Sdr146992 } 43317131Sdr146992 natfailed = -1; 43327131Sdr146992 npnext = np->in_rnext; 43337131Sdr146992 fr_ipnatderef(&np, ifs); 43347131Sdr146992 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); 43352393Syz155240 } 43362393Syz155240 43372393Syz155240 if ((np == NULL) && (rmsk != 0)) { 43382393Syz155240 while (rmsk) { 43392393Syz155240 msk <<= 1; 43402393Syz155240 if (rmsk & 0x80000000) 43412393Syz155240 break; 43422393Syz155240 rmsk <<= 1; 43432393Syz155240 } 43442393Syz155240 if (rmsk != 0) { 43452393Syz155240 rmsk <<= 1; 43462393Syz155240 goto maskloop; 43472393Syz155240 } 43482393Syz155240 } 43492393Syz155240 } 43507433SJohn.Ojemann@Sun.COM 43517433SJohn.Ojemann@Sun.COM nonatfrag: 43522393Syz155240 if (nat != NULL) { 43532393Syz155240 rval = fr_natin(fin, nat, natadd, nflags); 43542393Syz155240 if (rval == 1) { 43552393Syz155240 MUTEX_ENTER(&nat->nat_lock); 43567333SJohn.Ojemann@Sun.COM nat_update(fin, nat, nat->nat_ptr); 43577333SJohn.Ojemann@Sun.COM nat->nat_bytes[0] += fin->fin_plen; 43587333SJohn.Ojemann@Sun.COM nat->nat_pkts[0]++; 43592393Syz155240 nat->nat_ref++; 43602393Syz155240 MUTEX_EXIT(&nat->nat_lock); 43612393Syz155240 fin->fin_nat = nat; 43622393Syz155240 fin->fin_state = nat->nat_state; 43632393Syz155240 } 43642393Syz155240 } else 43652393Syz155240 rval = natfailed; 43663448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 43672393Syz155240 43682393Syz155240 if (rval == -1) { 43692393Syz155240 if (passp != NULL) 43702393Syz155240 *passp = FR_BLOCK; 43712393Syz155240 fin->fin_flx |= FI_BADNAT; 43722393Syz155240 } 43732393Syz155240 return rval; 43742393Syz155240 } 43752393Syz155240 43762393Syz155240 43772393Syz155240 /* ------------------------------------------------------------------------ */ 43782393Syz155240 /* Function: fr_natin */ 43792393Syz155240 /* Returns: int - -1 == packet failed NAT checks so block it, */ 43802393Syz155240 /* 1 == packet was successfully translated. */ 43812393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 43822393Syz155240 /* nat(I) - pointer to NAT structure */ 43832393Syz155240 /* natadd(I) - flag indicating if it is safe to add frag cache */ 43842393Syz155240 /* nflags(I) - NAT flags set for this packet */ 43852393Syz155240 /* Locks Held: ipf_nat (READ) */ 43862393Syz155240 /* */ 43872393Syz155240 /* Translate a packet coming "in" on an interface. */ 43882393Syz155240 /* ------------------------------------------------------------------------ */ 43892393Syz155240 int fr_natin(fin, nat, natadd, nflags) 43902393Syz155240 fr_info_t *fin; 43912393Syz155240 nat_t *nat; 43922393Syz155240 int natadd; 43932393Syz155240 u_32_t nflags; 43942393Syz155240 { 43952393Syz155240 icmphdr_t *icmp; 43965274Syx160601 u_short *csump; 43972393Syz155240 tcphdr_t *tcp; 43982393Syz155240 ipnat_t *np; 43992393Syz155240 int i; 44003448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 44012393Syz155240 44027176Syx160601 if (fin->fin_v == 6) { 44037176Syx160601 #ifdef USE_INET6 44047176Syx160601 return fr_nat6in(fin, nat, natadd, nflags); 44057176Syx160601 #else 44067176Syx160601 return NULL; 44077176Syx160601 #endif 44087176Syx160601 } 44097176Syx160601 44102958Sdr146992 #if SOLARIS && defined(_KERNEL) 44117513SDarren.Reed@Sun.COM net_handle_t net_data_p = ifs->ifs_ipf_ipv4; 44122958Sdr146992 #endif 44132958Sdr146992 44142393Syz155240 tcp = NULL; 44152393Syz155240 csump = NULL; 44162393Syz155240 np = nat->nat_ptr; 44172393Syz155240 fin->fin_fr = nat->nat_fr; 44182393Syz155240 44194712Szf203873 if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) 44204712Szf203873 (void) fr_nat_newfrag(fin, 0, nat); 44214712Szf203873 44222393Syz155240 if (np != NULL) { 44232393Syz155240 44242393Syz155240 /* ------------------------------------------------------------- */ 44252393Syz155240 /* A few quick notes: */ 44262393Syz155240 /* Following are test conditions prior to calling the */ 44272393Syz155240 /* appr_check routine. */ 44282393Syz155240 /* */ 44292393Syz155240 /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 44302393Syz155240 /* with a map rule, we attempt to match the packet's */ 44312393Syz155240 /* source port against in_dport, otherwise we'd compare the */ 44322393Syz155240 /* packet's destination. */ 44332393Syz155240 /* ------------------------------------------------------------- */ 44342393Syz155240 if (np->in_apr != NULL) { 44352393Syz155240 i = appr_check(fin, nat); 44362393Syz155240 if (i == -1) { 44372393Syz155240 return -1; 44382393Syz155240 } 44392393Syz155240 } 44402393Syz155240 } 44412393Syz155240 44422393Syz155240 #ifdef IPFILTER_SYNC 44432393Syz155240 ipfsync_update(SMC_NAT, fin, nat->nat_sync); 44442393Syz155240 #endif 44452393Syz155240 44462393Syz155240 fin->fin_ip->ip_dst = nat->nat_inip; 44472393Syz155240 fin->fin_fi.fi_daddr = nat->nat_inip.s_addr; 44482393Syz155240 if (nflags & IPN_TCPUDP) 44492393Syz155240 tcp = fin->fin_dp; 44502393Syz155240 44512393Syz155240 /* 44522393Syz155240 * Fix up checksums, not by recalculating them, but 44532393Syz155240 * simply computing adjustments. 44542393Syz155240 * Why only do this for some platforms on inbound packets ? 44552393Syz155240 * Because for those that it is done, IP processing is yet to happen 44562393Syz155240 * and so the IPv4 header checksum has not yet been evaluated. 44572393Syz155240 * Perhaps it should always be done for the benefit of things like 44582393Syz155240 * fast forwarding (so that it doesn't need to be recomputed) but with 44592393Syz155240 * header checksum offloading, perhaps it is a moot point. 44602393Syz155240 */ 44612393Syz155240 #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 44622393Syz155240 defined(__osf__) || defined(linux) 44632393Syz155240 if (nat->nat_dir == NAT_OUTBOUND) 44642958Sdr146992 fix_incksum(&fin->fin_ip->ip_sum, nat->nat_ipsumd); 44652393Syz155240 else 44662958Sdr146992 fix_outcksum(&fin->fin_ip->ip_sum, nat->nat_ipsumd); 44672393Syz155240 #endif 44682393Syz155240 44692393Syz155240 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 44702393Syz155240 if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) { 44712393Syz155240 tcp->th_dport = nat->nat_inport; 44722393Syz155240 fin->fin_data[1] = ntohs(nat->nat_inport); 44732393Syz155240 } 44742393Syz155240 44752393Syz155240 44762393Syz155240 if ((nat->nat_inport != 0) && (nflags & IPN_ICMPQUERY)) { 44772393Syz155240 icmp = fin->fin_dp; 44782393Syz155240 44792393Syz155240 icmp->icmp_id = nat->nat_inport; 44802393Syz155240 } 44812393Syz155240 44822393Syz155240 csump = nat_proto(fin, nat, nflags); 44832393Syz155240 } 44842393Syz155240 44855274Syx160601 /* 44865274Syx160601 * In case they are being forwarded, inbound packets always need to have 44875274Syx160601 * their checksum adjusted even if hardware checksum validation said OK. 44885274Syx160601 */ 44895274Syx160601 if (csump != NULL) { 44905274Syx160601 if (nat->nat_dir == NAT_OUTBOUND) 44915274Syx160601 fix_incksum(csump, nat->nat_sumd[0]); 44925274Syx160601 else 44935274Syx160601 fix_outcksum(csump, nat->nat_sumd[0]); 44945274Syx160601 } 44955274Syx160601 44962958Sdr146992 #if SOLARIS && defined(_KERNEL) 44972958Sdr146992 if (nflags & IPN_TCPUDP && 44982958Sdr146992 NET_IS_HCK_L4_PART(net_data_p, fin->fin_m)) { 44995274Syx160601 /* 45005274Syx160601 * Need to adjust the partial checksum result stored in 45015274Syx160601 * db_cksum16, which will be used for validation in IP. 45025274Syx160601 * See IP_CKSUM_RECV(). 45035274Syx160601 * Adjustment data should be the inverse of the IP address 45045274Syx160601 * changes, because db_cksum16 is supposed to be the complement 45055274Syx160601 * of the pesudo header. 45065274Syx160601 */ 45075274Syx160601 csump = &fin->fin_m->b_datap->db_cksum16; 45085274Syx160601 if (nat->nat_dir == NAT_OUTBOUND) 45095274Syx160601 fix_outcksum(csump, nat->nat_sumd[1]); 45105274Syx160601 else 45115274Syx160601 fix_incksum(csump, nat->nat_sumd[1]); 45125274Syx160601 } 45132958Sdr146992 #endif 45145274Syx160601 45157131Sdr146992 ifs->ifs_nat_stats.ns_mapped[0]++; 45162393Syz155240 fin->fin_flx |= FI_NATED; 45172393Syz155240 if (np != NULL && np->in_tag.ipt_num[0] != 0) 45182393Syz155240 fin->fin_nattag = &np->in_tag; 45192393Syz155240 return 1; 45202393Syz155240 } 45212393Syz155240 45222393Syz155240 45232393Syz155240 /* ------------------------------------------------------------------------ */ 45242393Syz155240 /* Function: nat_proto */ 45252393Syz155240 /* Returns: u_short* - pointer to transport header checksum to update, */ 45262393Syz155240 /* NULL if the transport protocol is not recognised */ 45272393Syz155240 /* as needing a checksum update. */ 45282393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 45292393Syz155240 /* nat(I) - pointer to NAT structure */ 45302393Syz155240 /* nflags(I) - NAT flags set for this packet */ 45312393Syz155240 /* */ 45322393Syz155240 /* Return the pointer to the checksum field for each protocol so understood.*/ 45332393Syz155240 /* If support for making other changes to a protocol header is required, */ 45342393Syz155240 /* that is not strictly 'address' translation, such as clamping the MSS in */ 45352393Syz155240 /* TCP down to a specific value, then do it from here. */ 45362393Syz155240 /* ------------------------------------------------------------------------ */ 45372393Syz155240 u_short *nat_proto(fin, nat, nflags) 45382393Syz155240 fr_info_t *fin; 45392393Syz155240 nat_t *nat; 45402393Syz155240 u_int nflags; 45412393Syz155240 { 45422393Syz155240 icmphdr_t *icmp; 45437176Syx160601 struct icmp6_hdr *icmp6; 45442393Syz155240 u_short *csump; 45452393Syz155240 tcphdr_t *tcp; 45462393Syz155240 udphdr_t *udp; 45472393Syz155240 45482393Syz155240 csump = NULL; 45492393Syz155240 if (fin->fin_out == 0) { 45502393Syz155240 fin->fin_rev = (nat->nat_dir == NAT_OUTBOUND); 45512393Syz155240 } else { 45522393Syz155240 fin->fin_rev = (nat->nat_dir == NAT_INBOUND); 45532393Syz155240 } 45542393Syz155240 45552393Syz155240 switch (fin->fin_p) 45562393Syz155240 { 45572393Syz155240 case IPPROTO_TCP : 45582393Syz155240 tcp = fin->fin_dp; 45592393Syz155240 45602393Syz155240 csump = &tcp->th_sum; 45612393Syz155240 45622393Syz155240 /* 45632393Syz155240 * Do a MSS CLAMPING on a SYN packet, 45642393Syz155240 * only deal IPv4 for now. 45652393Syz155240 */ 45662393Syz155240 if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0) 45672958Sdr146992 nat_mssclamp(tcp, nat->nat_mssclamp, csump); 45682393Syz155240 45692393Syz155240 break; 45702393Syz155240 45712393Syz155240 case IPPROTO_UDP : 45722393Syz155240 udp = fin->fin_dp; 45732393Syz155240 45742393Syz155240 if (udp->uh_sum) 45752393Syz155240 csump = &udp->uh_sum; 45762393Syz155240 break; 45772393Syz155240 45782393Syz155240 case IPPROTO_ICMP : 45792393Syz155240 icmp = fin->fin_dp; 45802393Syz155240 45812393Syz155240 if ((nflags & IPN_ICMPQUERY) != 0) { 45822393Syz155240 if (icmp->icmp_cksum != 0) 45832393Syz155240 csump = &icmp->icmp_cksum; 45842393Syz155240 } 45852393Syz155240 break; 45867176Syx160601 45877176Syx160601 case IPPROTO_ICMPV6 : 45887176Syx160601 icmp6 = fin->fin_dp; 45897176Syx160601 45907176Syx160601 if ((nflags & IPN_ICMPQUERY) != 0) { 45917176Syx160601 if (icmp6->icmp6_cksum != 0) 45927176Syx160601 csump = &icmp6->icmp6_cksum; 45937176Syx160601 } 45947176Syx160601 break; 45952393Syz155240 } 45962393Syz155240 return csump; 45972393Syz155240 } 45982393Syz155240 45992393Syz155240 46002393Syz155240 /* ------------------------------------------------------------------------ */ 46012393Syz155240 /* Function: fr_natunload */ 46022393Syz155240 /* Returns: Nil */ 4603*8170SJohn.Ojemann@Sun.COM /* Parameters: ifs - ipf stack instance */ 46042393Syz155240 /* */ 46052393Syz155240 /* Free all memory used by NAT structures allocated at runtime. */ 46062393Syz155240 /* ------------------------------------------------------------------------ */ 46073448Sdh155122 void fr_natunload(ifs) 46083448Sdh155122 ipf_stack_t *ifs; 46092393Syz155240 { 46102393Syz155240 ipftq_t *ifq, *ifqnext; 46112393Syz155240 46123448Sdh155122 (void) nat_clearlist(ifs); 4613*8170SJohn.Ojemann@Sun.COM (void) nat_flushtable(FLUSH_TABLE_ALL, ifs); 46142393Syz155240 46152393Syz155240 /* 46162393Syz155240 * Proxy timeout queues are not cleaned here because although they 46172393Syz155240 * exist on the NAT list, appr_unload is called after fr_natunload 46182393Syz155240 * and the proxies actually are responsible for them being created. 46192393Syz155240 * Should the proxy timeouts have their own list? There's no real 46202393Syz155240 * justification as this is the only complication. 46212393Syz155240 */ 46223448Sdh155122 for (ifq = ifs->ifs_nat_utqe; ifq != NULL; ifq = ifqnext) { 46232393Syz155240 ifqnext = ifq->ifq_next; 46242393Syz155240 if (((ifq->ifq_flags & IFQF_PROXY) == 0) && 46252393Syz155240 (fr_deletetimeoutqueue(ifq) == 0)) 46263448Sdh155122 fr_freetimeoutqueue(ifq, ifs); 46272393Syz155240 } 46282393Syz155240 46293448Sdh155122 if (ifs->ifs_nat_table[0] != NULL) { 46303448Sdh155122 KFREES(ifs->ifs_nat_table[0], 46313448Sdh155122 sizeof(nat_t *) * ifs->ifs_ipf_nattable_sz); 46323448Sdh155122 ifs->ifs_nat_table[0] = NULL; 46332393Syz155240 } 46343448Sdh155122 if (ifs->ifs_nat_table[1] != NULL) { 46353448Sdh155122 KFREES(ifs->ifs_nat_table[1], 46363448Sdh155122 sizeof(nat_t *) * ifs->ifs_ipf_nattable_sz); 46373448Sdh155122 ifs->ifs_nat_table[1] = NULL; 46382393Syz155240 } 46393448Sdh155122 if (ifs->ifs_nat_rules != NULL) { 46403448Sdh155122 KFREES(ifs->ifs_nat_rules, 46413448Sdh155122 sizeof(ipnat_t *) * ifs->ifs_ipf_natrules_sz); 46423448Sdh155122 ifs->ifs_nat_rules = NULL; 46432393Syz155240 } 46443448Sdh155122 if (ifs->ifs_rdr_rules != NULL) { 46453448Sdh155122 KFREES(ifs->ifs_rdr_rules, 46463448Sdh155122 sizeof(ipnat_t *) * ifs->ifs_ipf_rdrrules_sz); 46473448Sdh155122 ifs->ifs_rdr_rules = NULL; 46482393Syz155240 } 46493448Sdh155122 if (ifs->ifs_maptable != NULL) { 46503448Sdh155122 KFREES(ifs->ifs_maptable, 46513448Sdh155122 sizeof(hostmap_t *) * ifs->ifs_ipf_hostmap_sz); 46523448Sdh155122 ifs->ifs_maptable = NULL; 46532393Syz155240 } 46543448Sdh155122 if (ifs->ifs_nat_stats.ns_bucketlen[0] != NULL) { 46553448Sdh155122 KFREES(ifs->ifs_nat_stats.ns_bucketlen[0], 46563448Sdh155122 sizeof(u_long *) * ifs->ifs_ipf_nattable_sz); 46573448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[0] = NULL; 46582393Syz155240 } 46593448Sdh155122 if (ifs->ifs_nat_stats.ns_bucketlen[1] != NULL) { 46603448Sdh155122 KFREES(ifs->ifs_nat_stats.ns_bucketlen[1], 46613448Sdh155122 sizeof(u_long *) * ifs->ifs_ipf_nattable_sz); 46623448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[1] = NULL; 46632393Syz155240 } 46642393Syz155240 46653448Sdh155122 if (ifs->ifs_fr_nat_maxbucket_reset == 1) 46663448Sdh155122 ifs->ifs_fr_nat_maxbucket = 0; 46673448Sdh155122 46683448Sdh155122 if (ifs->ifs_fr_nat_init == 1) { 46693448Sdh155122 ifs->ifs_fr_nat_init = 0; 46703448Sdh155122 fr_sttab_destroy(ifs->ifs_nat_tqb); 46713448Sdh155122 46723448Sdh155122 RW_DESTROY(&ifs->ifs_ipf_natfrag); 46733448Sdh155122 RW_DESTROY(&ifs->ifs_ipf_nat); 46743448Sdh155122 46753448Sdh155122 MUTEX_DESTROY(&ifs->ifs_ipf_nat_new); 46763448Sdh155122 MUTEX_DESTROY(&ifs->ifs_ipf_natio); 46773448Sdh155122 46783448Sdh155122 MUTEX_DESTROY(&ifs->ifs_nat_udptq.ifq_lock); 46793448Sdh155122 MUTEX_DESTROY(&ifs->ifs_nat_icmptq.ifq_lock); 46803448Sdh155122 MUTEX_DESTROY(&ifs->ifs_nat_iptq.ifq_lock); 46812393Syz155240 } 46822393Syz155240 } 46832393Syz155240 46842393Syz155240 46852393Syz155240 /* ------------------------------------------------------------------------ */ 46862393Syz155240 /* Function: fr_natexpire */ 46872393Syz155240 /* Returns: Nil */ 4688*8170SJohn.Ojemann@Sun.COM /* Parameters: ifs - ipf stack instance */ 46892393Syz155240 /* */ 46902393Syz155240 /* Check all of the timeout queues for entries at the top which need to be */ 46912393Syz155240 /* expired. */ 46922393Syz155240 /* ------------------------------------------------------------------------ */ 46933448Sdh155122 void fr_natexpire(ifs) 46943448Sdh155122 ipf_stack_t *ifs; 46952393Syz155240 { 46962393Syz155240 ipftq_t *ifq, *ifqnext; 46972393Syz155240 ipftqent_t *tqe, *tqn; 46982393Syz155240 int i; 46992393Syz155240 SPL_INT(s); 47002393Syz155240 47012393Syz155240 SPL_NET(s); 47023448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 47033448Sdh155122 for (ifq = ifs->ifs_nat_tqb, i = 0; ifq != NULL; ifq = ifq->ifq_next) { 47042393Syz155240 for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { 47053448Sdh155122 if (tqe->tqe_die > ifs->ifs_fr_ticks) 47062393Syz155240 break; 47072393Syz155240 tqn = tqe->tqe_next; 4708*8170SJohn.Ojemann@Sun.COM (void) nat_delete(tqe->tqe_parent, NL_EXPIRE, ifs); 47092393Syz155240 } 47102393Syz155240 } 47112393Syz155240 47123448Sdh155122 for (ifq = ifs->ifs_nat_utqe; ifq != NULL; ifq = ifqnext) { 47132393Syz155240 ifqnext = ifq->ifq_next; 47142393Syz155240 47152393Syz155240 for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { 47163448Sdh155122 if (tqe->tqe_die > ifs->ifs_fr_ticks) 47172393Syz155240 break; 47182393Syz155240 tqn = tqe->tqe_next; 4719*8170SJohn.Ojemann@Sun.COM (void) nat_delete(tqe->tqe_parent, NL_EXPIRE, ifs); 47202393Syz155240 } 47212393Syz155240 } 47222393Syz155240 47233448Sdh155122 for (ifq = ifs->ifs_nat_utqe; ifq != NULL; ifq = ifqnext) { 47242393Syz155240 ifqnext = ifq->ifq_next; 47252393Syz155240 47262393Syz155240 if (((ifq->ifq_flags & IFQF_DELETE) != 0) && 47272393Syz155240 (ifq->ifq_ref == 0)) { 47283448Sdh155122 fr_freetimeoutqueue(ifq, ifs); 47292393Syz155240 } 47302393Syz155240 } 47312393Syz155240 47324817San207044 if (ifs->ifs_nat_doflush != 0) { 4733*8170SJohn.Ojemann@Sun.COM (void) nat_flushtable(FLUSH_TABLE_EXTRA, ifs); 47344817San207044 ifs->ifs_nat_doflush = 0; 47354817San207044 } 47364817San207044 47373448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 47382393Syz155240 SPL_X(s); 47392393Syz155240 } 47402393Syz155240 47412393Syz155240 47422393Syz155240 /* ------------------------------------------------------------------------ */ 47432958Sdr146992 /* Function: fr_nataddrsync */ 47442393Syz155240 /* Returns: Nil */ 47452958Sdr146992 /* Parameters: ifp(I) - pointer to network interface */ 47462958Sdr146992 /* addr(I) - pointer to new network address */ 47472393Syz155240 /* */ 47482393Syz155240 /* Walk through all of the currently active NAT sessions, looking for those */ 47492958Sdr146992 /* which need to have their translated address updated (where the interface */ 47502958Sdr146992 /* matches the one passed in) and change it, recalculating the checksum sum */ 47512958Sdr146992 /* difference too. */ 47522393Syz155240 /* ------------------------------------------------------------------------ */ 47537176Syx160601 void fr_nataddrsync(v, ifp, addr, ifs) 47547176Syx160601 int v; 47552393Syz155240 void *ifp; 47567176Syx160601 void *addr; 47573448Sdh155122 ipf_stack_t *ifs; 47582393Syz155240 { 47592393Syz155240 u_32_t sum1, sum2, sumd; 47602393Syz155240 nat_t *nat; 47612958Sdr146992 ipnat_t *np; 47622393Syz155240 SPL_INT(s); 47632393Syz155240 47643448Sdh155122 if (ifs->ifs_fr_running <= 0) 47652393Syz155240 return; 47662393Syz155240 47672393Syz155240 SPL_NET(s); 47683448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 47693448Sdh155122 47703448Sdh155122 if (ifs->ifs_fr_running <= 0) { 47713448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 47722393Syz155240 return; 47732393Syz155240 } 47742393Syz155240 47752958Sdr146992 /* 47762958Sdr146992 * Change IP addresses for NAT sessions for any protocol except TCP 47772958Sdr146992 * since it will break the TCP connection anyway. The only rules 47782958Sdr146992 * which will get changed are those which are "map ... -> 0/32", 47792958Sdr146992 * where the rule specifies the address is taken from the interface. 47802958Sdr146992 */ 47813448Sdh155122 for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) { 47822958Sdr146992 if (addr != NULL) { 47832958Sdr146992 if (((ifp != NULL) && ifp != (nat->nat_ifps[0])) || 47842958Sdr146992 ((nat->nat_flags & IPN_TCP) != 0)) 47852958Sdr146992 continue; 47867176Syx160601 if ((np = nat->nat_ptr) == NULL) 47872393Syz155240 continue; 47887176Syx160601 if (v == 4 && np->in_v == 4) { 47897176Syx160601 if (np->in_nip || np->in_outmsk != 0xffffffff) 47907176Syx160601 continue; 47917176Syx160601 /* 47927176Syx160601 * Change the map-to address to be the same as 47937176Syx160601 * the new one. 47947176Syx160601 */ 47957176Syx160601 sum1 = nat->nat_outip.s_addr; 47967176Syx160601 nat->nat_outip = *(struct in_addr *)addr; 47977176Syx160601 sum2 = nat->nat_outip.s_addr; 47987176Syx160601 } else if (v == 6 && np->in_v == 6) { 47997176Syx160601 if (!IP6_ISZERO(&np->in_next6.in6) || 48007176Syx160601 !IP6_ISONES(&np->in_out[1].in6)) 48017176Syx160601 continue; 48027176Syx160601 /* 48037176Syx160601 * Change the map-to address to be the same as 48047176Syx160601 * the new one. 48057176Syx160601 */ 48067176Syx160601 nat->nat_outip6.in6 = *(struct in6_addr *)addr; 48077176Syx160601 } else 48087176Syx160601 continue; 48092958Sdr146992 48102958Sdr146992 } else if (((ifp == NULL) || (ifp == nat->nat_ifps[0])) && 48117176Syx160601 !(nat->nat_flags & IPN_TCP) && (np = nat->nat_ptr)) { 48127176Syx160601 if (np->in_v == 4 && (v == 4 || v == 0)) { 48137176Syx160601 struct in_addr in; 48147176Syx160601 if (np->in_outmsk != 0xffffffff || np->in_nip) 48157176Syx160601 continue; 48167176Syx160601 /* 48177176Syx160601 * Change the map-to address to be the same as 48187176Syx160601 * the new one. 48197176Syx160601 */ 48207176Syx160601 sum1 = nat->nat_outip.s_addr; 48217176Syx160601 if (fr_ifpaddr(4, FRI_NORMAL, nat->nat_ifps[0], 48227176Syx160601 &in, NULL, ifs) != -1) 48237176Syx160601 nat->nat_outip = in; 48247176Syx160601 sum2 = nat->nat_outip.s_addr; 48257176Syx160601 } else if (np->in_v == 6 && (v == 6 || v == 0)) { 48267176Syx160601 struct in6_addr in6; 48277176Syx160601 if (!IP6_ISZERO(&np->in_next6.in6) || 48287176Syx160601 !IP6_ISONES(&np->in_out[1].in6)) 48297176Syx160601 continue; 48307176Syx160601 /* 48317176Syx160601 * Change the map-to address to be the same as 48327176Syx160601 * the new one. 48337176Syx160601 */ 48347176Syx160601 if (fr_ifpaddr(6, FRI_NORMAL, nat->nat_ifps[0], 48357176Syx160601 (void *)&in6, NULL, ifs) != -1) 48367176Syx160601 nat->nat_outip6.in6 = in6; 48377176Syx160601 } else 48387176Syx160601 continue; 48392958Sdr146992 } else { 48402958Sdr146992 continue; 48412393Syz155240 } 48422958Sdr146992 48432958Sdr146992 if (sum1 == sum2) 48442958Sdr146992 continue; 48452958Sdr146992 /* 48462958Sdr146992 * Readjust the checksum adjustment to take into 48472958Sdr146992 * account the new IP#. 48482958Sdr146992 */ 48492958Sdr146992 CALC_SUMD(sum1, sum2, sumd); 48502958Sdr146992 /* XXX - dont change for TCP when solaris does 48512958Sdr146992 * hardware checksumming. 48522958Sdr146992 */ 48532958Sdr146992 sumd += nat->nat_sumd[0]; 48542958Sdr146992 nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 48552958Sdr146992 nat->nat_sumd[1] = nat->nat_sumd[0]; 48562393Syz155240 } 48572393Syz155240 48583448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 48592958Sdr146992 SPL_X(s); 48602958Sdr146992 } 48612958Sdr146992 48622958Sdr146992 48632958Sdr146992 /* ------------------------------------------------------------------------ */ 48642958Sdr146992 /* Function: fr_natifpsync */ 48652958Sdr146992 /* Returns: Nil */ 48662958Sdr146992 /* Parameters: action(I) - how we are syncing */ 48672958Sdr146992 /* ifp(I) - pointer to network interface */ 48682958Sdr146992 /* name(I) - name of interface to sync to */ 48692958Sdr146992 /* */ 48702958Sdr146992 /* This function is used to resync the mapping of interface names and their */ 48712958Sdr146992 /* respective 'pointers'. For "action == IPFSYNC_RESYNC", resync all */ 48722958Sdr146992 /* interfaces by doing a new lookup of name to 'pointer'. For "action == */ 48732958Sdr146992 /* IPFSYNC_NEWIFP", treat ifp as the new pointer value associated with */ 48742958Sdr146992 /* "name" and for "action == IPFSYNC_OLDIFP", ifp is a pointer for which */ 48752958Sdr146992 /* there is no longer any interface associated with it. */ 48762958Sdr146992 /* ------------------------------------------------------------------------ */ 48777176Syx160601 void fr_natifpsync(action, v, ifp, name, ifs) 48787176Syx160601 int action, v; 48792958Sdr146992 void *ifp; 48802958Sdr146992 char *name; 48813448Sdh155122 ipf_stack_t *ifs; 48822958Sdr146992 { 48832958Sdr146992 #if defined(_KERNEL) && !defined(MENTAT) && defined(USE_SPL) 48842958Sdr146992 int s; 48852958Sdr146992 #endif 48862958Sdr146992 nat_t *nat; 48872958Sdr146992 ipnat_t *n; 48887176Syx160601 int nv; 48892958Sdr146992 48903448Sdh155122 if (ifs->ifs_fr_running <= 0) 48912958Sdr146992 return; 48922958Sdr146992 48932958Sdr146992 SPL_NET(s); 48943448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 48953448Sdh155122 48963448Sdh155122 if (ifs->ifs_fr_running <= 0) { 48973448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 48982958Sdr146992 return; 48992958Sdr146992 } 49002958Sdr146992 49012958Sdr146992 switch (action) 49022958Sdr146992 { 49032958Sdr146992 case IPFSYNC_RESYNC : 49043448Sdh155122 for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) { 49057176Syx160601 nv = (v == 0) ? nat->nat_v : v; 49067176Syx160601 if (nat->nat_v != nv) 49077176Syx160601 continue; 49082958Sdr146992 if ((ifp == nat->nat_ifps[0]) || 49092958Sdr146992 (nat->nat_ifps[0] == (void *)-1)) { 49102958Sdr146992 nat->nat_ifps[0] = 49117176Syx160601 fr_resolvenic(nat->nat_ifnames[0], nv, ifs); 49122958Sdr146992 } 49132958Sdr146992 49142958Sdr146992 if ((ifp == nat->nat_ifps[1]) || 49152958Sdr146992 (nat->nat_ifps[1] == (void *)-1)) { 49162958Sdr146992 nat->nat_ifps[1] = 49177176Syx160601 fr_resolvenic(nat->nat_ifnames[1], nv, ifs); 49182958Sdr146992 } 49192958Sdr146992 } 49202958Sdr146992 49213448Sdh155122 for (n = ifs->ifs_nat_list; (n != NULL); n = n->in_next) { 49227176Syx160601 nv = (v == 0) ? (int)n->in_v : v; 49237176Syx160601 if ((int)n->in_v != nv) 49247176Syx160601 continue; 49252958Sdr146992 if (n->in_ifps[0] == ifp || 49262958Sdr146992 n->in_ifps[0] == (void *)-1) { 49272958Sdr146992 n->in_ifps[0] = 49287176Syx160601 fr_resolvenic(n->in_ifnames[0], nv, ifs); 49292958Sdr146992 } 49302958Sdr146992 if (n->in_ifps[1] == ifp || 49312958Sdr146992 n->in_ifps[1] == (void *)-1) { 49322958Sdr146992 n->in_ifps[1] = 49337176Syx160601 fr_resolvenic(n->in_ifnames[1], nv, ifs); 49342958Sdr146992 } 49352958Sdr146992 } 49362958Sdr146992 break; 49372958Sdr146992 case IPFSYNC_NEWIFP : 49383448Sdh155122 for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) { 49397176Syx160601 if (nat->nat_v != v) 49407176Syx160601 continue; 49412958Sdr146992 if (!strncmp(name, nat->nat_ifnames[0], 49422958Sdr146992 sizeof(nat->nat_ifnames[0]))) 49432958Sdr146992 nat->nat_ifps[0] = ifp; 49442958Sdr146992 if (!strncmp(name, nat->nat_ifnames[1], 49452958Sdr146992 sizeof(nat->nat_ifnames[1]))) 49462958Sdr146992 nat->nat_ifps[1] = ifp; 49472958Sdr146992 } 49483448Sdh155122 for (n = ifs->ifs_nat_list; (n != NULL); n = n->in_next) { 49497176Syx160601 if ((int)n->in_v != v) 49507176Syx160601 continue; 49512958Sdr146992 if (!strncmp(name, n->in_ifnames[0], 49522958Sdr146992 sizeof(n->in_ifnames[0]))) 49532958Sdr146992 n->in_ifps[0] = ifp; 49542958Sdr146992 if (!strncmp(name, n->in_ifnames[1], 49552958Sdr146992 sizeof(n->in_ifnames[1]))) 49562958Sdr146992 n->in_ifps[1] = ifp; 49572958Sdr146992 } 49582958Sdr146992 break; 49592958Sdr146992 case IPFSYNC_OLDIFP : 49603448Sdh155122 for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) { 49617176Syx160601 if (nat->nat_v != v) 49627176Syx160601 continue; 49632958Sdr146992 if (ifp == nat->nat_ifps[0]) 49642958Sdr146992 nat->nat_ifps[0] = (void *)-1; 49652958Sdr146992 if (ifp == nat->nat_ifps[1]) 49662958Sdr146992 nat->nat_ifps[1] = (void *)-1; 49672958Sdr146992 } 49683448Sdh155122 for (n = ifs->ifs_nat_list; (n != NULL); n = n->in_next) { 49697176Syx160601 if ((int)n->in_v != v) 49707176Syx160601 continue; 49712958Sdr146992 if (n->in_ifps[0] == ifp) 49722958Sdr146992 n->in_ifps[0] = (void *)-1; 49732958Sdr146992 if (n->in_ifps[1] == ifp) 49742958Sdr146992 n->in_ifps[1] = (void *)-1; 49752958Sdr146992 } 49762958Sdr146992 break; 49772393Syz155240 } 49783448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 49792393Syz155240 SPL_X(s); 49802393Syz155240 } 49812393Syz155240 49822393Syz155240 49832393Syz155240 /* ------------------------------------------------------------------------ */ 49842393Syz155240 /* Function: nat_icmpquerytype4 */ 49852393Syz155240 /* Returns: int - 1 == success, 0 == failure */ 49862393Syz155240 /* Parameters: icmptype(I) - ICMP type number */ 49872393Syz155240 /* */ 49882393Syz155240 /* Tests to see if the ICMP type number passed is a query/response type or */ 49892393Syz155240 /* not. */ 49902393Syz155240 /* ------------------------------------------------------------------------ */ 49912393Syz155240 static INLINE int nat_icmpquerytype4(icmptype) 49922393Syz155240 int icmptype; 49932393Syz155240 { 49942393Syz155240 49952393Syz155240 /* 49962393Syz155240 * For the ICMP query NAT code, it is essential that both the query 49972393Syz155240 * and the reply match on the NAT rule. Because the NAT structure 49982393Syz155240 * does not keep track of the icmptype, and a single NAT structure 49992393Syz155240 * is used for all icmp types with the same src, dest and id, we 50002393Syz155240 * simply define the replies as queries as well. The funny thing is, 50012393Syz155240 * altough it seems silly to call a reply a query, this is exactly 50022393Syz155240 * as it is defined in the IPv4 specification 50032393Syz155240 */ 50042393Syz155240 50052393Syz155240 switch (icmptype) 50062393Syz155240 { 50072393Syz155240 50082393Syz155240 case ICMP_ECHOREPLY: 50092393Syz155240 case ICMP_ECHO: 50102393Syz155240 /* route aedvertisement/solliciation is currently unsupported: */ 50112393Syz155240 /* it would require rewriting the ICMP data section */ 50122393Syz155240 case ICMP_TSTAMP: 50132393Syz155240 case ICMP_TSTAMPREPLY: 50142393Syz155240 case ICMP_IREQ: 50152393Syz155240 case ICMP_IREQREPLY: 50162393Syz155240 case ICMP_MASKREQ: 50172393Syz155240 case ICMP_MASKREPLY: 50182393Syz155240 return 1; 50192393Syz155240 default: 50202393Syz155240 return 0; 50212393Syz155240 } 50222393Syz155240 } 50232393Syz155240 50242393Syz155240 50252393Syz155240 /* ------------------------------------------------------------------------ */ 50262393Syz155240 /* Function: nat_log */ 50272393Syz155240 /* Returns: Nil */ 50282393Syz155240 /* Parameters: nat(I) - pointer to NAT structure */ 50292393Syz155240 /* type(I) - type of log entry to create */ 50302393Syz155240 /* */ 50312393Syz155240 /* Creates a NAT log entry. */ 50322393Syz155240 /* ------------------------------------------------------------------------ */ 50333448Sdh155122 void nat_log(nat, type, ifs) 50342393Syz155240 struct nat *nat; 50352393Syz155240 u_int type; 50363448Sdh155122 ipf_stack_t *ifs; 50372393Syz155240 { 50382393Syz155240 #ifdef IPFILTER_LOG 50392393Syz155240 # ifndef LARGE_NAT 50402393Syz155240 struct ipnat *np; 50412393Syz155240 int rulen; 50422393Syz155240 # endif 50432393Syz155240 struct natlog natl; 50442393Syz155240 void *items[1]; 50452393Syz155240 size_t sizes[1]; 50462393Syz155240 int types[1]; 50472393Syz155240 50487176Syx160601 natl.nlg_inip = nat->nat_inip6; 50497176Syx160601 natl.nlg_outip = nat->nat_outip6; 50507176Syx160601 natl.nlg_origip = nat->nat_oip6; 50517176Syx160601 natl.nlg_bytes[0] = nat->nat_bytes[0]; 50527176Syx160601 natl.nlg_bytes[1] = nat->nat_bytes[1]; 50537176Syx160601 natl.nlg_pkts[0] = nat->nat_pkts[0]; 50547176Syx160601 natl.nlg_pkts[1] = nat->nat_pkts[1]; 50557176Syx160601 natl.nlg_origport = nat->nat_oport; 50567176Syx160601 natl.nlg_inport = nat->nat_inport; 50577176Syx160601 natl.nlg_outport = nat->nat_outport; 50587176Syx160601 natl.nlg_p = nat->nat_p; 50597176Syx160601 natl.nlg_type = type; 50607176Syx160601 natl.nlg_rule = -1; 50617176Syx160601 natl.nlg_v = nat->nat_v; 50622393Syz155240 # ifndef LARGE_NAT 50632393Syz155240 if (nat->nat_ptr != NULL) { 50643448Sdh155122 for (rulen = 0, np = ifs->ifs_nat_list; np; 50653448Sdh155122 np = np->in_next, rulen++) 50662393Syz155240 if (np == nat->nat_ptr) { 50677176Syx160601 natl.nlg_rule = rulen; 50682393Syz155240 break; 50692393Syz155240 } 50702393Syz155240 } 50712393Syz155240 # endif 50722393Syz155240 items[0] = &natl; 50732393Syz155240 sizes[0] = sizeof(natl); 50742393Syz155240 types[0] = 0; 50752393Syz155240 50763448Sdh155122 (void) ipllog(IPL_LOGNAT, NULL, items, sizes, types, 1, ifs); 50772393Syz155240 #endif 50782393Syz155240 } 50792393Syz155240 50802393Syz155240 50812393Syz155240 #if defined(__OpenBSD__) 50822393Syz155240 /* ------------------------------------------------------------------------ */ 50832393Syz155240 /* Function: nat_ifdetach */ 50842393Syz155240 /* Returns: Nil */ 50852393Syz155240 /* Parameters: ifp(I) - pointer to network interface */ 50862393Syz155240 /* */ 50872393Syz155240 /* Compatibility interface for OpenBSD to trigger the correct updating of */ 50882393Syz155240 /* interface references within IPFilter. */ 50892393Syz155240 /* ------------------------------------------------------------------------ */ 50903448Sdh155122 void nat_ifdetach(ifp, ifs) 50912393Syz155240 void *ifp; 50923448Sdh155122 ipf_stack_t *ifs; 50932393Syz155240 { 50943448Sdh155122 frsync(ifp, ifs); 50952393Syz155240 return; 50962393Syz155240 } 50972393Syz155240 #endif 50982393Syz155240 50992393Syz155240 51002393Syz155240 /* ------------------------------------------------------------------------ */ 51013448Sdh155122 /* Function: fr_ipnatderef */ 51023448Sdh155122 /* Returns: Nil */ 51037131Sdr146992 /* Parameters: inp(I) - pointer to pointer to NAT rule */ 51043448Sdh155122 /* Write Locks: ipf_nat */ 51053448Sdh155122 /* */ 51063448Sdh155122 /* ------------------------------------------------------------------------ */ 51073448Sdh155122 void fr_ipnatderef(inp, ifs) 51083448Sdh155122 ipnat_t **inp; 51093448Sdh155122 ipf_stack_t *ifs; 51103448Sdh155122 { 51113448Sdh155122 ipnat_t *in; 51123448Sdh155122 51133448Sdh155122 in = *inp; 51143448Sdh155122 *inp = NULL; 51153448Sdh155122 in->in_use--; 51163448Sdh155122 if (in->in_use == 0 && (in->in_flags & IPN_DELETE)) { 51173448Sdh155122 if (in->in_apr) 51183448Sdh155122 appr_free(in->in_apr); 51193448Sdh155122 KFREE(in); 51203448Sdh155122 ifs->ifs_nat_stats.ns_rules--; 51213448Sdh155122 #ifdef notdef 51223448Sdh155122 #if SOLARIS 51233448Sdh155122 if (ifs->ifs_nat_stats.ns_rules == 0) 51243448Sdh155122 ifs->ifs_pfil_delayed_copy = 1; 51253448Sdh155122 #endif 51263448Sdh155122 #endif 51273448Sdh155122 } 51283448Sdh155122 } 51293448Sdh155122 51303448Sdh155122 51313448Sdh155122 /* ------------------------------------------------------------------------ */ 51322393Syz155240 /* Function: fr_natderef */ 51332393Syz155240 /* Returns: Nil */ 5134*8170SJohn.Ojemann@Sun.COM /* Parameters: natp - pointer to pointer to NAT table entry */ 5135*8170SJohn.Ojemann@Sun.COM /* ifs - ipf stack instance */ 51362393Syz155240 /* */ 51372393Syz155240 /* Decrement the reference counter for this NAT table entry and free it if */ 51382393Syz155240 /* there are no more things using it. */ 51395055Sdr146992 /* */ 51405055Sdr146992 /* IF nat_ref == 1 when this function is called, then we have an orphan nat */ 51415055Sdr146992 /* structure *because* it only gets called on paths _after_ nat_ref has been*/ 51425055Sdr146992 /* incremented. If nat_ref == 1 then we shouldn't decrement it here */ 51435055Sdr146992 /* because nat_delete() will do that and send nat_ref to -1. */ 51445055Sdr146992 /* */ 51455055Sdr146992 /* Holding the lock on nat_lock is required to serialise nat_delete() being */ 51465055Sdr146992 /* called from a NAT flush ioctl with a deref happening because of a packet.*/ 51472393Syz155240 /* ------------------------------------------------------------------------ */ 51483448Sdh155122 void fr_natderef(natp, ifs) 51492393Syz155240 nat_t **natp; 51503448Sdh155122 ipf_stack_t *ifs; 51512393Syz155240 { 51522393Syz155240 nat_t *nat; 51532393Syz155240 51542393Syz155240 nat = *natp; 51552393Syz155240 *natp = NULL; 51565055Sdr146992 51575055Sdr146992 MUTEX_ENTER(&nat->nat_lock); 51585055Sdr146992 if (nat->nat_ref > 1) { 51595055Sdr146992 nat->nat_ref--; 51605055Sdr146992 MUTEX_EXIT(&nat->nat_lock); 51615055Sdr146992 return; 51625055Sdr146992 } 51635055Sdr146992 MUTEX_EXIT(&nat->nat_lock); 51645055Sdr146992 51653448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 5166*8170SJohn.Ojemann@Sun.COM (void) nat_delete(nat, NL_EXPIRE, ifs); 51673448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 51682393Syz155240 } 51692393Syz155240 51702393Syz155240 51712393Syz155240 /* ------------------------------------------------------------------------ */ 51722393Syz155240 /* Function: fr_natclone */ 51732393Syz155240 /* Returns: ipstate_t* - NULL == cloning failed, */ 5174*8170SJohn.Ojemann@Sun.COM /* else pointer to new NAT structure */ 5175*8170SJohn.Ojemann@Sun.COM /* Parameters: fin(I) - pointer to packet information */ 5176*8170SJohn.Ojemann@Sun.COM /* nat(I) - pointer to master NAT structure */ 51772393Syz155240 /* Write Lock: ipf_nat */ 51782393Syz155240 /* */ 5179*8170SJohn.Ojemann@Sun.COM /* Create a "duplicate" NAT table entry from the master. */ 51802393Syz155240 /* ------------------------------------------------------------------------ */ 51817176Syx160601 nat_t *fr_natclone(fin, nat) 51822393Syz155240 fr_info_t *fin; 51832393Syz155240 nat_t *nat; 51842393Syz155240 { 51852393Syz155240 frentry_t *fr; 51862393Syz155240 nat_t *clone; 51872393Syz155240 ipnat_t *np; 51883448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 51892393Syz155240 5190*8170SJohn.Ojemann@Sun.COM /* 5191*8170SJohn.Ojemann@Sun.COM * Trigger automatic call to nat_flushtable() if the 5192*8170SJohn.Ojemann@Sun.COM * table has reached capcity specified by hi watermark. 5193*8170SJohn.Ojemann@Sun.COM */ 5194*8170SJohn.Ojemann@Sun.COM if (NAT_TAB_WATER_LEVEL(ifs) > ifs->ifs_nat_flush_level_hi) 5195*8170SJohn.Ojemann@Sun.COM ifs->ifs_nat_doflush = 1; 5196*8170SJohn.Ojemann@Sun.COM 5197*8170SJohn.Ojemann@Sun.COM /* 5198*8170SJohn.Ojemann@Sun.COM * If automatic flushing did not do its job, and the table 5199*8170SJohn.Ojemann@Sun.COM * has filled up, don't try to create a new entry. 5200*8170SJohn.Ojemann@Sun.COM */ 5201*8170SJohn.Ojemann@Sun.COM if (ifs->ifs_nat_stats.ns_inuse >= ifs->ifs_ipf_nattable_max) { 5202*8170SJohn.Ojemann@Sun.COM ifs->ifs_nat_stats.ns_memfail++; 5203*8170SJohn.Ojemann@Sun.COM return NULL; 5204*8170SJohn.Ojemann@Sun.COM } 5205*8170SJohn.Ojemann@Sun.COM 52062393Syz155240 KMALLOC(clone, nat_t *); 52072393Syz155240 if (clone == NULL) 52082393Syz155240 return NULL; 52092393Syz155240 bcopy((char *)nat, (char *)clone, sizeof(*clone)); 52102393Syz155240 52112393Syz155240 MUTEX_NUKE(&clone->nat_lock); 52122393Syz155240 52132393Syz155240 clone->nat_aps = NULL; 52142393Syz155240 /* 52152393Syz155240 * Initialize all these so that nat_delete() doesn't cause a crash. 52162393Syz155240 */ 52172393Syz155240 clone->nat_tqe.tqe_pnext = NULL; 52182393Syz155240 clone->nat_tqe.tqe_next = NULL; 52192393Syz155240 clone->nat_tqe.tqe_ifq = NULL; 52202393Syz155240 clone->nat_tqe.tqe_parent = clone; 52212393Syz155240 52222393Syz155240 clone->nat_flags &= ~SI_CLONE; 52232393Syz155240 clone->nat_flags |= SI_CLONED; 52242393Syz155240 52252393Syz155240 if (clone->nat_hm) 52262393Syz155240 clone->nat_hm->hm_ref++; 52272393Syz155240 52283448Sdh155122 if (nat_insert(clone, fin->fin_rev, ifs) == -1) { 52292393Syz155240 KFREE(clone); 52302393Syz155240 return NULL; 52312393Syz155240 } 52322393Syz155240 np = clone->nat_ptr; 52332393Syz155240 if (np != NULL) { 52343448Sdh155122 if (ifs->ifs_nat_logging) 52353448Sdh155122 nat_log(clone, (u_int)np->in_redir, ifs); 52362393Syz155240 np->in_use++; 52372393Syz155240 } 52382393Syz155240 fr = clone->nat_fr; 52392393Syz155240 if (fr != NULL) { 52402393Syz155240 MUTEX_ENTER(&fr->fr_lock); 52412393Syz155240 fr->fr_ref++; 52422393Syz155240 MUTEX_EXIT(&fr->fr_lock); 52432393Syz155240 } 52442393Syz155240 52452393Syz155240 /* 52462393Syz155240 * Because the clone is created outside the normal loop of things and 52472393Syz155240 * TCP has special needs in terms of state, initialise the timeout 52482393Syz155240 * state of the new NAT from here. 52492393Syz155240 */ 52502393Syz155240 if (clone->nat_p == IPPROTO_TCP) { 52513448Sdh155122 (void) fr_tcp_age(&clone->nat_tqe, fin, ifs->ifs_nat_tqb, 52522393Syz155240 clone->nat_flags); 52532393Syz155240 } 52542393Syz155240 #ifdef IPFILTER_SYNC 52552393Syz155240 clone->nat_sync = ipfsync_new(SMC_NAT, fin, clone); 52562393Syz155240 #endif 52573448Sdh155122 if (ifs->ifs_nat_logging) 52583448Sdh155122 nat_log(clone, NL_CLONE, ifs); 52592393Syz155240 return clone; 52602393Syz155240 } 52612393Syz155240 52622393Syz155240 52632393Syz155240 /* ------------------------------------------------------------------------ */ 52642393Syz155240 /* Function: nat_wildok */ 52652393Syz155240 /* Returns: int - 1 == packet's ports match wildcards */ 52662393Syz155240 /* 0 == packet's ports don't match wildcards */ 52672393Syz155240 /* Parameters: nat(I) - NAT entry */ 52682393Syz155240 /* sport(I) - source port */ 52692393Syz155240 /* dport(I) - destination port */ 52702393Syz155240 /* flags(I) - wildcard flags */ 52712393Syz155240 /* dir(I) - packet direction */ 52722393Syz155240 /* */ 52732393Syz155240 /* Use NAT entry and packet direction to determine which combination of */ 52742393Syz155240 /* wildcard flags should be used. */ 52752393Syz155240 /* ------------------------------------------------------------------------ */ 52767176Syx160601 int nat_wildok(nat, sport, dport, flags, dir) 52772393Syz155240 nat_t *nat; 52782393Syz155240 int sport; 52792393Syz155240 int dport; 52802393Syz155240 int flags; 52812393Syz155240 int dir; 52822393Syz155240 { 52832393Syz155240 /* 52842393Syz155240 * When called by dir is set to 52852393Syz155240 * nat_inlookup NAT_INBOUND (0) 52862393Syz155240 * nat_outlookup NAT_OUTBOUND (1) 52872393Syz155240 * 52882393Syz155240 * We simply combine the packet's direction in dir with the original 52892393Syz155240 * "intended" direction of that NAT entry in nat->nat_dir to decide 52902393Syz155240 * which combination of wildcard flags to allow. 52912393Syz155240 */ 52922393Syz155240 52932393Syz155240 switch ((dir << 1) | nat->nat_dir) 52942393Syz155240 { 52952393Syz155240 case 3: /* outbound packet / outbound entry */ 52962393Syz155240 if (((nat->nat_inport == sport) || 52972393Syz155240 (flags & SI_W_SPORT)) && 52982393Syz155240 ((nat->nat_oport == dport) || 52992393Syz155240 (flags & SI_W_DPORT))) 53002393Syz155240 return 1; 53012393Syz155240 break; 53022393Syz155240 case 2: /* outbound packet / inbound entry */ 53032393Syz155240 if (((nat->nat_outport == sport) || 53042393Syz155240 (flags & SI_W_DPORT)) && 53052393Syz155240 ((nat->nat_oport == dport) || 53062393Syz155240 (flags & SI_W_SPORT))) 53072393Syz155240 return 1; 53082393Syz155240 break; 53092393Syz155240 case 1: /* inbound packet / outbound entry */ 53102393Syz155240 if (((nat->nat_oport == sport) || 53112393Syz155240 (flags & SI_W_DPORT)) && 53122393Syz155240 ((nat->nat_outport == dport) || 53132393Syz155240 (flags & SI_W_SPORT))) 53142393Syz155240 return 1; 53152393Syz155240 break; 53162393Syz155240 case 0: /* inbound packet / inbound entry */ 53172393Syz155240 if (((nat->nat_oport == sport) || 53182393Syz155240 (flags & SI_W_SPORT)) && 53192393Syz155240 ((nat->nat_outport == dport) || 53202393Syz155240 (flags & SI_W_DPORT))) 53212393Syz155240 return 1; 53222393Syz155240 break; 53232393Syz155240 default: 53242393Syz155240 break; 53252393Syz155240 } 53262393Syz155240 53272393Syz155240 return(0); 53282393Syz155240 } 53292393Syz155240 53302393Syz155240 53312393Syz155240 /* ------------------------------------------------------------------------ */ 53322393Syz155240 /* Function: nat_mssclamp */ 53332393Syz155240 /* Returns: Nil */ 53342393Syz155240 /* Parameters: tcp(I) - pointer to TCP header */ 53352393Syz155240 /* maxmss(I) - value to clamp the TCP MSS to */ 53362393Syz155240 /* csump(I) - pointer to TCP checksum */ 53372393Syz155240 /* */ 53382393Syz155240 /* Check for MSS option and clamp it if necessary. If found and changed, */ 53392393Syz155240 /* then the TCP header checksum will be updated to reflect the change in */ 53402393Syz155240 /* the MSS. */ 53412393Syz155240 /* ------------------------------------------------------------------------ */ 53422958Sdr146992 static void nat_mssclamp(tcp, maxmss, csump) 53432393Syz155240 tcphdr_t *tcp; 53442393Syz155240 u_32_t maxmss; 53452393Syz155240 u_short *csump; 53462393Syz155240 { 53472393Syz155240 u_char *cp, *ep, opt; 53482393Syz155240 int hlen, advance; 53492393Syz155240 u_32_t mss, sumd; 53502393Syz155240 53512393Syz155240 hlen = TCP_OFF(tcp) << 2; 53522393Syz155240 if (hlen > sizeof(*tcp)) { 53532393Syz155240 cp = (u_char *)tcp + sizeof(*tcp); 53542393Syz155240 ep = (u_char *)tcp + hlen; 53552393Syz155240 53562393Syz155240 while (cp < ep) { 53572393Syz155240 opt = cp[0]; 53582393Syz155240 if (opt == TCPOPT_EOL) 53592393Syz155240 break; 53602393Syz155240 else if (opt == TCPOPT_NOP) { 53612393Syz155240 cp++; 53622393Syz155240 continue; 53632393Syz155240 } 53642393Syz155240 53652393Syz155240 if (cp + 1 >= ep) 53662393Syz155240 break; 53672393Syz155240 advance = cp[1]; 53682393Syz155240 if ((cp + advance > ep) || (advance <= 0)) 53692393Syz155240 break; 53702393Syz155240 switch (opt) 53712393Syz155240 { 53722393Syz155240 case TCPOPT_MAXSEG: 53732393Syz155240 if (advance != 4) 53742393Syz155240 break; 53752393Syz155240 mss = cp[2] * 256 + cp[3]; 53762393Syz155240 if (mss > maxmss) { 53772393Syz155240 cp[2] = maxmss / 256; 53782393Syz155240 cp[3] = maxmss & 0xff; 53792393Syz155240 CALC_SUMD(mss, maxmss, sumd); 53802958Sdr146992 fix_outcksum(csump, sumd); 53812393Syz155240 } 53822393Syz155240 break; 53832393Syz155240 default: 53842393Syz155240 /* ignore unknown options */ 53852393Syz155240 break; 53862393Syz155240 } 53872393Syz155240 53882393Syz155240 cp += advance; 53892393Syz155240 } 53902393Syz155240 } 53912393Syz155240 } 53922393Syz155240 53932393Syz155240 53942393Syz155240 /* ------------------------------------------------------------------------ */ 53952393Syz155240 /* Function: fr_setnatqueue */ 53962393Syz155240 /* Returns: Nil */ 53972393Syz155240 /* Parameters: nat(I)- pointer to NAT structure */ 53982393Syz155240 /* rev(I) - forward(0) or reverse(1) direction */ 53992393Syz155240 /* Locks: ipf_nat (read or write) */ 54002393Syz155240 /* */ 54012393Syz155240 /* Put the NAT entry on its default queue entry, using rev as a helped in */ 54022393Syz155240 /* determining which queue it should be placed on. */ 54032393Syz155240 /* ------------------------------------------------------------------------ */ 54043448Sdh155122 void fr_setnatqueue(nat, rev, ifs) 54052393Syz155240 nat_t *nat; 54062393Syz155240 int rev; 54073448Sdh155122 ipf_stack_t *ifs; 54082393Syz155240 { 54092393Syz155240 ipftq_t *oifq, *nifq; 54102393Syz155240 54112393Syz155240 if (nat->nat_ptr != NULL) 54122393Syz155240 nifq = nat->nat_ptr->in_tqehead[rev]; 54132393Syz155240 else 54142393Syz155240 nifq = NULL; 54152393Syz155240 54162393Syz155240 if (nifq == NULL) { 54172393Syz155240 switch (nat->nat_p) 54182393Syz155240 { 54192393Syz155240 case IPPROTO_UDP : 54203448Sdh155122 nifq = &ifs->ifs_nat_udptq; 54212393Syz155240 break; 54222393Syz155240 case IPPROTO_ICMP : 54233448Sdh155122 nifq = &ifs->ifs_nat_icmptq; 54242393Syz155240 break; 54252393Syz155240 case IPPROTO_TCP : 54263448Sdh155122 nifq = ifs->ifs_nat_tqb + nat->nat_tqe.tqe_state[rev]; 54272393Syz155240 break; 54282393Syz155240 default : 54293448Sdh155122 nifq = &ifs->ifs_nat_iptq; 54302393Syz155240 break; 54312393Syz155240 } 54322393Syz155240 } 54332393Syz155240 54342393Syz155240 oifq = nat->nat_tqe.tqe_ifq; 54352393Syz155240 /* 54362393Syz155240 * If it's currently on a timeout queue, move it from one queue to 54372393Syz155240 * another, else put it on the end of the newly determined queue. 54382393Syz155240 */ 54392393Syz155240 if (oifq != NULL) 54403448Sdh155122 fr_movequeue(&nat->nat_tqe, oifq, nifq, ifs); 54412393Syz155240 else 54423448Sdh155122 fr_queueappend(&nat->nat_tqe, nifq, nat, ifs); 54432393Syz155240 return; 54442393Syz155240 } 54453448Sdh155122 54465417Sjojemann /* ------------------------------------------------------------------------ */ 54473448Sdh155122 /* Function: nat_getnext */ 54483448Sdh155122 /* Returns: int - 0 == ok, else error */ 54493448Sdh155122 /* Parameters: t(I) - pointer to ipftoken structure */ 54503448Sdh155122 /* itp(I) - pointer to ipfgeniter_t structure */ 54515417Sjojemann /* ifs - ipf stack instance */ 54523448Sdh155122 /* */ 54535417Sjojemann /* Fetch the next nat/ipnat/hostmap structure pointer from the linked list */ 54545417Sjojemann /* and copy it out to the storage space pointed to by itp. The next item */ 54553448Sdh155122 /* in the list to look at is put back in the ipftoken struture. */ 54563448Sdh155122 /* ------------------------------------------------------------------------ */ 54573448Sdh155122 static int nat_getnext(t, itp, ifs) 54583448Sdh155122 ipftoken_t *t; 54593448Sdh155122 ipfgeniter_t *itp; 54603448Sdh155122 ipf_stack_t *ifs; 54613448Sdh155122 { 54623448Sdh155122 hostmap_t *hm, *nexthm = NULL, zerohm; 54633448Sdh155122 ipnat_t *ipn, *nextipnat = NULL, zeroipn; 54643448Sdh155122 nat_t *nat, *nextnat = NULL, zeronat; 54655417Sjojemann int error = 0, count; 54665417Sjojemann char *dst; 54675417Sjojemann 54685417Sjojemann if (itp->igi_nitems == 0) 54695417Sjojemann return EINVAL; 54703448Sdh155122 54713448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 54725417Sjojemann 54736518Sjojemann /* 54746518Sjojemann * Get "previous" entry from the token and find the next entry. 54756518Sjojemann */ 54763448Sdh155122 switch (itp->igi_type) 54773448Sdh155122 { 54783448Sdh155122 case IPFGENITER_HOSTMAP : 54793448Sdh155122 hm = t->ipt_data; 54803448Sdh155122 if (hm == NULL) { 54813448Sdh155122 nexthm = ifs->ifs_ipf_hm_maplist; 54823448Sdh155122 } else { 54835417Sjojemann nexthm = hm->hm_next; 54843448Sdh155122 } 54853448Sdh155122 break; 54863448Sdh155122 54873448Sdh155122 case IPFGENITER_IPNAT : 54883448Sdh155122 ipn = t->ipt_data; 54893448Sdh155122 if (ipn == NULL) { 54903448Sdh155122 nextipnat = ifs->ifs_nat_list; 54913448Sdh155122 } else { 54923448Sdh155122 nextipnat = ipn->in_next; 54933448Sdh155122 } 54943448Sdh155122 break; 54953448Sdh155122 54963448Sdh155122 case IPFGENITER_NAT : 54973448Sdh155122 nat = t->ipt_data; 54983448Sdh155122 if (nat == NULL) { 54993448Sdh155122 nextnat = ifs->ifs_nat_instances; 55003448Sdh155122 } else { 55013448Sdh155122 nextnat = nat->nat_next; 55023448Sdh155122 } 55035417Sjojemann break; 55045417Sjojemann default : 55055417Sjojemann RWLOCK_EXIT(&ifs->ifs_ipf_nat); 55065417Sjojemann return EINVAL; 55075417Sjojemann } 55085417Sjojemann 55095417Sjojemann dst = itp->igi_data; 55105417Sjojemann for (count = itp->igi_nitems; count > 0; count--) { 55116518Sjojemann /* 55126518Sjojemann * If we found an entry, add a reference to it and update the token. 55136518Sjojemann * Otherwise, zero out data to be returned and NULL out token. 55146518Sjojemann */ 55155417Sjojemann switch (itp->igi_type) 55165417Sjojemann { 55175417Sjojemann case IPFGENITER_HOSTMAP : 55185417Sjojemann if (nexthm != NULL) { 55195417Sjojemann ATOMIC_INC32(nexthm->hm_ref); 55205417Sjojemann t->ipt_data = nexthm; 55213448Sdh155122 } else { 55225417Sjojemann bzero(&zerohm, sizeof(zerohm)); 55235417Sjojemann nexthm = &zerohm; 55245417Sjojemann t->ipt_data = NULL; 55255417Sjojemann } 55265417Sjojemann break; 55275417Sjojemann case IPFGENITER_IPNAT : 55285417Sjojemann if (nextipnat != NULL) { 55295417Sjojemann ATOMIC_INC32(nextipnat->in_use); 55305417Sjojemann t->ipt_data = nextipnat; 55315417Sjojemann } else { 55325417Sjojemann bzero(&zeroipn, sizeof(zeroipn)); 55335417Sjojemann nextipnat = &zeroipn; 55345417Sjojemann t->ipt_data = NULL; 55355417Sjojemann } 55365417Sjojemann break; 55375417Sjojemann case IPFGENITER_NAT : 55385417Sjojemann if (nextnat != NULL) { 55393448Sdh155122 MUTEX_ENTER(&nextnat->nat_lock); 55403448Sdh155122 nextnat->nat_ref++; 55413448Sdh155122 MUTEX_EXIT(&nextnat->nat_lock); 55425417Sjojemann t->ipt_data = nextnat; 55435417Sjojemann } else { 55445417Sjojemann bzero(&zeronat, sizeof(zeronat)); 55455417Sjojemann nextnat = &zeronat; 55465417Sjojemann t->ipt_data = NULL; 55473448Sdh155122 } 55485417Sjojemann break; 55495417Sjojemann default : 55505417Sjojemann break; 55513448Sdh155122 } 55525417Sjojemann 55535417Sjojemann /* 55546518Sjojemann * Now that we have ref, it's save to give up lock. 55555417Sjojemann */ 55565417Sjojemann RWLOCK_EXIT(&ifs->ifs_ipf_nat); 55575417Sjojemann 55586518Sjojemann /* 55596518Sjojemann * Copy out data and clean up references and token as needed. 55606518Sjojemann */ 55615417Sjojemann switch (itp->igi_type) 55625417Sjojemann { 55635417Sjojemann case IPFGENITER_HOSTMAP : 55645417Sjojemann error = COPYOUT(nexthm, dst, sizeof(*nexthm)); 55656518Sjojemann if (error != 0) 55665417Sjojemann error = EFAULT; 55676518Sjojemann if (t->ipt_data == NULL) { 55686518Sjojemann ipf_freetoken(t, ifs); 55696518Sjojemann break; 55705417Sjojemann } else { 55716518Sjojemann if (hm != NULL) { 55726518Sjojemann WRITE_ENTER(&ifs->ifs_ipf_nat); 55736518Sjojemann fr_hostmapdel(&hm); 55746518Sjojemann RWLOCK_EXIT(&ifs->ifs_ipf_nat); 55756518Sjojemann } 55766518Sjojemann if (nexthm->hm_next == NULL) { 55776518Sjojemann ipf_freetoken(t, ifs); 55786518Sjojemann break; 55796518Sjojemann } 55805417Sjojemann dst += sizeof(*nexthm); 55815417Sjojemann hm = nexthm; 55825417Sjojemann nexthm = nexthm->hm_next; 55835417Sjojemann } 55845417Sjojemann break; 55856518Sjojemann 55865417Sjojemann case IPFGENITER_IPNAT : 55875417Sjojemann error = COPYOUT(nextipnat, dst, sizeof(*nextipnat)); 55886518Sjojemann if (error != 0) 55895417Sjojemann error = EFAULT; 55906518Sjojemann if (t->ipt_data == NULL) { 55916518Sjojemann ipf_freetoken(t, ifs); 55926518Sjojemann break; 55935417Sjojemann } else { 55946518Sjojemann if (ipn != NULL) { 55956518Sjojemann WRITE_ENTER(&ifs->ifs_ipf_nat); 55966518Sjojemann fr_ipnatderef(&ipn, ifs); 55976518Sjojemann RWLOCK_EXIT(&ifs->ifs_ipf_nat); 55986518Sjojemann } 55996518Sjojemann if (nextipnat->in_next == NULL) { 56006518Sjojemann ipf_freetoken(t, ifs); 56016518Sjojemann break; 56026518Sjojemann } 56035417Sjojemann dst += sizeof(*nextipnat); 56045417Sjojemann ipn = nextipnat; 56055417Sjojemann nextipnat = nextipnat->in_next; 56065417Sjojemann } 56075417Sjojemann break; 56086518Sjojemann 56095417Sjojemann case IPFGENITER_NAT : 56105417Sjojemann error = COPYOUT(nextnat, dst, sizeof(*nextnat)); 56116518Sjojemann if (error != 0) 56125417Sjojemann error = EFAULT; 56136518Sjojemann if (t->ipt_data == NULL) { 56146518Sjojemann ipf_freetoken(t, ifs); 56156518Sjojemann break; 56165417Sjojemann } else { 56176518Sjojemann if (nat != NULL) 56186518Sjojemann fr_natderef(&nat, ifs); 56196518Sjojemann if (nextnat->nat_next == NULL) { 56206518Sjojemann ipf_freetoken(t, ifs); 56216518Sjojemann break; 56226518Sjojemann } 56235417Sjojemann dst += sizeof(*nextnat); 56245417Sjojemann nat = nextnat; 56255417Sjojemann nextnat = nextnat->nat_next; 56265417Sjojemann } 56275417Sjojemann break; 56285417Sjojemann default : 56295417Sjojemann break; 56305417Sjojemann } 56315417Sjojemann 56325417Sjojemann if ((count == 1) || (error != 0)) 56335417Sjojemann break; 56345417Sjojemann 56355417Sjojemann READ_ENTER(&ifs->ifs_ipf_nat); 56363448Sdh155122 } 56373448Sdh155122 56383448Sdh155122 return error; 56393448Sdh155122 } 56403448Sdh155122 56413448Sdh155122 56423448Sdh155122 /* ------------------------------------------------------------------------ */ 56433448Sdh155122 /* Function: nat_iterator */ 56443448Sdh155122 /* Returns: int - 0 == ok, else error */ 56453448Sdh155122 /* Parameters: token(I) - pointer to ipftoken structure */ 56463448Sdh155122 /* itp(I) - pointer to ipfgeniter_t structure */ 56473448Sdh155122 /* */ 56483448Sdh155122 /* This function acts as a handler for the SIOCGENITER ioctls that use a */ 56493448Sdh155122 /* generic structure to iterate through a list. There are three different */ 56503448Sdh155122 /* linked lists of NAT related information to go through: NAT rules, active */ 56513448Sdh155122 /* NAT mappings and the NAT fragment cache. */ 56523448Sdh155122 /* ------------------------------------------------------------------------ */ 56533448Sdh155122 static int nat_iterator(token, itp, ifs) 56543448Sdh155122 ipftoken_t *token; 56553448Sdh155122 ipfgeniter_t *itp; 56563448Sdh155122 ipf_stack_t *ifs; 56573448Sdh155122 { 56583448Sdh155122 int error; 56593448Sdh155122 56603448Sdh155122 if (itp->igi_data == NULL) 56613448Sdh155122 return EFAULT; 56623448Sdh155122 56633448Sdh155122 token->ipt_subtype = itp->igi_type; 56643448Sdh155122 56653448Sdh155122 switch (itp->igi_type) 56663448Sdh155122 { 56673448Sdh155122 case IPFGENITER_HOSTMAP : 56683448Sdh155122 case IPFGENITER_IPNAT : 56693448Sdh155122 case IPFGENITER_NAT : 56703448Sdh155122 error = nat_getnext(token, itp, ifs); 56713448Sdh155122 break; 56723448Sdh155122 case IPFGENITER_NATFRAG : 56733448Sdh155122 error = fr_nextfrag(token, itp, &ifs->ifs_ipfr_natlist, 56743448Sdh155122 &ifs->ifs_ipfr_nattail, 56753448Sdh155122 &ifs->ifs_ipf_natfrag, ifs); 56763448Sdh155122 break; 56773448Sdh155122 default : 56783448Sdh155122 error = EINVAL; 56793448Sdh155122 break; 56803448Sdh155122 } 56813448Sdh155122 56823448Sdh155122 return error; 56833448Sdh155122 } 56844817San207044 56854817San207044 5686*8170SJohn.Ojemann@Sun.COM /* ---------------------------------------------------------------------- */ 5687*8170SJohn.Ojemann@Sun.COM /* Function: nat_flushtable */ 5688*8170SJohn.Ojemann@Sun.COM /* Returns: int - 0 == success, -1 == failure */ 5689*8170SJohn.Ojemann@Sun.COM /* Parameters: flush_option - how to flush the active NAT table */ 5690*8170SJohn.Ojemann@Sun.COM /* ifs - ipf stack instance */ 5691*8170SJohn.Ojemann@Sun.COM /* Write Locks: ipf_nat */ 5692*8170SJohn.Ojemann@Sun.COM /* */ 5693*8170SJohn.Ojemann@Sun.COM /* Flush NAT tables. Three actions currently defined: */ 5694*8170SJohn.Ojemann@Sun.COM /* */ 5695*8170SJohn.Ojemann@Sun.COM /* FLUSH_TABLE_ALL : Flush all NAT table entries */ 5696*8170SJohn.Ojemann@Sun.COM /* */ 5697*8170SJohn.Ojemann@Sun.COM /* FLUSH_TABLE_CLOSING : Flush entries with TCP connections which */ 5698*8170SJohn.Ojemann@Sun.COM /* have started to close on both ends using */ 5699*8170SJohn.Ojemann@Sun.COM /* ipf_flushclosing(). */ 5700*8170SJohn.Ojemann@Sun.COM /* */ 5701*8170SJohn.Ojemann@Sun.COM /* FLUSH_TABLE_EXTRA : First, flush entries which are "almost" closed. */ 5702*8170SJohn.Ojemann@Sun.COM /* Then, if needed, flush entries with TCP */ 5703*8170SJohn.Ojemann@Sun.COM /* connections which have been idle for a long */ 5704*8170SJohn.Ojemann@Sun.COM /* time with ipf_extraflush(). */ 5705*8170SJohn.Ojemann@Sun.COM /* ---------------------------------------------------------------------- */ 5706*8170SJohn.Ojemann@Sun.COM static int nat_flushtable(flush_option, ifs) 5707*8170SJohn.Ojemann@Sun.COM int flush_option; 57084817San207044 ipf_stack_t *ifs; 57094817San207044 { 5710*8170SJohn.Ojemann@Sun.COM nat_t *nat, *natn; 5711*8170SJohn.Ojemann@Sun.COM int removed; 5712*8170SJohn.Ojemann@Sun.COM SPL_INT(s); 5713*8170SJohn.Ojemann@Sun.COM 5714*8170SJohn.Ojemann@Sun.COM removed = 0; 5715*8170SJohn.Ojemann@Sun.COM 5716*8170SJohn.Ojemann@Sun.COM SPL_NET(s); 5717*8170SJohn.Ojemann@Sun.COM switch (flush_option) 5718*8170SJohn.Ojemann@Sun.COM { 5719*8170SJohn.Ojemann@Sun.COM case FLUSH_TABLE_ALL: 5720*8170SJohn.Ojemann@Sun.COM natn = ifs->ifs_nat_instances; 5721*8170SJohn.Ojemann@Sun.COM while ((nat = natn) != NULL) { 5722*8170SJohn.Ojemann@Sun.COM natn = nat->nat_next; 5723*8170SJohn.Ojemann@Sun.COM if (nat_delete(nat, NL_FLUSH, ifs) == 0) 5724*8170SJohn.Ojemann@Sun.COM removed++; 57254817San207044 } 5726*8170SJohn.Ojemann@Sun.COM break; 5727*8170SJohn.Ojemann@Sun.COM 5728*8170SJohn.Ojemann@Sun.COM case FLUSH_TABLE_CLOSING: 5729*8170SJohn.Ojemann@Sun.COM removed = ipf_flushclosing(NAT_FLUSH, 5730*8170SJohn.Ojemann@Sun.COM IPF_TCPS_CLOSE_WAIT, 5731*8170SJohn.Ojemann@Sun.COM ifs->ifs_nat_tqb, 5732*8170SJohn.Ojemann@Sun.COM ifs->ifs_nat_utqe, 5733*8170SJohn.Ojemann@Sun.COM ifs); 5734*8170SJohn.Ojemann@Sun.COM break; 5735*8170SJohn.Ojemann@Sun.COM 5736*8170SJohn.Ojemann@Sun.COM case FLUSH_TABLE_EXTRA: 5737*8170SJohn.Ojemann@Sun.COM removed = ipf_flushclosing(NAT_FLUSH, 5738*8170SJohn.Ojemann@Sun.COM IPF_TCPS_FIN_WAIT_2, 5739*8170SJohn.Ojemann@Sun.COM ifs->ifs_nat_tqb, 5740*8170SJohn.Ojemann@Sun.COM ifs->ifs_nat_utqe, 5741*8170SJohn.Ojemann@Sun.COM ifs); 5742*8170SJohn.Ojemann@Sun.COM 5743*8170SJohn.Ojemann@Sun.COM /* 5744*8170SJohn.Ojemann@Sun.COM * Be sure we haven't done this in the last 10 seconds. 5745*8170SJohn.Ojemann@Sun.COM */ 5746*8170SJohn.Ojemann@Sun.COM if (ifs->ifs_fr_ticks - ifs->ifs_nat_last_force_flush < 5747*8170SJohn.Ojemann@Sun.COM IPF_TTLVAL(10)) 5748*8170SJohn.Ojemann@Sun.COM break; 5749*8170SJohn.Ojemann@Sun.COM ifs->ifs_nat_last_force_flush = ifs->ifs_fr_ticks; 5750*8170SJohn.Ojemann@Sun.COM removed += ipf_extraflush(NAT_FLUSH, 5751*8170SJohn.Ojemann@Sun.COM &ifs->ifs_nat_tqb[IPF_TCPS_ESTABLISHED], 5752*8170SJohn.Ojemann@Sun.COM ifs->ifs_nat_utqe, 5753*8170SJohn.Ojemann@Sun.COM ifs); 5754*8170SJohn.Ojemann@Sun.COM break; 5755*8170SJohn.Ojemann@Sun.COM 5756*8170SJohn.Ojemann@Sun.COM default: /* Flush Nothing */ 5757*8170SJohn.Ojemann@Sun.COM break; 5758*8170SJohn.Ojemann@Sun.COM } 5759*8170SJohn.Ojemann@Sun.COM 5760*8170SJohn.Ojemann@Sun.COM SPL_X(s); 5761*8170SJohn.Ojemann@Sun.COM return (removed); 57624817San207044 } 5763