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_flushtable __P((ipf_stack_t *)); 1423448Sdh155122 static int nat_clearlist __P((ipf_stack_t *)); 1433448Sdh155122 static void nat_addnat __P((struct ipnat *, ipf_stack_t *)); 1443448Sdh155122 static void nat_addrdr __P((struct ipnat *, ipf_stack_t *)); 1453448Sdh155122 static int fr_natgetent __P((caddr_t, ipf_stack_t *)); 1463448Sdh155122 static int fr_natgetsz __P((caddr_t, ipf_stack_t *)); 1473448Sdh155122 static int fr_natputent __P((caddr_t, int, ipf_stack_t *)); 1483448Sdh155122 static void nat_tabmove __P((nat_t *, ipf_stack_t *)); 1492393Syz155240 static int nat_match __P((fr_info_t *, ipnat_t *)); 1502393Syz155240 static INLINE int nat_newmap __P((fr_info_t *, nat_t *, natinfo_t *)); 1512393Syz155240 static INLINE int nat_newrdr __P((fr_info_t *, nat_t *, natinfo_t *)); 1522393Syz155240 static hostmap_t *nat_hostmap __P((ipnat_t *, struct in_addr, 1533448Sdh155122 struct in_addr, struct in_addr, u_32_t, 1543448Sdh155122 ipf_stack_t *)); 1552393Syz155240 static INLINE int nat_icmpquerytype4 __P((int)); 1567176Syx160601 static int nat_ruleaddrinit __P((ipnat_t *)); 1577176Syx160601 static int nat_siocaddnat __P((ipnat_t *, ipnat_t **, int, ipf_stack_t *)); 1587176Syx160601 static void nat_siocdelnat __P((ipnat_t *, ipnat_t **, int, ipf_stack_t *)); 1593448Sdh155122 static INLINE int nat_icmperrortype4 __P((int)); 1602393Syz155240 static INLINE int nat_finalise __P((fr_info_t *, nat_t *, natinfo_t *, 1612393Syz155240 tcphdr_t *, nat_t **, int)); 1624380Sjojemann static INLINE int nat_resolverule __P((ipnat_t *, ipf_stack_t *)); 1632958Sdr146992 static void nat_mssclamp __P((tcphdr_t *, u_32_t, u_short *)); 1643448Sdh155122 static int nat_getnext __P((ipftoken_t *, ipfgeniter_t *, ipf_stack_t *)); 1653448Sdh155122 static int nat_iterator __P((ipftoken_t *, ipfgeniter_t *, ipf_stack_t *)); 1664817San207044 static int nat_extraflush __P((int, ipf_stack_t *)); 1674817San207044 static int nat_earlydrop __P((ipftq_t *, int, ipf_stack_t *)); 1684817San207044 static int nat_flushclosing __P((int, ipf_stack_t *)); 1694817San207044 1704817San207044 1714817San207044 /* 1724817San207044 * Below we declare a list of constants used only in the nat_extraflush() 1734817San207044 * routine. We are placing it here, instead of in nat_extraflush() itself, 1744817San207044 * because we want to make it visible to tools such as mdb, nm etc., so the 1754817San207044 * values can easily be altered during debugging. 1764817San207044 */ 1774817San207044 static const int idletime_tab[] = { 1784817San207044 IPF_TTLVAL(30), /* 30 seconds */ 1794817San207044 IPF_TTLVAL(1800), /* 30 minutes */ 1804817San207044 IPF_TTLVAL(43200), /* 12 hours */ 1814817San207044 IPF_TTLVAL(345600), /* 4 days */ 1824817San207044 }; 1832393Syz155240 1846253San207044 #define NAT_HAS_L4_CHANGED(n) \ 1856295San207044 (((n)->nat_flags & (IPN_TCPUDPICMP)) && \ 1866295San207044 (n)->nat_inport != (n)->nat_outport) 1872393Syz155240 1882393Syz155240 /* ------------------------------------------------------------------------ */ 1892393Syz155240 /* Function: fr_natinit */ 1902393Syz155240 /* Returns: int - 0 == success, -1 == failure */ 1912393Syz155240 /* Parameters: Nil */ 1922393Syz155240 /* */ 1932393Syz155240 /* Initialise all of the NAT locks, tables and other structures. */ 1942393Syz155240 /* ------------------------------------------------------------------------ */ 1953448Sdh155122 int fr_natinit(ifs) 1963448Sdh155122 ipf_stack_t *ifs; 1972393Syz155240 { 1982393Syz155240 int i; 1992393Syz155240 2003448Sdh155122 KMALLOCS(ifs->ifs_nat_table[0], nat_t **, 2013448Sdh155122 sizeof(nat_t *) * ifs->ifs_ipf_nattable_sz); 2023448Sdh155122 if (ifs->ifs_nat_table[0] != NULL) 2033448Sdh155122 bzero((char *)ifs->ifs_nat_table[0], 2043448Sdh155122 ifs->ifs_ipf_nattable_sz * sizeof(nat_t *)); 2052393Syz155240 else 2062393Syz155240 return -1; 2072393Syz155240 2083448Sdh155122 KMALLOCS(ifs->ifs_nat_table[1], nat_t **, 2093448Sdh155122 sizeof(nat_t *) * ifs->ifs_ipf_nattable_sz); 2103448Sdh155122 if (ifs->ifs_nat_table[1] != NULL) 2113448Sdh155122 bzero((char *)ifs->ifs_nat_table[1], 2123448Sdh155122 ifs->ifs_ipf_nattable_sz * sizeof(nat_t *)); 2132393Syz155240 else 2142393Syz155240 return -2; 2152393Syz155240 2163448Sdh155122 KMALLOCS(ifs->ifs_nat_rules, ipnat_t **, 2173448Sdh155122 sizeof(ipnat_t *) * ifs->ifs_ipf_natrules_sz); 2183448Sdh155122 if (ifs->ifs_nat_rules != NULL) 2193448Sdh155122 bzero((char *)ifs->ifs_nat_rules, 2203448Sdh155122 ifs->ifs_ipf_natrules_sz * sizeof(ipnat_t *)); 2212393Syz155240 else 2222393Syz155240 return -3; 2232393Syz155240 2243448Sdh155122 KMALLOCS(ifs->ifs_rdr_rules, ipnat_t **, 2253448Sdh155122 sizeof(ipnat_t *) * ifs->ifs_ipf_rdrrules_sz); 2263448Sdh155122 if (ifs->ifs_rdr_rules != NULL) 2273448Sdh155122 bzero((char *)ifs->ifs_rdr_rules, 2283448Sdh155122 ifs->ifs_ipf_rdrrules_sz * sizeof(ipnat_t *)); 2292393Syz155240 else 2302393Syz155240 return -4; 2312393Syz155240 2323448Sdh155122 KMALLOCS(ifs->ifs_maptable, hostmap_t **, 2333448Sdh155122 sizeof(hostmap_t *) * ifs->ifs_ipf_hostmap_sz); 2343448Sdh155122 if (ifs->ifs_maptable != NULL) 2353448Sdh155122 bzero((char *)ifs->ifs_maptable, 2363448Sdh155122 sizeof(hostmap_t *) * ifs->ifs_ipf_hostmap_sz); 2372393Syz155240 else 2382393Syz155240 return -5; 2392393Syz155240 2403448Sdh155122 ifs->ifs_ipf_hm_maplist = NULL; 2413448Sdh155122 2423448Sdh155122 KMALLOCS(ifs->ifs_nat_stats.ns_bucketlen[0], u_long *, 2433448Sdh155122 ifs->ifs_ipf_nattable_sz * sizeof(u_long)); 2443448Sdh155122 if (ifs->ifs_nat_stats.ns_bucketlen[0] == NULL) 2453448Sdh155122 return -1; 2463448Sdh155122 bzero((char *)ifs->ifs_nat_stats.ns_bucketlen[0], 2473448Sdh155122 ifs->ifs_ipf_nattable_sz * sizeof(u_long)); 2483448Sdh155122 2493448Sdh155122 KMALLOCS(ifs->ifs_nat_stats.ns_bucketlen[1], u_long *, 2503448Sdh155122 ifs->ifs_ipf_nattable_sz * sizeof(u_long)); 2513448Sdh155122 if (ifs->ifs_nat_stats.ns_bucketlen[1] == NULL) 2523448Sdh155122 return -1; 2533448Sdh155122 bzero((char *)ifs->ifs_nat_stats.ns_bucketlen[1], 2543448Sdh155122 ifs->ifs_ipf_nattable_sz * sizeof(u_long)); 2553448Sdh155122 2563448Sdh155122 if (ifs->ifs_fr_nat_maxbucket == 0) { 2573448Sdh155122 for (i = ifs->ifs_ipf_nattable_sz; i > 0; i >>= 1) 2583448Sdh155122 ifs->ifs_fr_nat_maxbucket++; 2593448Sdh155122 ifs->ifs_fr_nat_maxbucket *= 2; 2602393Syz155240 } 2612393Syz155240 2623448Sdh155122 fr_sttab_init(ifs->ifs_nat_tqb, ifs); 2632393Syz155240 /* 2642393Syz155240 * Increase this because we may have "keep state" following this too 2652393Syz155240 * and packet storms can occur if this is removed too quickly. 2662393Syz155240 */ 2673448Sdh155122 ifs->ifs_nat_tqb[IPF_TCPS_CLOSED].ifq_ttl = ifs->ifs_fr_tcplastack; 2683448Sdh155122 ifs->ifs_nat_tqb[IPF_TCP_NSTATES - 1].ifq_next = &ifs->ifs_nat_udptq; 2693448Sdh155122 ifs->ifs_nat_udptq.ifq_ttl = ifs->ifs_fr_defnatage; 2703448Sdh155122 ifs->ifs_nat_udptq.ifq_ref = 1; 2713448Sdh155122 ifs->ifs_nat_udptq.ifq_head = NULL; 2723448Sdh155122 ifs->ifs_nat_udptq.ifq_tail = &ifs->ifs_nat_udptq.ifq_head; 2733448Sdh155122 MUTEX_INIT(&ifs->ifs_nat_udptq.ifq_lock, "nat ipftq udp tab"); 2743448Sdh155122 ifs->ifs_nat_udptq.ifq_next = &ifs->ifs_nat_icmptq; 2753448Sdh155122 ifs->ifs_nat_icmptq.ifq_ttl = ifs->ifs_fr_defnaticmpage; 2763448Sdh155122 ifs->ifs_nat_icmptq.ifq_ref = 1; 2773448Sdh155122 ifs->ifs_nat_icmptq.ifq_head = NULL; 2783448Sdh155122 ifs->ifs_nat_icmptq.ifq_tail = &ifs->ifs_nat_icmptq.ifq_head; 2793448Sdh155122 MUTEX_INIT(&ifs->ifs_nat_icmptq.ifq_lock, "nat icmp ipftq tab"); 2803448Sdh155122 ifs->ifs_nat_icmptq.ifq_next = &ifs->ifs_nat_iptq; 2813448Sdh155122 ifs->ifs_nat_iptq.ifq_ttl = ifs->ifs_fr_defnatipage; 2823448Sdh155122 ifs->ifs_nat_iptq.ifq_ref = 1; 2833448Sdh155122 ifs->ifs_nat_iptq.ifq_head = NULL; 2843448Sdh155122 ifs->ifs_nat_iptq.ifq_tail = &ifs->ifs_nat_iptq.ifq_head; 2853448Sdh155122 MUTEX_INIT(&ifs->ifs_nat_iptq.ifq_lock, "nat ip ipftq tab"); 2863448Sdh155122 ifs->ifs_nat_iptq.ifq_next = NULL; 2872393Syz155240 2882393Syz155240 for (i = 0; i < IPF_TCP_NSTATES; i++) { 2893448Sdh155122 if (ifs->ifs_nat_tqb[i].ifq_ttl < ifs->ifs_fr_defnaticmpage) 2903448Sdh155122 ifs->ifs_nat_tqb[i].ifq_ttl = ifs->ifs_fr_defnaticmpage; 2912393Syz155240 #ifdef LARGE_NAT 2923448Sdh155122 else if (ifs->ifs_nat_tqb[i].ifq_ttl > ifs->ifs_fr_defnatage) 2933448Sdh155122 ifs->ifs_nat_tqb[i].ifq_ttl = ifs->ifs_fr_defnatage; 2942393Syz155240 #endif 2952393Syz155240 } 2962393Syz155240 2972393Syz155240 /* 2982393Syz155240 * Increase this because we may have "keep state" following 2992393Syz155240 * this too and packet storms can occur if this is removed 3002393Syz155240 * too quickly. 3012393Syz155240 */ 3023448Sdh155122 ifs->ifs_nat_tqb[IPF_TCPS_CLOSED].ifq_ttl = 3033448Sdh155122 ifs->ifs_nat_tqb[IPF_TCPS_LAST_ACK].ifq_ttl; 3043448Sdh155122 3053448Sdh155122 RWLOCK_INIT(&ifs->ifs_ipf_nat, "ipf IP NAT rwlock"); 3063448Sdh155122 RWLOCK_INIT(&ifs->ifs_ipf_natfrag, "ipf IP NAT-Frag rwlock"); 3073448Sdh155122 MUTEX_INIT(&ifs->ifs_ipf_nat_new, "ipf nat new mutex"); 3083448Sdh155122 MUTEX_INIT(&ifs->ifs_ipf_natio, "ipf nat io mutex"); 3093448Sdh155122 3103448Sdh155122 ifs->ifs_fr_nat_init = 1; 3112393Syz155240 3122393Syz155240 return 0; 3132393Syz155240 } 3142393Syz155240 3152393Syz155240 3162393Syz155240 /* ------------------------------------------------------------------------ */ 3172393Syz155240 /* Function: nat_addrdr */ 3182393Syz155240 /* Returns: Nil */ 3192393Syz155240 /* Parameters: n(I) - pointer to NAT rule to add */ 3202393Syz155240 /* */ 3212393Syz155240 /* Adds a redirect rule to the hash table of redirect rules and the list of */ 3222393Syz155240 /* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ 3232393Syz155240 /* use by redirect rules. */ 3242393Syz155240 /* ------------------------------------------------------------------------ */ 3253448Sdh155122 static void nat_addrdr(n, ifs) 3262393Syz155240 ipnat_t *n; 3273448Sdh155122 ipf_stack_t *ifs; 3282393Syz155240 { 3292393Syz155240 ipnat_t **np; 3302393Syz155240 u_32_t j; 3312393Syz155240 u_int hv; 3322393Syz155240 int k; 3332393Syz155240 3342393Syz155240 k = count4bits(n->in_outmsk); 3352393Syz155240 if ((k >= 0) && (k != 32)) 3363448Sdh155122 ifs->ifs_rdr_masks |= 1 << k; 3372393Syz155240 j = (n->in_outip & n->in_outmsk); 3383448Sdh155122 hv = NAT_HASH_FN(j, 0, ifs->ifs_ipf_rdrrules_sz); 3393448Sdh155122 np = ifs->ifs_rdr_rules + hv; 3402393Syz155240 while (*np != NULL) 3412393Syz155240 np = &(*np)->in_rnext; 3422393Syz155240 n->in_rnext = NULL; 3432393Syz155240 n->in_prnext = np; 3442393Syz155240 n->in_hv = hv; 3452393Syz155240 *np = n; 3462393Syz155240 } 3472393Syz155240 3482393Syz155240 3492393Syz155240 /* ------------------------------------------------------------------------ */ 3502393Syz155240 /* Function: nat_addnat */ 3512393Syz155240 /* Returns: Nil */ 3522393Syz155240 /* Parameters: n(I) - pointer to NAT rule to add */ 3532393Syz155240 /* */ 3542393Syz155240 /* Adds a NAT map rule to the hash table of rules and the list of loaded */ 3552393Syz155240 /* NAT rules. Updates the bitmask indicating which netmasks are in use by */ 3562393Syz155240 /* redirect rules. */ 3572393Syz155240 /* ------------------------------------------------------------------------ */ 3583448Sdh155122 static void nat_addnat(n, ifs) 3592393Syz155240 ipnat_t *n; 3603448Sdh155122 ipf_stack_t *ifs; 3612393Syz155240 { 3622393Syz155240 ipnat_t **np; 3632393Syz155240 u_32_t j; 3642393Syz155240 u_int hv; 3652393Syz155240 int k; 3662393Syz155240 3672393Syz155240 k = count4bits(n->in_inmsk); 3682393Syz155240 if ((k >= 0) && (k != 32)) 3693448Sdh155122 ifs->ifs_nat_masks |= 1 << k; 3702393Syz155240 j = (n->in_inip & n->in_inmsk); 3713448Sdh155122 hv = NAT_HASH_FN(j, 0, ifs->ifs_ipf_natrules_sz); 3723448Sdh155122 np = ifs->ifs_nat_rules + hv; 3732393Syz155240 while (*np != NULL) 3742393Syz155240 np = &(*np)->in_mnext; 3752393Syz155240 n->in_mnext = NULL; 3762393Syz155240 n->in_pmnext = np; 3772393Syz155240 n->in_hv = hv; 3782393Syz155240 *np = n; 3792393Syz155240 } 3802393Syz155240 3812393Syz155240 3822393Syz155240 /* ------------------------------------------------------------------------ */ 3832393Syz155240 /* Function: nat_delrdr */ 3842393Syz155240 /* Returns: Nil */ 3852393Syz155240 /* Parameters: n(I) - pointer to NAT rule to delete */ 3862393Syz155240 /* */ 3872393Syz155240 /* Removes a redirect rule from the hash table of redirect rules. */ 3882393Syz155240 /* ------------------------------------------------------------------------ */ 3897176Syx160601 void nat_delrdr(n) 3902393Syz155240 ipnat_t *n; 3912393Syz155240 { 3922393Syz155240 if (n->in_rnext) 3932393Syz155240 n->in_rnext->in_prnext = n->in_prnext; 3942393Syz155240 *n->in_prnext = n->in_rnext; 3952393Syz155240 } 3962393Syz155240 3972393Syz155240 3982393Syz155240 /* ------------------------------------------------------------------------ */ 3992393Syz155240 /* Function: nat_delnat */ 4002393Syz155240 /* Returns: Nil */ 4012393Syz155240 /* Parameters: n(I) - pointer to NAT rule to delete */ 4022393Syz155240 /* */ 4032393Syz155240 /* Removes a NAT map rule from the hash table of NAT map rules. */ 4042393Syz155240 /* ------------------------------------------------------------------------ */ 4057176Syx160601 void nat_delnat(n) 4062393Syz155240 ipnat_t *n; 4072393Syz155240 { 4082393Syz155240 if (n->in_mnext != NULL) 4092393Syz155240 n->in_mnext->in_pmnext = n->in_pmnext; 4102393Syz155240 *n->in_pmnext = n->in_mnext; 4112393Syz155240 } 4122393Syz155240 4132393Syz155240 4142393Syz155240 /* ------------------------------------------------------------------------ */ 4152393Syz155240 /* Function: nat_hostmap */ 4162393Syz155240 /* Returns: struct hostmap* - NULL if no hostmap could be created, */ 4172393Syz155240 /* else a pointer to the hostmapping to use */ 4182393Syz155240 /* Parameters: np(I) - pointer to NAT rule */ 4192393Syz155240 /* real(I) - real IP address */ 4202393Syz155240 /* map(I) - mapped IP address */ 4212393Syz155240 /* port(I) - destination port number */ 4222393Syz155240 /* Write Locks: ipf_nat */ 4232393Syz155240 /* */ 4242393Syz155240 /* Check if an ip address has already been allocated for a given mapping */ 4252393Syz155240 /* that is not doing port based translation. If is not yet allocated, then */ 4262393Syz155240 /* create a new entry if a non-NULL NAT rule pointer has been supplied. */ 4272393Syz155240 /* ------------------------------------------------------------------------ */ 4283448Sdh155122 static struct hostmap *nat_hostmap(np, src, dst, map, port, ifs) 4292393Syz155240 ipnat_t *np; 4302393Syz155240 struct in_addr src; 4312393Syz155240 struct in_addr dst; 4322393Syz155240 struct in_addr map; 4332393Syz155240 u_32_t port; 4343448Sdh155122 ipf_stack_t *ifs; 4352393Syz155240 { 4362393Syz155240 hostmap_t *hm; 4372393Syz155240 u_int hv; 4382393Syz155240 4392393Syz155240 hv = (src.s_addr ^ dst.s_addr); 4402393Syz155240 hv += src.s_addr; 4412393Syz155240 hv += dst.s_addr; 4422393Syz155240 hv %= HOSTMAP_SIZE; 4433448Sdh155122 for (hm = ifs->ifs_maptable[hv]; hm; hm = hm->hm_next) 4442393Syz155240 if ((hm->hm_srcip.s_addr == src.s_addr) && 4452393Syz155240 (hm->hm_dstip.s_addr == dst.s_addr) && 4462393Syz155240 ((np == NULL) || (np == hm->hm_ipnat)) && 4472393Syz155240 ((port == 0) || (port == hm->hm_port))) { 4482393Syz155240 hm->hm_ref++; 4492393Syz155240 return hm; 4502393Syz155240 } 4512393Syz155240 4522393Syz155240 if (np == NULL) 4532393Syz155240 return NULL; 4542393Syz155240 4552393Syz155240 KMALLOC(hm, hostmap_t *); 4562393Syz155240 if (hm) { 4573448Sdh155122 hm->hm_hnext = ifs->ifs_ipf_hm_maplist; 4583448Sdh155122 hm->hm_phnext = &ifs->ifs_ipf_hm_maplist; 4593448Sdh155122 if (ifs->ifs_ipf_hm_maplist != NULL) 4603448Sdh155122 ifs->ifs_ipf_hm_maplist->hm_phnext = &hm->hm_hnext; 4613448Sdh155122 ifs->ifs_ipf_hm_maplist = hm; 4623448Sdh155122 4633448Sdh155122 hm->hm_next = ifs->ifs_maptable[hv]; 4643448Sdh155122 hm->hm_pnext = ifs->ifs_maptable + hv; 4653448Sdh155122 if (ifs->ifs_maptable[hv] != NULL) 4663448Sdh155122 ifs->ifs_maptable[hv]->hm_pnext = &hm->hm_next; 4673448Sdh155122 ifs->ifs_maptable[hv] = hm; 4682393Syz155240 hm->hm_ipnat = np; 4692393Syz155240 hm->hm_srcip = src; 4702393Syz155240 hm->hm_dstip = dst; 4712393Syz155240 hm->hm_mapip = map; 4722393Syz155240 hm->hm_ref = 1; 4732393Syz155240 hm->hm_port = port; 4747176Syx160601 hm->hm_v = 4; 4752393Syz155240 } 4762393Syz155240 return hm; 4772393Syz155240 } 4782393Syz155240 4792393Syz155240 4802393Syz155240 /* ------------------------------------------------------------------------ */ 4815417Sjojemann /* Function: fr_hostmapdel */ 4822393Syz155240 /* Returns: Nil */ 4835417Sjojemann /* Parameters: hmp(I) - pointer to pointer to hostmap structure */ 4842393Syz155240 /* Write Locks: ipf_nat */ 4852393Syz155240 /* */ 4862393Syz155240 /* Decrement the references to this hostmap structure by one. If this */ 4872393Syz155240 /* reaches zero then remove it and free it. */ 4882393Syz155240 /* ------------------------------------------------------------------------ */ 4895417Sjojemann void fr_hostmapdel(hmp) 4905417Sjojemann struct hostmap **hmp; 4912393Syz155240 { 4925417Sjojemann struct hostmap *hm; 4935417Sjojemann 4945417Sjojemann hm = *hmp; 4955417Sjojemann *hmp = NULL; 4965417Sjojemann 4972393Syz155240 hm->hm_ref--; 4982393Syz155240 if (hm->hm_ref == 0) { 4992393Syz155240 if (hm->hm_next) 5002393Syz155240 hm->hm_next->hm_pnext = hm->hm_pnext; 5012393Syz155240 *hm->hm_pnext = hm->hm_next; 5023448Sdh155122 if (hm->hm_hnext) 5033448Sdh155122 hm->hm_hnext->hm_phnext = hm->hm_phnext; 5043448Sdh155122 *hm->hm_phnext = hm->hm_hnext; 5052393Syz155240 KFREE(hm); 5062393Syz155240 } 5072393Syz155240 } 5082393Syz155240 5092393Syz155240 5102393Syz155240 /* ------------------------------------------------------------------------ */ 5112393Syz155240 /* Function: fix_outcksum */ 5122393Syz155240 /* Returns: Nil */ 5132958Sdr146992 /* Parameters: sp(I) - location of 16bit checksum to update */ 5142393Syz155240 /* n((I) - amount to adjust checksum by */ 5152393Syz155240 /* */ 5162393Syz155240 /* Adjusts the 16bit checksum by "n" for packets going out. */ 5172393Syz155240 /* ------------------------------------------------------------------------ */ 5182958Sdr146992 void fix_outcksum(sp, n) 5192393Syz155240 u_short *sp; 5202393Syz155240 u_32_t n; 5212393Syz155240 { 5222393Syz155240 u_short sumshort; 5232393Syz155240 u_32_t sum1; 5242393Syz155240 5252393Syz155240 if (n == 0) 5262393Syz155240 return; 5272393Syz155240 5282393Syz155240 sum1 = (~ntohs(*sp)) & 0xffff; 5292393Syz155240 sum1 += (n); 5302393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 5312393Syz155240 /* Again */ 5322393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 5332393Syz155240 sumshort = ~(u_short)sum1; 5342393Syz155240 *(sp) = htons(sumshort); 5352393Syz155240 } 5362393Syz155240 5372393Syz155240 5382393Syz155240 /* ------------------------------------------------------------------------ */ 5392393Syz155240 /* Function: fix_incksum */ 5402393Syz155240 /* Returns: Nil */ 5412958Sdr146992 /* Parameters: sp(I) - location of 16bit checksum to update */ 5422393Syz155240 /* n((I) - amount to adjust checksum by */ 5432393Syz155240 /* */ 5442393Syz155240 /* Adjusts the 16bit checksum by "n" for packets going in. */ 5452393Syz155240 /* ------------------------------------------------------------------------ */ 5462958Sdr146992 void fix_incksum(sp, n) 5472393Syz155240 u_short *sp; 5482393Syz155240 u_32_t n; 5492393Syz155240 { 5502393Syz155240 u_short sumshort; 5512393Syz155240 u_32_t sum1; 5522393Syz155240 5532393Syz155240 if (n == 0) 5542393Syz155240 return; 5552393Syz155240 5562393Syz155240 sum1 = (~ntohs(*sp)) & 0xffff; 5572393Syz155240 sum1 += ~(n) & 0xffff; 5582393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 5592393Syz155240 /* Again */ 5602393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 5612393Syz155240 sumshort = ~(u_short)sum1; 5622393Syz155240 *(sp) = htons(sumshort); 5632393Syz155240 } 5642393Syz155240 5652393Syz155240 5662393Syz155240 /* ------------------------------------------------------------------------ */ 5672393Syz155240 /* Function: fix_datacksum */ 5682393Syz155240 /* Returns: Nil */ 5692393Syz155240 /* Parameters: sp(I) - location of 16bit checksum to update */ 5702393Syz155240 /* n((I) - amount to adjust checksum by */ 5712393Syz155240 /* */ 5722393Syz155240 /* Fix_datacksum is used *only* for the adjustments of checksums in the */ 5732393Syz155240 /* data section of an IP packet. */ 5742393Syz155240 /* */ 5752393Syz155240 /* The only situation in which you need to do this is when NAT'ing an */ 5762393Syz155240 /* ICMP error message. Such a message, contains in its body the IP header */ 5772393Syz155240 /* of the original IP packet, that causes the error. */ 5782393Syz155240 /* */ 5792393Syz155240 /* You can't use fix_incksum or fix_outcksum in that case, because for the */ 5802393Syz155240 /* kernel the data section of the ICMP error is just data, and no special */ 5812393Syz155240 /* processing like hardware cksum or ntohs processing have been done by the */ 5822393Syz155240 /* kernel on the data section. */ 5832393Syz155240 /* ------------------------------------------------------------------------ */ 5842393Syz155240 void fix_datacksum(sp, n) 5852393Syz155240 u_short *sp; 5862393Syz155240 u_32_t n; 5872393Syz155240 { 5882393Syz155240 u_short sumshort; 5892393Syz155240 u_32_t sum1; 5902393Syz155240 5912393Syz155240 if (n == 0) 5922393Syz155240 return; 5932393Syz155240 5942393Syz155240 sum1 = (~ntohs(*sp)) & 0xffff; 5952393Syz155240 sum1 += (n); 5962393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 5972393Syz155240 /* Again */ 5982393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 5992393Syz155240 sumshort = ~(u_short)sum1; 6002393Syz155240 *(sp) = htons(sumshort); 6012393Syz155240 } 6022393Syz155240 6032393Syz155240 6042393Syz155240 /* ------------------------------------------------------------------------ */ 6052393Syz155240 /* Function: fr_nat_ioctl */ 6062393Syz155240 /* Returns: int - 0 == success, != 0 == failure */ 6072393Syz155240 /* Parameters: data(I) - pointer to ioctl data */ 6082393Syz155240 /* cmd(I) - ioctl command integer */ 6092393Syz155240 /* mode(I) - file mode bits used with open */ 6102393Syz155240 /* */ 6112393Syz155240 /* Processes an ioctl call made to operate on the IP Filter NAT device. */ 6122393Syz155240 /* ------------------------------------------------------------------------ */ 6133448Sdh155122 int fr_nat_ioctl(data, cmd, mode, uid, ctx, ifs) 6142393Syz155240 ioctlcmd_t cmd; 6152393Syz155240 caddr_t data; 6163448Sdh155122 int mode, uid; 6173448Sdh155122 void *ctx; 6183448Sdh155122 ipf_stack_t *ifs; 6192393Syz155240 { 6202393Syz155240 ipnat_t *nat, *nt, *n = NULL, **np = NULL; 6212393Syz155240 int error = 0, ret, arg, getlock; 6222393Syz155240 ipnat_t natd; 6232393Syz155240 6242393Syz155240 #if (BSD >= 199306) && defined(_KERNEL) 6252393Syz155240 if ((securelevel >= 2) && (mode & FWRITE)) 6262393Syz155240 return EPERM; 6272393Syz155240 #endif 6282393Syz155240 6292393Syz155240 #if defined(__osf__) && defined(_KERNEL) 6302393Syz155240 getlock = 0; 6312393Syz155240 #else 6322393Syz155240 getlock = (mode & NAT_LOCKHELD) ? 0 : 1; 6332393Syz155240 #endif 6342393Syz155240 6352393Syz155240 nat = NULL; /* XXX gcc -Wuninitialized */ 6362393Syz155240 if (cmd == (ioctlcmd_t)SIOCADNAT) { 6372393Syz155240 KMALLOC(nt, ipnat_t *); 6382393Syz155240 } else { 6392393Syz155240 nt = NULL; 6402393Syz155240 } 6412393Syz155240 6422393Syz155240 if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) { 6432393Syz155240 if (mode & NAT_SYSSPACE) { 6442393Syz155240 bcopy(data, (char *)&natd, sizeof(natd)); 6452393Syz155240 error = 0; 6462393Syz155240 } else { 6472393Syz155240 error = fr_inobj(data, &natd, IPFOBJ_IPNAT); 6482393Syz155240 } 6492393Syz155240 6502393Syz155240 } else if (cmd == (ioctlcmd_t)SIOCIPFFL) { /* SIOCFLNAT & SIOCCNATL */ 6512393Syz155240 BCOPYIN(data, &arg, sizeof(arg)); 6522393Syz155240 } 6532393Syz155240 6542393Syz155240 if (error != 0) 6552393Syz155240 goto done; 6562393Syz155240 6572393Syz155240 /* 6582393Syz155240 * For add/delete, look to see if the NAT entry is already present 6592393Syz155240 */ 6602393Syz155240 if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) { 6612393Syz155240 nat = &natd; 6622393Syz155240 if (nat->in_v == 0) /* For backward compat. */ 6632393Syz155240 nat->in_v = 4; 6642393Syz155240 nat->in_flags &= IPN_USERFLAGS; 6652393Syz155240 if ((nat->in_redir & NAT_MAPBLK) == 0) { 6662393Syz155240 if ((nat->in_flags & IPN_SPLIT) == 0) 6672393Syz155240 nat->in_inip &= nat->in_inmsk; 6682393Syz155240 if ((nat->in_flags & IPN_IPRANGE) == 0) 6692393Syz155240 nat->in_outip &= nat->in_outmsk; 6702393Syz155240 } 6713448Sdh155122 MUTEX_ENTER(&ifs->ifs_ipf_natio); 6723448Sdh155122 for (np = &ifs->ifs_nat_list; ((n = *np) != NULL); 6733448Sdh155122 np = &n->in_next) 6745828Sjojemann if (bcmp((char *)&nat->in_flags, (char *)&n->in_flags, 6755828Sjojemann IPN_CMPSIZ) == 0) { 6765828Sjojemann if (nat->in_redir == NAT_REDIRECT && 6775828Sjojemann nat->in_pnext != n->in_pnext) 6785828Sjojemann continue; 6792393Syz155240 break; 6805828Sjojemann } 6812393Syz155240 } 6822393Syz155240 6832393Syz155240 switch (cmd) 6842393Syz155240 { 6853448Sdh155122 case SIOCGENITER : 6863448Sdh155122 { 6873448Sdh155122 ipfgeniter_t iter; 6883448Sdh155122 ipftoken_t *token; 6893448Sdh155122 6903448Sdh155122 error = fr_inobj(data, &iter, IPFOBJ_GENITER); 6913448Sdh155122 if (error != 0) 6923448Sdh155122 break; 6933448Sdh155122 6943448Sdh155122 token = ipf_findtoken(iter.igi_type, uid, ctx, ifs); 6953448Sdh155122 if (token != NULL) 6963448Sdh155122 error = nat_iterator(token, &iter, ifs); 6973448Sdh155122 else 6983448Sdh155122 error = ESRCH; 6993448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_tokens); 7003448Sdh155122 break; 7013448Sdh155122 } 7022393Syz155240 #ifdef IPFILTER_LOG 7032393Syz155240 case SIOCIPFFB : 7042393Syz155240 { 7052393Syz155240 int tmp; 7062393Syz155240 7072393Syz155240 if (!(mode & FWRITE)) 7082393Syz155240 error = EPERM; 7092393Syz155240 else { 7103448Sdh155122 tmp = ipflog_clear(IPL_LOGNAT, ifs); 7112393Syz155240 BCOPYOUT((char *)&tmp, (char *)data, sizeof(tmp)); 7122393Syz155240 } 7132393Syz155240 break; 7142393Syz155240 } 7152393Syz155240 case SIOCSETLG : 7162393Syz155240 if (!(mode & FWRITE)) 7172393Syz155240 error = EPERM; 7182393Syz155240 else { 7193448Sdh155122 BCOPYIN((char *)data, 7203448Sdh155122 (char *)&ifs->ifs_nat_logging, 7213448Sdh155122 sizeof(ifs->ifs_nat_logging)); 7222393Syz155240 } 7232393Syz155240 break; 7242393Syz155240 case SIOCGETLG : 7253448Sdh155122 BCOPYOUT((char *)&ifs->ifs_nat_logging, (char *)data, 7263448Sdh155122 sizeof(ifs->ifs_nat_logging)); 7272393Syz155240 break; 7282393Syz155240 case FIONREAD : 7293448Sdh155122 arg = ifs->ifs_iplused[IPL_LOGNAT]; 7302393Syz155240 BCOPYOUT(&arg, data, sizeof(arg)); 7312393Syz155240 break; 7322393Syz155240 #endif 7332393Syz155240 case SIOCADNAT : 7342393Syz155240 if (!(mode & FWRITE)) { 7352393Syz155240 error = EPERM; 7362393Syz155240 } else if (n != NULL) { 7372393Syz155240 error = EEXIST; 7382393Syz155240 } else if (nt == NULL) { 7392393Syz155240 error = ENOMEM; 7402393Syz155240 } 7412393Syz155240 if (error != 0) { 7423448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_natio); 7432393Syz155240 break; 7442393Syz155240 } 7452393Syz155240 bcopy((char *)nat, (char *)nt, sizeof(*n)); 7463448Sdh155122 error = nat_siocaddnat(nt, np, getlock, ifs); 7473448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_natio); 7482393Syz155240 if (error == 0) 7492393Syz155240 nt = NULL; 7502393Syz155240 break; 7512393Syz155240 case SIOCRMNAT : 7522393Syz155240 if (!(mode & FWRITE)) { 7532393Syz155240 error = EPERM; 7542393Syz155240 n = NULL; 7552393Syz155240 } else if (n == NULL) { 7562393Syz155240 error = ESRCH; 7572393Syz155240 } 7582393Syz155240 7592393Syz155240 if (error != 0) { 7603448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_natio); 7612393Syz155240 break; 7622393Syz155240 } 7633448Sdh155122 nat_siocdelnat(n, np, getlock, ifs); 7643448Sdh155122 7653448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_natio); 7662393Syz155240 n = NULL; 7672393Syz155240 break; 7682393Syz155240 case SIOCGNATS : 7693448Sdh155122 ifs->ifs_nat_stats.ns_table[0] = ifs->ifs_nat_table[0]; 7703448Sdh155122 ifs->ifs_nat_stats.ns_table[1] = ifs->ifs_nat_table[1]; 7713448Sdh155122 ifs->ifs_nat_stats.ns_list = ifs->ifs_nat_list; 7723448Sdh155122 ifs->ifs_nat_stats.ns_maptable = ifs->ifs_maptable; 7733448Sdh155122 ifs->ifs_nat_stats.ns_maplist = ifs->ifs_ipf_hm_maplist; 7743448Sdh155122 ifs->ifs_nat_stats.ns_nattab_max = ifs->ifs_ipf_nattable_max; 7753448Sdh155122 ifs->ifs_nat_stats.ns_nattab_sz = ifs->ifs_ipf_nattable_sz; 7763448Sdh155122 ifs->ifs_nat_stats.ns_rultab_sz = ifs->ifs_ipf_natrules_sz; 7773448Sdh155122 ifs->ifs_nat_stats.ns_rdrtab_sz = ifs->ifs_ipf_rdrrules_sz; 7783448Sdh155122 ifs->ifs_nat_stats.ns_hostmap_sz = ifs->ifs_ipf_hostmap_sz; 7793448Sdh155122 ifs->ifs_nat_stats.ns_instances = ifs->ifs_nat_instances; 7803448Sdh155122 ifs->ifs_nat_stats.ns_apslist = ifs->ifs_ap_sess_list; 7813448Sdh155122 error = fr_outobj(data, &ifs->ifs_nat_stats, IPFOBJ_NATSTAT); 7822393Syz155240 break; 7832393Syz155240 case SIOCGNATL : 7842393Syz155240 { 7852393Syz155240 natlookup_t nl; 7862393Syz155240 7872393Syz155240 if (getlock) { 7883448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 7892393Syz155240 } 7902393Syz155240 error = fr_inobj(data, &nl, IPFOBJ_NATLOOKUP); 7917176Syx160601 if (nl.nl_v != 6) 7927176Syx160601 nl.nl_v = 4; 7932393Syz155240 if (error == 0) { 7947176Syx160601 void *ptr; 7957176Syx160601 7967176Syx160601 switch (nl.nl_v) 7977176Syx160601 { 7987176Syx160601 case 4: 7997176Syx160601 ptr = nat_lookupredir(&nl, ifs); 8007176Syx160601 break; 8017176Syx160601 #ifdef USE_INET6 8027176Syx160601 case 6: 8037176Syx160601 ptr = nat6_lookupredir(&nl, ifs); 8047176Syx160601 break; 8057176Syx160601 #endif 8067176Syx160601 default: 8077176Syx160601 ptr = NULL; 8087176Syx160601 break; 8097176Syx160601 } 8107176Syx160601 8117176Syx160601 if (ptr != NULL) { 8122393Syz155240 error = fr_outobj(data, &nl, IPFOBJ_NATLOOKUP); 8132393Syz155240 } else { 8142393Syz155240 error = ESRCH; 8152393Syz155240 } 8162393Syz155240 } 8172393Syz155240 if (getlock) { 8183448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 8192393Syz155240 } 8202393Syz155240 break; 8212393Syz155240 } 8222393Syz155240 case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */ 8232393Syz155240 if (!(mode & FWRITE)) { 8242393Syz155240 error = EPERM; 8252393Syz155240 break; 8262393Syz155240 } 8272393Syz155240 if (getlock) { 8283448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 8292393Syz155240 } 8302393Syz155240 error = 0; 8312393Syz155240 if (arg == 0) 8323448Sdh155122 ret = nat_flushtable(ifs); 8332393Syz155240 else if (arg == 1) 8343448Sdh155122 ret = nat_clearlist(ifs); 8354817San207044 else if (arg >= 2 && arg <= 4) 8364817San207044 ret = nat_extraflush(arg - 2, ifs); 8372393Syz155240 else 8382393Syz155240 error = EINVAL; 8392393Syz155240 if (getlock) { 8403448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 8412393Syz155240 } 8422393Syz155240 if (error == 0) { 8432393Syz155240 BCOPYOUT(&ret, data, sizeof(ret)); 8442393Syz155240 } 8452393Syz155240 break; 8462393Syz155240 case SIOCPROXY : 8473448Sdh155122 error = appr_ioctl(data, cmd, mode, ifs); 8482393Syz155240 break; 8492393Syz155240 case SIOCSTLCK : 8502393Syz155240 if (!(mode & FWRITE)) { 8512393Syz155240 error = EPERM; 8522393Syz155240 } else { 8533448Sdh155122 fr_lock(data, &ifs->ifs_fr_nat_lock); 8542393Syz155240 } 8552393Syz155240 break; 8562393Syz155240 case SIOCSTPUT : 8572761Sjojemann if ((mode & FWRITE) != 0) { 8583448Sdh155122 error = fr_natputent(data, getlock, ifs); 8592393Syz155240 } else { 8602393Syz155240 error = EACCES; 8612393Syz155240 } 8622393Syz155240 break; 8632393Syz155240 case SIOCSTGSZ : 8643448Sdh155122 if (ifs->ifs_fr_nat_lock) { 8652393Syz155240 if (getlock) { 8663448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 8672393Syz155240 } 8683448Sdh155122 error = fr_natgetsz(data, ifs); 8692393Syz155240 if (getlock) { 8703448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 8712393Syz155240 } 8722393Syz155240 } else 8732393Syz155240 error = EACCES; 8742393Syz155240 break; 8752393Syz155240 case SIOCSTGET : 8763448Sdh155122 if (ifs->ifs_fr_nat_lock) { 8772393Syz155240 if (getlock) { 8783448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 8792393Syz155240 } 8803448Sdh155122 error = fr_natgetent(data, ifs); 8812393Syz155240 if (getlock) { 8823448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 8832393Syz155240 } 8842393Syz155240 } else 8852393Syz155240 error = EACCES; 8862393Syz155240 break; 8873448Sdh155122 case SIOCIPFDELTOK : 8883448Sdh155122 (void) BCOPYIN((caddr_t)data, (caddr_t)&arg, sizeof(arg)); 8893448Sdh155122 error = ipf_deltoken(arg, uid, ctx, ifs); 8903448Sdh155122 break; 8912393Syz155240 default : 8922393Syz155240 error = EINVAL; 8932393Syz155240 break; 8942393Syz155240 } 8952393Syz155240 done: 8962393Syz155240 if (nt) 8972393Syz155240 KFREE(nt); 8982393Syz155240 return error; 8992393Syz155240 } 9002393Syz155240 9012393Syz155240 9022393Syz155240 /* ------------------------------------------------------------------------ */ 9032393Syz155240 /* Function: nat_siocaddnat */ 9042393Syz155240 /* Returns: int - 0 == success, != 0 == failure */ 9052393Syz155240 /* Parameters: n(I) - pointer to new NAT rule */ 9062393Syz155240 /* np(I) - pointer to where to insert new NAT rule */ 9072393Syz155240 /* getlock(I) - flag indicating if lock on ipf_nat is held */ 9082393Syz155240 /* Mutex Locks: ipf_natio */ 9092393Syz155240 /* */ 9102393Syz155240 /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 9112393Syz155240 /* from information passed to the kernel, then add it to the appropriate */ 9122393Syz155240 /* NAT rule table(s). */ 9132393Syz155240 /* ------------------------------------------------------------------------ */ 9143448Sdh155122 static int nat_siocaddnat(n, np, getlock, ifs) 9152393Syz155240 ipnat_t *n, **np; 9162393Syz155240 int getlock; 9173448Sdh155122 ipf_stack_t *ifs; 9182393Syz155240 { 9192393Syz155240 int error = 0, i, j; 9202393Syz155240 9214380Sjojemann if (nat_resolverule(n, ifs) != 0) 9224380Sjojemann return ENOENT; 9232393Syz155240 9242393Syz155240 if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) 9252393Syz155240 return EINVAL; 9262393Syz155240 9272393Syz155240 n->in_use = 0; 9282393Syz155240 if (n->in_redir & NAT_MAPBLK) 9292393Syz155240 n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk); 9302393Syz155240 else if (n->in_flags & IPN_AUTOPORTMAP) 9312393Syz155240 n->in_space = USABLE_PORTS * ~ntohl(n->in_inmsk); 9322393Syz155240 else if (n->in_flags & IPN_IPRANGE) 9332393Syz155240 n->in_space = ntohl(n->in_outmsk) - ntohl(n->in_outip); 9342393Syz155240 else if (n->in_flags & IPN_SPLIT) 9352393Syz155240 n->in_space = 2; 9362393Syz155240 else if (n->in_outmsk != 0) 9372393Syz155240 n->in_space = ~ntohl(n->in_outmsk); 9382393Syz155240 else 9392393Syz155240 n->in_space = 1; 9402393Syz155240 9412393Syz155240 /* 9422393Syz155240 * Calculate the number of valid IP addresses in the output 9432393Syz155240 * mapping range. In all cases, the range is inclusive of 9442393Syz155240 * the start and ending IP addresses. 9452393Syz155240 * If to a CIDR address, lose 2: broadcast + network address 9462393Syz155240 * (so subtract 1) 9472393Syz155240 * If to a range, add one. 9482393Syz155240 * If to a single IP address, set to 1. 9492393Syz155240 */ 9502393Syz155240 if (n->in_space) { 9512393Syz155240 if ((n->in_flags & IPN_IPRANGE) != 0) 9522393Syz155240 n->in_space += 1; 9532393Syz155240 else 9542393Syz155240 n->in_space -= 1; 9552393Syz155240 } else 9562393Syz155240 n->in_space = 1; 9572393Syz155240 9587176Syx160601 #ifdef USE_INET6 9597176Syx160601 if (n->in_v == 6 && (n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0 && 9607176Syx160601 !IP6_ISONES(&n->in_out[1]) && !IP6_ISZERO(&n->in_out[1])) 9617176Syx160601 IP6_ADD(&n->in_out[0], 1, &n->in_next6) 9627176Syx160601 else if (n->in_v == 6 && 9637176Syx160601 (n->in_flags & IPN_SPLIT) && (n->in_redir & NAT_REDIRECT)) 9647176Syx160601 n->in_next6 = n->in_in[0]; 9657176Syx160601 else if (n->in_v == 6) 9667176Syx160601 n->in_next6 = n->in_out[0]; 9677176Syx160601 else 9687176Syx160601 #endif 9692393Syz155240 if ((n->in_outmsk != 0xffffffff) && (n->in_outmsk != 0) && 9702393Syz155240 ((n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0)) 9712393Syz155240 n->in_nip = ntohl(n->in_outip) + 1; 9722393Syz155240 else if ((n->in_flags & IPN_SPLIT) && 9732393Syz155240 (n->in_redir & NAT_REDIRECT)) 9742393Syz155240 n->in_nip = ntohl(n->in_inip); 9752393Syz155240 else 9762393Syz155240 n->in_nip = ntohl(n->in_outip); 9777176Syx160601 9782393Syz155240 if (n->in_redir & NAT_MAP) { 9792393Syz155240 n->in_pnext = ntohs(n->in_pmin); 9802393Syz155240 /* 9812393Syz155240 * Multiply by the number of ports made available. 9822393Syz155240 */ 9832393Syz155240 if (ntohs(n->in_pmax) >= ntohs(n->in_pmin)) { 9842393Syz155240 n->in_space *= (ntohs(n->in_pmax) - 9852393Syz155240 ntohs(n->in_pmin) + 1); 9862393Syz155240 /* 9872393Syz155240 * Because two different sources can map to 9882393Syz155240 * different destinations but use the same 9892393Syz155240 * local IP#/port #. 9902393Syz155240 * If the result is smaller than in_space, then 9912393Syz155240 * we may have wrapped around 32bits. 9922393Syz155240 */ 9932393Syz155240 i = n->in_inmsk; 9942393Syz155240 if ((i != 0) && (i != 0xffffffff)) { 9952393Syz155240 j = n->in_space * (~ntohl(i) + 1); 9962393Syz155240 if (j >= n->in_space) 9972393Syz155240 n->in_space = j; 9982393Syz155240 else 9992393Syz155240 n->in_space = 0xffffffff; 10002393Syz155240 } 10012393Syz155240 } 10022393Syz155240 /* 10032393Syz155240 * If no protocol is specified, multiple by 256 to allow for 10042393Syz155240 * at least one IP:IP mapping per protocol. 10052393Syz155240 */ 10062393Syz155240 if ((n->in_flags & IPN_TCPUDPICMP) == 0) { 10072393Syz155240 j = n->in_space * 256; 10082393Syz155240 if (j >= n->in_space) 10092393Syz155240 n->in_space = j; 10102393Syz155240 else 10112393Syz155240 n->in_space = 0xffffffff; 10122393Syz155240 } 10132393Syz155240 } 10142393Syz155240 10152393Syz155240 /* Otherwise, these fields are preset */ 10162393Syz155240 10172393Syz155240 if (getlock) { 10183448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 10192393Syz155240 } 10202393Syz155240 n->in_next = NULL; 10212393Syz155240 *np = n; 10222393Syz155240 10232393Syz155240 if (n->in_age[0] != 0) 10243448Sdh155122 n->in_tqehead[0] = fr_addtimeoutqueue(&ifs->ifs_nat_utqe, 10253448Sdh155122 n->in_age[0], ifs); 10262393Syz155240 10272393Syz155240 if (n->in_age[1] != 0) 10283448Sdh155122 n->in_tqehead[1] = fr_addtimeoutqueue(&ifs->ifs_nat_utqe, 10293448Sdh155122 n->in_age[1], ifs); 10302393Syz155240 10312393Syz155240 if (n->in_redir & NAT_REDIRECT) { 10322393Syz155240 n->in_flags &= ~IPN_NOTDST; 10337176Syx160601 switch (n->in_v) 10347176Syx160601 { 10357176Syx160601 case 4 : 10367176Syx160601 nat_addrdr(n, ifs); 10377176Syx160601 break; 10387176Syx160601 #ifdef USE_INET6 10397176Syx160601 case 6 : 10407176Syx160601 nat6_addrdr(n, ifs); 10417176Syx160601 break; 10427176Syx160601 #endif 10437176Syx160601 default : 10447176Syx160601 break; 10457176Syx160601 } 10462393Syz155240 } 10472393Syz155240 if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { 10482393Syz155240 n->in_flags &= ~IPN_NOTSRC; 10497176Syx160601 switch (n->in_v) 10507176Syx160601 { 10517176Syx160601 case 4 : 10527176Syx160601 nat_addnat(n, ifs); 10537176Syx160601 break; 10547176Syx160601 #ifdef USE_INET6 10557176Syx160601 case 6 : 10567176Syx160601 nat6_addnat(n, ifs); 10577176Syx160601 break; 10587176Syx160601 #endif 10597176Syx160601 default : 10607176Syx160601 break; 10617176Syx160601 } 10622393Syz155240 } 10632393Syz155240 n = NULL; 10643448Sdh155122 ifs->ifs_nat_stats.ns_rules++; 10652393Syz155240 if (getlock) { 10663448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); /* WRITE */ 10672393Syz155240 } 10682393Syz155240 10692393Syz155240 return error; 10702393Syz155240 } 10712393Syz155240 10722393Syz155240 10732393Syz155240 /* ------------------------------------------------------------------------ */ 10742393Syz155240 /* Function: nat_resolvrule */ 10754380Sjojemann /* Returns: int - 0 == success, -1 == failure */ 10762393Syz155240 /* Parameters: n(I) - pointer to NAT rule */ 10772393Syz155240 /* */ 10784380Sjojemann /* Resolve some of the details inside the NAT rule. Includes resolving */ 10794380Sjojemann /* any specified interfaces and proxy labels, and determines whether or not */ 10804380Sjojemann /* all proxy labels are correctly specified. */ 10814380Sjojemann /* */ 10824380Sjojemann /* Called by nat_siocaddnat() (SIOCADNAT) and fr_natputent (SIOCSTPUT). */ 10832393Syz155240 /* ------------------------------------------------------------------------ */ 10844380Sjojemann static int nat_resolverule(n, ifs) 10852393Syz155240 ipnat_t *n; 10863448Sdh155122 ipf_stack_t *ifs; 10872393Syz155240 { 10882393Syz155240 n->in_ifnames[0][LIFNAMSIZ - 1] = '\0'; 10897176Syx160601 n->in_ifps[0] = fr_resolvenic(n->in_ifnames[0], n->in_v, ifs); 10902393Syz155240 10912393Syz155240 n->in_ifnames[1][LIFNAMSIZ - 1] = '\0'; 10922393Syz155240 if (n->in_ifnames[1][0] == '\0') { 10932393Syz155240 (void) strncpy(n->in_ifnames[1], n->in_ifnames[0], LIFNAMSIZ); 10942393Syz155240 n->in_ifps[1] = n->in_ifps[0]; 10952393Syz155240 } else { 10967176Syx160601 n->in_ifps[1] = fr_resolvenic(n->in_ifnames[1], n->in_v, ifs); 10972393Syz155240 } 10982393Syz155240 10992393Syz155240 if (n->in_plabel[0] != '\0') { 11003448Sdh155122 n->in_apr = appr_lookup(n->in_p, n->in_plabel, ifs); 11014380Sjojemann if (n->in_apr == NULL) 11024380Sjojemann return -1; 11032393Syz155240 } 11044380Sjojemann return 0; 11052393Syz155240 } 11062393Syz155240 11072393Syz155240 11082393Syz155240 /* ------------------------------------------------------------------------ */ 11092393Syz155240 /* Function: nat_siocdelnat */ 11102393Syz155240 /* Returns: int - 0 == success, != 0 == failure */ 11112393Syz155240 /* Parameters: n(I) - pointer to new NAT rule */ 11122393Syz155240 /* np(I) - pointer to where to insert new NAT rule */ 11132393Syz155240 /* getlock(I) - flag indicating if lock on ipf_nat is held */ 11142393Syz155240 /* Mutex Locks: ipf_natio */ 11152393Syz155240 /* */ 11162393Syz155240 /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 11172393Syz155240 /* from information passed to the kernel, then add it to the appropriate */ 11182393Syz155240 /* NAT rule table(s). */ 11192393Syz155240 /* ------------------------------------------------------------------------ */ 11203448Sdh155122 static void nat_siocdelnat(n, np, getlock, ifs) 11212393Syz155240 ipnat_t *n, **np; 11222393Syz155240 int getlock; 11233448Sdh155122 ipf_stack_t *ifs; 11242393Syz155240 { 11257176Syx160601 int i; 11267176Syx160601 11272393Syz155240 if (getlock) { 11283448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 11292393Syz155240 } 11302393Syz155240 if (n->in_redir & NAT_REDIRECT) 11312393Syz155240 nat_delrdr(n); 11322393Syz155240 if (n->in_redir & (NAT_MAPBLK|NAT_MAP)) 11332393Syz155240 nat_delnat(n); 11343448Sdh155122 if (ifs->ifs_nat_list == NULL) { 11353448Sdh155122 ifs->ifs_nat_masks = 0; 11363448Sdh155122 ifs->ifs_rdr_masks = 0; 11377176Syx160601 for (i = 0; i < 4; i++) { 11387176Syx160601 ifs->ifs_nat6_masks[i] = 0; 11397176Syx160601 ifs->ifs_rdr6_masks[i] = 0; 11407176Syx160601 } 11412393Syz155240 } 11422393Syz155240 11432393Syz155240 if (n->in_tqehead[0] != NULL) { 11442393Syz155240 if (fr_deletetimeoutqueue(n->in_tqehead[0]) == 0) { 11454817San207044 fr_freetimeoutqueue(n->in_tqehead[0], ifs); 11462393Syz155240 } 11472393Syz155240 } 11482393Syz155240 11492393Syz155240 if (n->in_tqehead[1] != NULL) { 11502393Syz155240 if (fr_deletetimeoutqueue(n->in_tqehead[1]) == 0) { 11513448Sdh155122 fr_freetimeoutqueue(n->in_tqehead[1], ifs); 11522393Syz155240 } 11532393Syz155240 } 11542393Syz155240 11552393Syz155240 *np = n->in_next; 11562393Syz155240 11572393Syz155240 if (n->in_use == 0) { 11582393Syz155240 if (n->in_apr) 11592393Syz155240 appr_free(n->in_apr); 11602393Syz155240 KFREE(n); 11613448Sdh155122 ifs->ifs_nat_stats.ns_rules--; 11622393Syz155240 } else { 11632393Syz155240 n->in_flags |= IPN_DELETE; 11642393Syz155240 n->in_next = NULL; 11652393Syz155240 } 11662393Syz155240 if (getlock) { 11673448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); /* READ/WRITE */ 11682393Syz155240 } 11692393Syz155240 } 11702393Syz155240 11712393Syz155240 11722393Syz155240 /* ------------------------------------------------------------------------ */ 11732393Syz155240 /* Function: fr_natgetsz */ 11742393Syz155240 /* Returns: int - 0 == success, != 0 is the error value. */ 11752393Syz155240 /* Parameters: data(I) - pointer to natget structure with kernel pointer */ 11762393Syz155240 /* get the size of. */ 11772393Syz155240 /* */ 11782393Syz155240 /* Handle SIOCSTGSZ. */ 11792393Syz155240 /* Return the size of the nat list entry to be copied back to user space. */ 11802393Syz155240 /* The size of the entry is stored in the ng_sz field and the enture natget */ 11812393Syz155240 /* structure is copied back to the user. */ 11822393Syz155240 /* ------------------------------------------------------------------------ */ 11833448Sdh155122 static int fr_natgetsz(data, ifs) 11842393Syz155240 caddr_t data; 11853448Sdh155122 ipf_stack_t *ifs; 11862393Syz155240 { 11872393Syz155240 ap_session_t *aps; 11882393Syz155240 nat_t *nat, *n; 11892393Syz155240 natget_t ng; 11902393Syz155240 11912393Syz155240 BCOPYIN(data, &ng, sizeof(ng)); 11922393Syz155240 11932393Syz155240 nat = ng.ng_ptr; 11942393Syz155240 if (!nat) { 11953448Sdh155122 nat = ifs->ifs_nat_instances; 11962393Syz155240 ng.ng_sz = 0; 11972393Syz155240 /* 11982393Syz155240 * Empty list so the size returned is 0. Simple. 11992393Syz155240 */ 12002393Syz155240 if (nat == NULL) { 12012393Syz155240 BCOPYOUT(&ng, data, sizeof(ng)); 12022393Syz155240 return 0; 12032393Syz155240 } 12042393Syz155240 } else { 12052393Syz155240 /* 12062393Syz155240 * Make sure the pointer we're copying from exists in the 12072393Syz155240 * current list of entries. Security precaution to prevent 12082393Syz155240 * copying of random kernel data. 12092393Syz155240 */ 12103448Sdh155122 for (n = ifs->ifs_nat_instances; n; n = n->nat_next) 12112393Syz155240 if (n == nat) 12122393Syz155240 break; 12132393Syz155240 if (!n) 12142393Syz155240 return ESRCH; 12152393Syz155240 } 12162393Syz155240 12172393Syz155240 /* 12182393Syz155240 * Incluse any space required for proxy data structures. 12192393Syz155240 */ 12202393Syz155240 ng.ng_sz = sizeof(nat_save_t); 12212393Syz155240 aps = nat->nat_aps; 12222393Syz155240 if (aps != NULL) { 12232393Syz155240 ng.ng_sz += sizeof(ap_session_t) - 4; 12242393Syz155240 if (aps->aps_data != 0) 12252393Syz155240 ng.ng_sz += aps->aps_psiz; 12262393Syz155240 } 12272393Syz155240 12282393Syz155240 BCOPYOUT(&ng, data, sizeof(ng)); 12292393Syz155240 return 0; 12302393Syz155240 } 12312393Syz155240 12322393Syz155240 12332393Syz155240 /* ------------------------------------------------------------------------ */ 12342393Syz155240 /* Function: fr_natgetent */ 12352393Syz155240 /* Returns: int - 0 == success, != 0 is the error value. */ 12362393Syz155240 /* Parameters: data(I) - pointer to natget structure with kernel pointer */ 12372393Syz155240 /* to NAT structure to copy out. */ 12382393Syz155240 /* */ 12392393Syz155240 /* Handle SIOCSTGET. */ 12402393Syz155240 /* Copies out NAT entry to user space. Any additional data held for a */ 12412393Syz155240 /* proxy is also copied, as to is the NAT rule which was responsible for it */ 12422393Syz155240 /* ------------------------------------------------------------------------ */ 12433448Sdh155122 static int fr_natgetent(data, ifs) 12442393Syz155240 caddr_t data; 12453448Sdh155122 ipf_stack_t *ifs; 12462393Syz155240 { 12472393Syz155240 int error, outsize; 12482393Syz155240 ap_session_t *aps; 12492393Syz155240 nat_save_t *ipn, ipns; 12502393Syz155240 nat_t *n, *nat; 12512393Syz155240 12522393Syz155240 error = fr_inobj(data, &ipns, IPFOBJ_NATSAVE); 12532393Syz155240 if (error != 0) 12542393Syz155240 return error; 12552393Syz155240 12562393Syz155240 if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) 12572393Syz155240 return EINVAL; 12582393Syz155240 12592393Syz155240 KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize); 12602393Syz155240 if (ipn == NULL) 12612393Syz155240 return ENOMEM; 12622393Syz155240 12632393Syz155240 ipn->ipn_dsize = ipns.ipn_dsize; 12642393Syz155240 nat = ipns.ipn_next; 12652393Syz155240 if (nat == NULL) { 12663448Sdh155122 nat = ifs->ifs_nat_instances; 12672393Syz155240 if (nat == NULL) { 12683448Sdh155122 if (ifs->ifs_nat_instances == NULL) 12692393Syz155240 error = ENOENT; 12702393Syz155240 goto finished; 12712393Syz155240 } 12722393Syz155240 } else { 12732393Syz155240 /* 12742393Syz155240 * Make sure the pointer we're copying from exists in the 12752393Syz155240 * current list of entries. Security precaution to prevent 12762393Syz155240 * copying of random kernel data. 12772393Syz155240 */ 12783448Sdh155122 for (n = ifs->ifs_nat_instances; n; n = n->nat_next) 12792393Syz155240 if (n == nat) 12802393Syz155240 break; 12812393Syz155240 if (n == NULL) { 12822393Syz155240 error = ESRCH; 12832393Syz155240 goto finished; 12842393Syz155240 } 12852393Syz155240 } 12862393Syz155240 ipn->ipn_next = nat->nat_next; 12872393Syz155240 12882393Syz155240 /* 12892393Syz155240 * Copy the NAT structure. 12902393Syz155240 */ 12912393Syz155240 bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat)); 12922393Syz155240 12932393Syz155240 /* 12942393Syz155240 * If we have a pointer to the NAT rule it belongs to, save that too. 12952393Syz155240 */ 12962393Syz155240 if (nat->nat_ptr != NULL) 12972393Syz155240 bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat, 12982393Syz155240 sizeof(ipn->ipn_ipnat)); 12992393Syz155240 13002393Syz155240 /* 13012393Syz155240 * If we also know the NAT entry has an associated filter rule, 13022393Syz155240 * save that too. 13032393Syz155240 */ 13042393Syz155240 if (nat->nat_fr != NULL) 13052393Syz155240 bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr, 13062393Syz155240 sizeof(ipn->ipn_fr)); 13072393Syz155240 13082393Syz155240 /* 13092393Syz155240 * Last but not least, if there is an application proxy session set 13102393Syz155240 * up for this NAT entry, then copy that out too, including any 13112393Syz155240 * private data saved along side it by the proxy. 13122393Syz155240 */ 13132393Syz155240 aps = nat->nat_aps; 13142393Syz155240 outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data); 13152393Syz155240 if (aps != NULL) { 13162393Syz155240 char *s; 13172393Syz155240 13182393Syz155240 if (outsize < sizeof(*aps)) { 13192393Syz155240 error = ENOBUFS; 13202393Syz155240 goto finished; 13212393Syz155240 } 13222393Syz155240 13232393Syz155240 s = ipn->ipn_data; 13242393Syz155240 bcopy((char *)aps, s, sizeof(*aps)); 13252393Syz155240 s += sizeof(*aps); 13262393Syz155240 outsize -= sizeof(*aps); 13272393Syz155240 if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz)) 13282393Syz155240 bcopy(aps->aps_data, s, aps->aps_psiz); 13292393Syz155240 else 13302393Syz155240 error = ENOBUFS; 13312393Syz155240 } 13322393Syz155240 if (error == 0) { 13332393Syz155240 error = fr_outobjsz(data, ipn, IPFOBJ_NATSAVE, ipns.ipn_dsize); 13342393Syz155240 } 13352393Syz155240 13362393Syz155240 finished: 13372393Syz155240 if (ipn != NULL) { 13382393Syz155240 KFREES(ipn, ipns.ipn_dsize); 13392393Syz155240 } 13402393Syz155240 return error; 13412393Syz155240 } 13422393Syz155240 13436253San207044 /* ------------------------------------------------------------------------ */ 13446253San207044 /* Function: nat_calc_chksum_diffs */ 13456253San207044 /* Returns: void */ 13466253San207044 /* Parameters: nat - pointer to NAT table entry */ 13476253San207044 /* */ 13486253San207044 /* Function calculates chksum deltas for IP header (nat_ipsumd) and TCP/UDP */ 13496253San207044 /* headers (nat_sumd). The things for L4 (UDP/TCP) get complicated when */ 13506253San207044 /* we are dealing with partial chksum offload. For these cases we need to */ 13516253San207044 /* compute a 'partial chksum delta'. The 'partial chksum delta'is stored */ 13526253San207044 /* into nat_sumd[1], while ordinary chksum delta for TCP/UDP is in */ 13536253San207044 /* nat_sumd[0]. */ 13546253San207044 /* */ 13556253San207044 /* The function accepts initialized NAT table entry and computes the deltas */ 13566253San207044 /* from nat_inip/nat_outip members. The function is called right before */ 13576253San207044 /* the new entry is inserted into the table. */ 13586253San207044 /* */ 13596253San207044 /* The ipsumd (IP hedaer chksum delta adjustment) is computed as a chksum */ 13606253San207044 /* of delta between original and new IP addresses. */ 13616253San207044 /* */ 13626253San207044 /* the nat_sumd[0] (TCP/UDP header chksum delta adjustment) is computed as */ 13636253San207044 /* a chkusm of delta between original an new IP addrress:port tupples. */ 13646253San207044 /* */ 13656253San207044 /* Some facts about chksum, we should remember: */ 13666253San207044 /* IP header chksum covers IP header only */ 13676253San207044 /* */ 13686253San207044 /* TCP/UDP chksum covers data payload and so called pseudo header */ 13696253San207044 /* SRC, DST IP address */ 13706253San207044 /* SRC, DST Port */ 13716253San207044 /* length of payload */ 13726253San207044 /* */ 13736253San207044 /* The partial chksum delta (nat_sumd[1] is used to adjust db_ckusm16 */ 13746253San207044 /* member of dblk_t structure. The db_ckusm16 member is not part of */ 13756253San207044 /* IP/UDP/TCP header it is 16 bit value computed by NIC driver with partial */ 13766253San207044 /* chksum offload capacbility for every inbound packet. The db_cksum16 is */ 13776253San207044 /* stored along with other IP packet data in dblk_t structure and used in */ 13786253San207044 /* for IP/UDP/TCP chksum validation later in ip.c. */ 13796253San207044 /* */ 13806253San207044 /* The partial chksum delta (adjustment, nat_sumd[1]) is computed as chksum */ 13816253San207044 /* of delta between new and orig address. NOTE: the order of operands for */ 13826253San207044 /* partial delta operation is swapped compared to computing the IP/TCP/UDP */ 13836253San207044 /* header adjustment. It is by design see (IP_CKSUM_RECV() macro in ip.c). */ 13846253San207044 /* */ 13856253San207044 /* ------------------------------------------------------------------------ */ 13867176Syx160601 void nat_calc_chksum_diffs(nat) 13876253San207044 nat_t *nat; 13886253San207044 { 13896253San207044 u_32_t sum_orig = 0; 13906253San207044 u_32_t sum_changed = 0; 13916253San207044 u_32_t sumd; 13926253San207044 u_32_t ipsum_orig = 0; 13936253San207044 u_32_t ipsum_changed = 0; 13946253San207044 13957176Syx160601 if (nat->nat_v != 4 && nat->nat_v != 6) 13967176Syx160601 return; 13977176Syx160601 13986253San207044 /* 13996253San207044 * the switch calculates operands for CALC_SUMD(), 14006253San207044 * which will compute the partial chksum delta. 14016253San207044 */ 14026253San207044 switch (nat->nat_dir) 14036253San207044 { 14046253San207044 case NAT_INBOUND: 14056253San207044 /* 14066253San207044 * we are dealing with RDR rule (DST address gets 14076253San207044 * modified on packet from client) 14086253San207044 */ 14097176Syx160601 if (nat->nat_v == 4) { 14107176Syx160601 sum_changed = LONG_SUM(ntohl(nat->nat_inip.s_addr)); 14117176Syx160601 sum_orig = LONG_SUM(ntohl(nat->nat_outip.s_addr)); 14127176Syx160601 } else { 14137176Syx160601 sum_changed = LONG_SUM6(&nat->nat_inip6); 14147176Syx160601 sum_orig = LONG_SUM6(&nat->nat_outip6); 14157176Syx160601 } 14166253San207044 break; 14176253San207044 case NAT_OUTBOUND: 14186253San207044 /* 14196253San207044 * we are dealing with MAP rule (SRC address gets 14206253San207044 * modified on packet from client) 14216253San207044 */ 14227176Syx160601 if (nat->nat_v == 4) { 14237176Syx160601 sum_changed = LONG_SUM(ntohl(nat->nat_outip.s_addr)); 14247176Syx160601 sum_orig = LONG_SUM(ntohl(nat->nat_inip.s_addr)); 14257176Syx160601 } else { 14267176Syx160601 sum_changed = LONG_SUM6(&nat->nat_outip6); 14277176Syx160601 sum_orig = LONG_SUM6(&nat->nat_inip6); 14287176Syx160601 } 14296253San207044 break; 14306253San207044 default: ; 14316253San207044 break; 14326253San207044 } 14336253San207044 14346253San207044 /* 14356253San207044 * we also preserve CALC_SUMD() operands here, for IP chksum delta 14366253San207044 * calculation, which happens at the end of function. 14376253San207044 */ 14386253San207044 ipsum_changed = sum_changed; 14396253San207044 ipsum_orig = sum_orig; 14406253San207044 /* 14416253San207044 * NOTE: the order of operands for partial chksum adjustment 14426253San207044 * computation has to be swapped! 14436253San207044 */ 14446253San207044 CALC_SUMD(sum_changed, sum_orig, sumd); 14456253San207044 nat->nat_sumd[1] = (sumd & 0xffff) + (sumd >> 16); 14466253San207044 14476295San207044 if (nat->nat_flags & (IPN_TCPUDP | IPN_ICMPQUERY)) { 14486253San207044 14496253San207044 /* 14506253San207044 * switch calculates operands for CALC_SUMD(), which will 14516253San207044 * compute the full chksum delta. 14526253San207044 */ 14536253San207044 switch (nat->nat_dir) 14546253San207044 { 14556253San207044 case NAT_INBOUND: 14567176Syx160601 if (nat->nat_v == 4) { 14577176Syx160601 sum_changed = LONG_SUM( 14587176Syx160601 ntohl(nat->nat_inip.s_addr) + 14597176Syx160601 ntohs(nat->nat_inport)); 14607176Syx160601 sum_orig = LONG_SUM( 14617176Syx160601 ntohl(nat->nat_outip.s_addr) + 14627176Syx160601 ntohs(nat->nat_outport)); 14637176Syx160601 } else { 14647176Syx160601 sum_changed = LONG_SUM6(&nat->nat_inip6) + 14657176Syx160601 ntohs(nat->nat_inport); 14667176Syx160601 sum_orig = LONG_SUM6(&nat->nat_outip6) + 14677176Syx160601 ntohs(nat->nat_outport); 14687176Syx160601 } 14696253San207044 break; 14706253San207044 case NAT_OUTBOUND: 14717176Syx160601 if (nat->nat_v == 4) { 14727176Syx160601 sum_changed = LONG_SUM( 14737176Syx160601 ntohl(nat->nat_outip.s_addr) + 14747176Syx160601 ntohs(nat->nat_outport)); 14757176Syx160601 sum_orig = LONG_SUM( 14767176Syx160601 ntohl(nat->nat_inip.s_addr) + 14777176Syx160601 ntohs(nat->nat_inport)); 14787176Syx160601 } else { 14797176Syx160601 sum_changed = LONG_SUM6(&nat->nat_outip6) + 14807176Syx160601 ntohs(nat->nat_outport); 14817176Syx160601 sum_orig = LONG_SUM6(&nat->nat_inip6) + 14827176Syx160601 ntohs(nat->nat_inport); 14837176Syx160601 } 14846253San207044 break; 14856253San207044 default: ; 14866253San207044 break; 14876253San207044 } 14886253San207044 14896253San207044 CALC_SUMD(sum_orig, sum_changed, sumd); 14906253San207044 nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 14916295San207044 14926295San207044 if (!(nat->nat_flags & IPN_TCPUDP)) { 14936295San207044 /* 14946295San207044 * partial HW chksum offload works for TCP/UDP headers only, 14956295San207044 * so we need to enforce full chksum adjustment for ICMP 14966295San207044 */ 14976295San207044 nat->nat_sumd[1] = nat->nat_sumd[0]; 14986295San207044 } 14996253San207044 } 15006253San207044 else 15016253San207044 nat->nat_sumd[0] = nat->nat_sumd[1]; 15026253San207044 15036253San207044 /* 15046253San207044 * we may reuse the already computed nat_sumd[0] for IP header chksum 15056253San207044 * adjustment in case the L4 (TCP/UDP header) is not changed by NAT. 15066253San207044 */ 15077176Syx160601 if (nat->nat_v == 4) { 15087176Syx160601 if (NAT_HAS_L4_CHANGED(nat)) { 15097176Syx160601 /* 15107176Syx160601 * bad luck, NAT changes also the L4 header, use IP 15117176Syx160601 * addresses to compute chksum adjustment for IP header. 15127176Syx160601 */ 15137176Syx160601 CALC_SUMD(ipsum_orig, ipsum_changed, sumd); 15147176Syx160601 nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); 15157176Syx160601 } else { 15167176Syx160601 /* 15177176Syx160601 * the NAT does not change L4 hdr -> reuse chksum 15187176Syx160601 * adjustment for IP hdr. 15197176Syx160601 */ 15207176Syx160601 nat->nat_ipsumd = nat->nat_sumd[0]; 15217176Syx160601 15227176Syx160601 /* 15237176Syx160601 * if L4 header does not use chksum - zero out deltas 15247176Syx160601 */ 15257176Syx160601 if (!(nat->nat_flags & IPN_TCPUDP)) { 15267176Syx160601 nat->nat_sumd[0] = 0; 15277176Syx160601 nat->nat_sumd[1] = 0; 15287176Syx160601 } 15296295San207044 } 15306253San207044 } 15316253San207044 15326253San207044 return; 15336253San207044 } 15342393Syz155240 15352393Syz155240 /* ------------------------------------------------------------------------ */ 15362393Syz155240 /* Function: fr_natputent */ 15372393Syz155240 /* Returns: int - 0 == success, != 0 is the error value. */ 15382393Syz155240 /* Parameters: data(I) - pointer to natget structure with NAT */ 15392393Syz155240 /* structure information to load into the kernel */ 15402393Syz155240 /* getlock(I) - flag indicating whether or not a write lock */ 15412393Syz155240 /* on ipf_nat is already held. */ 15422393Syz155240 /* */ 15432393Syz155240 /* Handle SIOCSTPUT. */ 15442393Syz155240 /* Loads a NAT table entry from user space, including a NAT rule, proxy and */ 15452393Syz155240 /* firewall rule data structures, if pointers to them indicate so. */ 15462393Syz155240 /* ------------------------------------------------------------------------ */ 15473448Sdh155122 static int fr_natputent(data, getlock, ifs) 15482393Syz155240 caddr_t data; 15492393Syz155240 int getlock; 15503448Sdh155122 ipf_stack_t *ifs; 15512393Syz155240 { 15522393Syz155240 nat_save_t ipn, *ipnn; 15532393Syz155240 ap_session_t *aps; 15542393Syz155240 nat_t *n, *nat; 15552393Syz155240 frentry_t *fr; 15562393Syz155240 fr_info_t fin; 15572393Syz155240 ipnat_t *in; 15582393Syz155240 int error; 15592393Syz155240 15602393Syz155240 error = fr_inobj(data, &ipn, IPFOBJ_NATSAVE); 15612393Syz155240 if (error != 0) 15622393Syz155240 return error; 15632393Syz155240 15642393Syz155240 /* 15654817San207044 * Trigger automatic call to nat_extraflush() if the 15664817San207044 * table has reached capcity specified by hi watermark. 15674817San207044 */ 15684817San207044 if (NAT_TAB_WATER_LEVEL(ifs) > ifs->ifs_nat_flush_lvl_hi) 15694817San207044 ifs->ifs_nat_doflush = 1; 15704817San207044 15714817San207044 /* 1572*7432SJohn.Ojemann@Sun.COM * If automatic flushing did not do its job, and the table 1573*7432SJohn.Ojemann@Sun.COM * has filled up, don't try to create a new entry. 1574*7432SJohn.Ojemann@Sun.COM */ 1575*7432SJohn.Ojemann@Sun.COM if (ifs->ifs_nat_stats.ns_inuse >= ifs->ifs_ipf_nattable_max) { 1576*7432SJohn.Ojemann@Sun.COM ifs->ifs_nat_stats.ns_memfail++; 1577*7432SJohn.Ojemann@Sun.COM return ENOMEM; 1578*7432SJohn.Ojemann@Sun.COM } 1579*7432SJohn.Ojemann@Sun.COM 1580*7432SJohn.Ojemann@Sun.COM /* 15812393Syz155240 * Initialise early because of code at junkput label. 15822393Syz155240 */ 15832393Syz155240 in = NULL; 15842393Syz155240 aps = NULL; 15852393Syz155240 nat = NULL; 15862393Syz155240 ipnn = NULL; 15872393Syz155240 15882393Syz155240 /* 15892393Syz155240 * New entry, copy in the rest of the NAT entry if it's size is more 15902393Syz155240 * than just the nat_t structure. 15912393Syz155240 */ 15922393Syz155240 fr = NULL; 15932393Syz155240 if (ipn.ipn_dsize > sizeof(ipn)) { 15942393Syz155240 if (ipn.ipn_dsize > 81920) { 15952393Syz155240 error = ENOMEM; 15962393Syz155240 goto junkput; 15972393Syz155240 } 15982393Syz155240 15992393Syz155240 KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize); 16002393Syz155240 if (ipnn == NULL) 16012393Syz155240 return ENOMEM; 16022393Syz155240 16032393Syz155240 error = fr_inobjsz(data, ipnn, IPFOBJ_NATSAVE, ipn.ipn_dsize); 16042393Syz155240 if (error != 0) { 16052393Syz155240 error = EFAULT; 16062393Syz155240 goto junkput; 16072393Syz155240 } 16082393Syz155240 } else 16092393Syz155240 ipnn = &ipn; 16102393Syz155240 16112393Syz155240 KMALLOC(nat, nat_t *); 16122393Syz155240 if (nat == NULL) { 16132393Syz155240 error = ENOMEM; 16142393Syz155240 goto junkput; 16152393Syz155240 } 16162393Syz155240 16172393Syz155240 bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat)); 16182393Syz155240 /* 16192393Syz155240 * Initialize all these so that nat_delete() doesn't cause a crash. 16202393Syz155240 */ 16212393Syz155240 bzero((char *)nat, offsetof(struct nat, nat_tqe)); 16222393Syz155240 nat->nat_tqe.tqe_pnext = NULL; 16232393Syz155240 nat->nat_tqe.tqe_next = NULL; 16242393Syz155240 nat->nat_tqe.tqe_ifq = NULL; 16252393Syz155240 nat->nat_tqe.tqe_parent = nat; 16262393Syz155240 16272393Syz155240 /* 16282393Syz155240 * Restore the rule associated with this nat session 16292393Syz155240 */ 16302393Syz155240 in = ipnn->ipn_nat.nat_ptr; 16312393Syz155240 if (in != NULL) { 16322393Syz155240 KMALLOC(in, ipnat_t *); 16332393Syz155240 nat->nat_ptr = in; 16342393Syz155240 if (in == NULL) { 16352393Syz155240 error = ENOMEM; 16362393Syz155240 goto junkput; 16372393Syz155240 } 16382393Syz155240 bzero((char *)in, offsetof(struct ipnat, in_next6)); 16392393Syz155240 bcopy((char *)&ipnn->ipn_ipnat, (char *)in, sizeof(*in)); 16402393Syz155240 in->in_use = 1; 16412393Syz155240 in->in_flags |= IPN_DELETE; 16422393Syz155240 16433448Sdh155122 ATOMIC_INC(ifs->ifs_nat_stats.ns_rules); 16443448Sdh155122 16454380Sjojemann if (nat_resolverule(in, ifs) != 0) { 16464380Sjojemann error = ESRCH; 16474380Sjojemann goto junkput; 16484380Sjojemann } 16492393Syz155240 } 16502393Syz155240 16512393Syz155240 /* 16522393Syz155240 * Check that the NAT entry doesn't already exist in the kernel. 16532393Syz155240 */ 16547176Syx160601 if (nat->nat_v != 6) 16557176Syx160601 nat->nat_v = 4; 16562393Syz155240 bzero((char *)&fin, sizeof(fin)); 16572393Syz155240 fin.fin_p = nat->nat_p; 16584414Sjojemann fin.fin_ifs = ifs; 16592393Syz155240 if (nat->nat_dir == NAT_OUTBOUND) { 16602393Syz155240 fin.fin_data[0] = ntohs(nat->nat_oport); 16612393Syz155240 fin.fin_data[1] = ntohs(nat->nat_outport); 16622508Syz155240 fin.fin_ifp = nat->nat_ifps[0]; 16632393Syz155240 if (getlock) { 16643448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 16652393Syz155240 } 16667176Syx160601 16677176Syx160601 switch (nat->nat_v) 16687176Syx160601 { 16697176Syx160601 case 4: 16707176Syx160601 fin.fin_v = nat->nat_v; 16717176Syx160601 n = nat_inlookup(&fin, nat->nat_flags, fin.fin_p, 16727176Syx160601 nat->nat_oip, nat->nat_outip); 16737176Syx160601 break; 16747176Syx160601 #ifdef USE_INET6 16757176Syx160601 case 6: 16767176Syx160601 n = nat6_inlookup(&fin, nat->nat_flags, fin.fin_p, 16777176Syx160601 &nat->nat_oip6.in6, &nat->nat_outip6.in6); 16787176Syx160601 break; 16797176Syx160601 #endif 16807176Syx160601 default: 16817176Syx160601 n = NULL; 16827176Syx160601 break; 16837176Syx160601 } 16847176Syx160601 16852393Syz155240 if (getlock) { 16863448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 16872393Syz155240 } 16882393Syz155240 if (n != NULL) { 16892393Syz155240 error = EEXIST; 16902393Syz155240 goto junkput; 16912393Syz155240 } 16922393Syz155240 } else if (nat->nat_dir == NAT_INBOUND) { 16932393Syz155240 fin.fin_data[0] = ntohs(nat->nat_inport); 16942393Syz155240 fin.fin_data[1] = ntohs(nat->nat_oport); 16952508Syz155240 fin.fin_ifp = nat->nat_ifps[1]; 16962393Syz155240 if (getlock) { 16973448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 16982393Syz155240 } 16997176Syx160601 17007176Syx160601 switch (nat->nat_v) 17017176Syx160601 { 17027176Syx160601 case 4: 17037176Syx160601 n = nat_outlookup(&fin, nat->nat_flags, fin.fin_p, 17047176Syx160601 nat->nat_inip, nat->nat_oip); 17057176Syx160601 break; 17067176Syx160601 #ifdef USE_INET6 17077176Syx160601 case 6: 17087176Syx160601 n = nat6_outlookup(&fin, nat->nat_flags, fin.fin_p, 17097176Syx160601 &nat->nat_inip6.in6, &nat->nat_oip6.in6); 17107176Syx160601 break; 17117176Syx160601 #endif 17127176Syx160601 default: 17137176Syx160601 n = NULL; 17147176Syx160601 break; 17157176Syx160601 } 17167176Syx160601 17172393Syz155240 if (getlock) { 17183448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 17192393Syz155240 } 17202393Syz155240 if (n != NULL) { 17212393Syz155240 error = EEXIST; 17222393Syz155240 goto junkput; 17232393Syz155240 } 17242393Syz155240 } else { 17252393Syz155240 error = EINVAL; 17262393Syz155240 goto junkput; 17272393Syz155240 } 17282393Syz155240 17292393Syz155240 /* 17302393Syz155240 * Restore ap_session_t structure. Include the private data allocated 17312393Syz155240 * if it was there. 17322393Syz155240 */ 17332393Syz155240 aps = nat->nat_aps; 17342393Syz155240 if (aps != NULL) { 17352393Syz155240 KMALLOC(aps, ap_session_t *); 17362393Syz155240 nat->nat_aps = aps; 17372393Syz155240 if (aps == NULL) { 17382393Syz155240 error = ENOMEM; 17392393Syz155240 goto junkput; 17402393Syz155240 } 17412393Syz155240 bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps)); 17422393Syz155240 if (in != NULL) 17432393Syz155240 aps->aps_apr = in->in_apr; 17442393Syz155240 else 17452393Syz155240 aps->aps_apr = NULL; 17462393Syz155240 if (aps->aps_psiz != 0) { 17472393Syz155240 if (aps->aps_psiz > 81920) { 17482393Syz155240 error = ENOMEM; 17492393Syz155240 goto junkput; 17502393Syz155240 } 17512393Syz155240 KMALLOCS(aps->aps_data, void *, aps->aps_psiz); 17522393Syz155240 if (aps->aps_data == NULL) { 17532393Syz155240 error = ENOMEM; 17542393Syz155240 goto junkput; 17552393Syz155240 } 17562393Syz155240 bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data, 17572393Syz155240 aps->aps_psiz); 17582393Syz155240 } else { 17592393Syz155240 aps->aps_psiz = 0; 17602393Syz155240 aps->aps_data = NULL; 17612393Syz155240 } 17622393Syz155240 } 17632393Syz155240 17642393Syz155240 /* 17652393Syz155240 * If there was a filtering rule associated with this entry then 17662393Syz155240 * build up a new one. 17672393Syz155240 */ 17682393Syz155240 fr = nat->nat_fr; 17692393Syz155240 if (fr != NULL) { 17702393Syz155240 if ((nat->nat_flags & SI_NEWFR) != 0) { 17712393Syz155240 KMALLOC(fr, frentry_t *); 17722393Syz155240 nat->nat_fr = fr; 17732393Syz155240 if (fr == NULL) { 17742393Syz155240 error = ENOMEM; 17752393Syz155240 goto junkput; 17762393Syz155240 } 17772393Syz155240 ipnn->ipn_nat.nat_fr = fr; 17782393Syz155240 (void) fr_outobj(data, ipnn, IPFOBJ_NATSAVE); 17792393Syz155240 bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr)); 17804380Sjojemann 17814380Sjojemann fr->fr_ref = 1; 17824380Sjojemann fr->fr_dsize = 0; 17834380Sjojemann fr->fr_data = NULL; 17844380Sjojemann fr->fr_type = FR_T_NONE; 17854380Sjojemann 17862393Syz155240 MUTEX_NUKE(&fr->fr_lock); 17872393Syz155240 MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock"); 17882393Syz155240 } else { 17894380Sjojemann if (getlock) { 17904380Sjojemann READ_ENTER(&ifs->ifs_ipf_nat); 17914380Sjojemann } 17923448Sdh155122 for (n = ifs->ifs_nat_instances; n; n = n->nat_next) 17932393Syz155240 if (n->nat_fr == fr) 17942393Syz155240 break; 17952393Syz155240 17962393Syz155240 if (n != NULL) { 17972393Syz155240 MUTEX_ENTER(&fr->fr_lock); 17982393Syz155240 fr->fr_ref++; 17992393Syz155240 MUTEX_EXIT(&fr->fr_lock); 18002393Syz155240 } 18014380Sjojemann if (getlock) { 18024380Sjojemann RWLOCK_EXIT(&ifs->ifs_ipf_nat); 18034380Sjojemann } 18042393Syz155240 if (!n) { 18052393Syz155240 error = ESRCH; 18062393Syz155240 goto junkput; 18072393Syz155240 } 18082393Syz155240 } 18092393Syz155240 } 18102393Syz155240 18112393Syz155240 if (ipnn != &ipn) { 18122393Syz155240 KFREES(ipnn, ipn.ipn_dsize); 18132393Syz155240 ipnn = NULL; 18142393Syz155240 } 18152393Syz155240 18166253San207044 nat_calc_chksum_diffs(nat); 18176253San207044 18182393Syz155240 if (getlock) { 18193448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 18202393Syz155240 } 18217176Syx160601 18227176Syx160601 nat_calc_chksum_diffs(nat); 18237176Syx160601 18247176Syx160601 switch (nat->nat_v) 18257176Syx160601 { 18267176Syx160601 case 4 : 18277176Syx160601 error = nat_insert(nat, nat->nat_rev, ifs); 18287176Syx160601 break; 18297176Syx160601 #ifdef USE_INET6 18307176Syx160601 case 6 : 18317176Syx160601 error = nat6_insert(nat, nat->nat_rev, ifs); 18327176Syx160601 break; 18337176Syx160601 #endif 18347176Syx160601 default : 18357176Syx160601 break; 18367176Syx160601 } 18377176Syx160601 18382393Syz155240 if ((error == 0) && (aps != NULL)) { 18393448Sdh155122 aps->aps_next = ifs->ifs_ap_sess_list; 18403448Sdh155122 ifs->ifs_ap_sess_list = aps; 18412393Syz155240 } 18422393Syz155240 if (getlock) { 18433448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 18442393Syz155240 } 18452393Syz155240 18462393Syz155240 if (error == 0) 18472393Syz155240 return 0; 18482393Syz155240 18492393Syz155240 error = ENOMEM; 18502393Syz155240 18512393Syz155240 junkput: 18522393Syz155240 if (fr != NULL) 18533448Sdh155122 (void) fr_derefrule(&fr, ifs); 18542393Syz155240 18552393Syz155240 if ((ipnn != NULL) && (ipnn != &ipn)) { 18562393Syz155240 KFREES(ipnn, ipn.ipn_dsize); 18572393Syz155240 } 18582393Syz155240 if (nat != NULL) { 18592393Syz155240 if (aps != NULL) { 18602393Syz155240 if (aps->aps_data != NULL) { 18612393Syz155240 KFREES(aps->aps_data, aps->aps_psiz); 18622393Syz155240 } 18632393Syz155240 KFREE(aps); 18642393Syz155240 } 18652393Syz155240 if (in != NULL) { 18662393Syz155240 if (in->in_apr) 18672393Syz155240 appr_free(in->in_apr); 18682393Syz155240 KFREE(in); 18692393Syz155240 } 18702393Syz155240 KFREE(nat); 18712393Syz155240 } 18722393Syz155240 return error; 18732393Syz155240 } 18742393Syz155240 18752393Syz155240 18762393Syz155240 /* ------------------------------------------------------------------------ */ 18772393Syz155240 /* Function: nat_delete */ 18782393Syz155240 /* Returns: Nil */ 18792393Syz155240 /* Parameters: natd(I) - pointer to NAT structure to delete */ 18802393Syz155240 /* logtype(I) - type of LOG record to create before deleting */ 1881*7432SJohn.Ojemann@Sun.COM /* ifs - ipf stack instance */ 18822393Syz155240 /* Write Lock: ipf_nat */ 18832393Syz155240 /* */ 18842393Syz155240 /* Delete a nat entry from the various lists and table. If NAT logging is */ 18852393Syz155240 /* enabled then generate a NAT log record for this event. */ 18862393Syz155240 /* ------------------------------------------------------------------------ */ 1887*7432SJohn.Ojemann@Sun.COM void nat_delete(nat, logtype, ifs) 18882393Syz155240 struct nat *nat; 18892393Syz155240 int logtype; 18903448Sdh155122 ipf_stack_t *ifs; 18912393Syz155240 { 18922393Syz155240 struct ipnat *ipn; 1893*7432SJohn.Ojemann@Sun.COM int removed = 0; 18942393Syz155240 18953448Sdh155122 if (logtype != 0 && ifs->ifs_nat_logging != 0) 18963448Sdh155122 nat_log(nat, logtype, ifs); 18973448Sdh155122 18982393Syz155240 /* 1899*7432SJohn.Ojemann@Sun.COM * Start by removing the entry from the hash table of nat entries 1900*7432SJohn.Ojemann@Sun.COM * so it will not be "used" again. 1901*7432SJohn.Ojemann@Sun.COM * 1902*7432SJohn.Ojemann@Sun.COM * It will remain in the "list" of nat entries until all references 1903*7432SJohn.Ojemann@Sun.COM * have been accounted for. 19042393Syz155240 */ 1905*7432SJohn.Ojemann@Sun.COM if ((nat->nat_phnext[0] != NULL) && (nat->nat_phnext[1] != NULL)) { 1906*7432SJohn.Ojemann@Sun.COM removed = 1; 1907*7432SJohn.Ojemann@Sun.COM 19083448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--; 19093448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--; 19102393Syz155240 19112393Syz155240 *nat->nat_phnext[0] = nat->nat_hnext[0]; 19122393Syz155240 if (nat->nat_hnext[0] != NULL) { 19132393Syz155240 nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 19142393Syz155240 nat->nat_hnext[0] = NULL; 19152393Syz155240 } 19162393Syz155240 nat->nat_phnext[0] = NULL; 19172393Syz155240 19182393Syz155240 *nat->nat_phnext[1] = nat->nat_hnext[1]; 19192393Syz155240 if (nat->nat_hnext[1] != NULL) { 19202393Syz155240 nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 19212393Syz155240 nat->nat_hnext[1] = NULL; 19222393Syz155240 } 19232393Syz155240 nat->nat_phnext[1] = NULL; 19242393Syz155240 19252393Syz155240 if ((nat->nat_flags & SI_WILDP) != 0) 19263448Sdh155122 ifs->ifs_nat_stats.ns_wilds--; 19272393Syz155240 } 19282393Syz155240 1929*7432SJohn.Ojemann@Sun.COM /* 1930*7432SJohn.Ojemann@Sun.COM * Next, remove it from the timeout queue it is in. 1931*7432SJohn.Ojemann@Sun.COM */ 1932*7432SJohn.Ojemann@Sun.COM fr_deletequeueentry(&nat->nat_tqe); 1933*7432SJohn.Ojemann@Sun.COM 19342393Syz155240 if (nat->nat_me != NULL) { 19352393Syz155240 *nat->nat_me = NULL; 19362393Syz155240 nat->nat_me = NULL; 19372393Syz155240 } 19382393Syz155240 19395055Sdr146992 MUTEX_ENTER(&nat->nat_lock); 1940*7432SJohn.Ojemann@Sun.COM if (logtype == NL_DESTROY) { 1941*7432SJohn.Ojemann@Sun.COM /* 1942*7432SJohn.Ojemann@Sun.COM * NL_DESTROY should only be passed when nat_ref >= 2. 1943*7432SJohn.Ojemann@Sun.COM * This happens when a nat'd packet is blocked, we have 1944*7432SJohn.Ojemann@Sun.COM * just created the nat table entry (reason why the ref 1945*7432SJohn.Ojemann@Sun.COM * count is 2 or higher), but and we want to throw away 1946*7432SJohn.Ojemann@Sun.COM * that NAT session as result of the blocked packet. 1947*7432SJohn.Ojemann@Sun.COM */ 1948*7432SJohn.Ojemann@Sun.COM if (nat->nat_ref > 2) { 1949*7432SJohn.Ojemann@Sun.COM nat->nat_ref -= 2; 1950*7432SJohn.Ojemann@Sun.COM MUTEX_EXIT(&nat->nat_lock); 1951*7432SJohn.Ojemann@Sun.COM if (removed) 1952*7432SJohn.Ojemann@Sun.COM ifs->ifs_nat_stats.ns_orphans++; 1953*7432SJohn.Ojemann@Sun.COM return; 1954*7432SJohn.Ojemann@Sun.COM } 1955*7432SJohn.Ojemann@Sun.COM } else if (nat->nat_ref > 1) { 19565055Sdr146992 nat->nat_ref--; 19575055Sdr146992 MUTEX_EXIT(&nat->nat_lock); 1958*7432SJohn.Ojemann@Sun.COM if (removed) 1959*7432SJohn.Ojemann@Sun.COM ifs->ifs_nat_stats.ns_orphans++; 19602393Syz155240 return; 19612393Syz155240 } 19625055Sdr146992 MUTEX_EXIT(&nat->nat_lock); 19635055Sdr146992 1964*7432SJohn.Ojemann@Sun.COM nat->nat_ref = 0; 1965*7432SJohn.Ojemann@Sun.COM 19665055Sdr146992 /* 1967*7432SJohn.Ojemann@Sun.COM * If entry had already been removed, 1968*7432SJohn.Ojemann@Sun.COM * it means we're cleaning up an orphan. 19695055Sdr146992 */ 1970*7432SJohn.Ojemann@Sun.COM if (!removed) 1971*7432SJohn.Ojemann@Sun.COM ifs->ifs_nat_stats.ns_orphans--; 19722393Syz155240 19732393Syz155240 #ifdef IPFILTER_SYNC 19742393Syz155240 if (nat->nat_sync) 19752393Syz155240 ipfsync_del(nat->nat_sync); 19762393Syz155240 #endif 19772393Syz155240 1978*7432SJohn.Ojemann@Sun.COM /* 1979*7432SJohn.Ojemann@Sun.COM * Now remove it from master list of nat table entries 1980*7432SJohn.Ojemann@Sun.COM */ 1981*7432SJohn.Ojemann@Sun.COM if (nat->nat_pnext != NULL) { 1982*7432SJohn.Ojemann@Sun.COM *nat->nat_pnext = nat->nat_next; 1983*7432SJohn.Ojemann@Sun.COM if (nat->nat_next != NULL) { 1984*7432SJohn.Ojemann@Sun.COM nat->nat_next->nat_pnext = nat->nat_pnext; 1985*7432SJohn.Ojemann@Sun.COM nat->nat_next = NULL; 1986*7432SJohn.Ojemann@Sun.COM } 1987*7432SJohn.Ojemann@Sun.COM nat->nat_pnext = NULL; 1988*7432SJohn.Ojemann@Sun.COM } 1989*7432SJohn.Ojemann@Sun.COM 19902393Syz155240 if (nat->nat_fr != NULL) 19913448Sdh155122 (void)fr_derefrule(&nat->nat_fr, ifs); 19922393Syz155240 19932393Syz155240 if (nat->nat_hm != NULL) 19945417Sjojemann fr_hostmapdel(&nat->nat_hm); 19952393Syz155240 19962393Syz155240 /* 19972393Syz155240 * If there is an active reference from the nat entry to its parent 19982393Syz155240 * rule, decrement the rule's reference count and free it too if no 19992393Syz155240 * longer being used. 20002393Syz155240 */ 20012393Syz155240 ipn = nat->nat_ptr; 20022393Syz155240 if (ipn != NULL) { 20032393Syz155240 ipn->in_space++; 20042393Syz155240 ipn->in_use--; 20052393Syz155240 if (ipn->in_use == 0 && (ipn->in_flags & IPN_DELETE)) { 20062393Syz155240 if (ipn->in_apr) 20072393Syz155240 appr_free(ipn->in_apr); 20082393Syz155240 KFREE(ipn); 20093448Sdh155122 ifs->ifs_nat_stats.ns_rules--; 20102393Syz155240 } 20112393Syz155240 } 20122393Syz155240 20132393Syz155240 MUTEX_DESTROY(&nat->nat_lock); 20142393Syz155240 20153448Sdh155122 aps_free(nat->nat_aps, ifs); 20163448Sdh155122 ifs->ifs_nat_stats.ns_inuse--; 20172393Syz155240 20182393Syz155240 /* 20192393Syz155240 * If there's a fragment table entry too for this nat entry, then 20202393Syz155240 * dereference that as well. This is after nat_lock is released 20212393Syz155240 * because of Tru64. 20222393Syz155240 */ 20233448Sdh155122 fr_forgetnat((void *)nat, ifs); 20242393Syz155240 20252393Syz155240 KFREE(nat); 20262393Syz155240 } 20272393Syz155240 20282393Syz155240 20292393Syz155240 /* ------------------------------------------------------------------------ */ 20302393Syz155240 /* Function: nat_flushtable */ 20312393Syz155240 /* Returns: int - number of NAT rules deleted */ 20322393Syz155240 /* Parameters: Nil */ 20332393Syz155240 /* */ 20342393Syz155240 /* Deletes all currently active NAT sessions. In deleting each NAT entry a */ 20352393Syz155240 /* log record should be emitted in nat_delete() if NAT logging is enabled. */ 20362393Syz155240 /* ------------------------------------------------------------------------ */ 20372393Syz155240 /* 20382393Syz155240 * nat_flushtable - clear the NAT table of all mapping entries. 20392393Syz155240 */ 20403448Sdh155122 static int nat_flushtable(ifs) 20413448Sdh155122 ipf_stack_t *ifs; 20422393Syz155240 { 20432393Syz155240 nat_t *nat; 20442393Syz155240 int j = 0; 20452393Syz155240 20462393Syz155240 /* 20472393Syz155240 * ALL NAT mappings deleted, so lets just make the deletions 20482393Syz155240 * quicker. 20492393Syz155240 */ 20503448Sdh155122 if (ifs->ifs_nat_table[0] != NULL) 20513448Sdh155122 bzero((char *)ifs->ifs_nat_table[0], 20523448Sdh155122 sizeof(ifs->ifs_nat_table[0]) * ifs->ifs_ipf_nattable_sz); 20533448Sdh155122 if (ifs->ifs_nat_table[1] != NULL) 20543448Sdh155122 bzero((char *)ifs->ifs_nat_table[1], 20553448Sdh155122 sizeof(ifs->ifs_nat_table[1]) * ifs->ifs_ipf_nattable_sz); 20563448Sdh155122 20573448Sdh155122 while ((nat = ifs->ifs_nat_instances) != NULL) { 20583448Sdh155122 nat_delete(nat, NL_FLUSH, ifs); 20592393Syz155240 j++; 20602393Syz155240 } 20612393Syz155240 20622393Syz155240 return j; 20632393Syz155240 } 20642393Syz155240 20652393Syz155240 20662393Syz155240 /* ------------------------------------------------------------------------ */ 20672393Syz155240 /* Function: nat_clearlist */ 20682393Syz155240 /* Returns: int - number of NAT/RDR rules deleted */ 20692393Syz155240 /* Parameters: Nil */ 20702393Syz155240 /* */ 20712393Syz155240 /* Delete all rules in the current list of rules. There is nothing elegant */ 20722393Syz155240 /* about this cleanup: simply free all entries on the list of rules and */ 20732393Syz155240 /* clear out the tables used for hashed NAT rule lookups. */ 20742393Syz155240 /* ------------------------------------------------------------------------ */ 20753448Sdh155122 static int nat_clearlist(ifs) 20763448Sdh155122 ipf_stack_t *ifs; 20772393Syz155240 { 20783448Sdh155122 ipnat_t *n, **np = &ifs->ifs_nat_list; 20792393Syz155240 int i = 0; 20802393Syz155240 20813448Sdh155122 if (ifs->ifs_nat_rules != NULL) 20823448Sdh155122 bzero((char *)ifs->ifs_nat_rules, 20833448Sdh155122 sizeof(*ifs->ifs_nat_rules) * ifs->ifs_ipf_natrules_sz); 20843448Sdh155122 if (ifs->ifs_rdr_rules != NULL) 20853448Sdh155122 bzero((char *)ifs->ifs_rdr_rules, 20863448Sdh155122 sizeof(*ifs->ifs_rdr_rules) * ifs->ifs_ipf_rdrrules_sz); 20872393Syz155240 20882393Syz155240 while ((n = *np) != NULL) { 20892393Syz155240 *np = n->in_next; 20902393Syz155240 if (n->in_use == 0) { 20912393Syz155240 if (n->in_apr != NULL) 20922393Syz155240 appr_free(n->in_apr); 20932393Syz155240 KFREE(n); 20943448Sdh155122 ifs->ifs_nat_stats.ns_rules--; 20952393Syz155240 } else { 20962393Syz155240 n->in_flags |= IPN_DELETE; 20972393Syz155240 n->in_next = NULL; 20982393Syz155240 } 20992393Syz155240 i++; 21002393Syz155240 } 21013448Sdh155122 ifs->ifs_nat_masks = 0; 21023448Sdh155122 ifs->ifs_rdr_masks = 0; 21037176Syx160601 for (i = 0; i < 4; i++) { 21047176Syx160601 ifs->ifs_nat6_masks[i] = 0; 21057176Syx160601 ifs->ifs_rdr6_masks[i] = 0; 21067176Syx160601 } 21072393Syz155240 return i; 21082393Syz155240 } 21092393Syz155240 21102393Syz155240 21112393Syz155240 /* ------------------------------------------------------------------------ */ 21122393Syz155240 /* Function: nat_newmap */ 21132393Syz155240 /* Returns: int - -1 == error, 0 == success */ 21142393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 21152393Syz155240 /* nat(I) - pointer to NAT entry */ 21162393Syz155240 /* ni(I) - pointer to structure with misc. information needed */ 21172393Syz155240 /* to create new NAT entry. */ 21182393Syz155240 /* */ 21192393Syz155240 /* Given an empty NAT structure, populate it with new information about a */ 21202393Syz155240 /* new NAT session, as defined by the matching NAT rule. */ 21212393Syz155240 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 21222393Syz155240 /* to the new IP address for the translation. */ 21232393Syz155240 /* ------------------------------------------------------------------------ */ 21242393Syz155240 static INLINE int nat_newmap(fin, nat, ni) 21252393Syz155240 fr_info_t *fin; 21262393Syz155240 nat_t *nat; 21272393Syz155240 natinfo_t *ni; 21282393Syz155240 { 21292393Syz155240 u_short st_port, dport, sport, port, sp, dp; 21302393Syz155240 struct in_addr in, inb; 21312393Syz155240 hostmap_t *hm; 21322393Syz155240 u_32_t flags; 21332393Syz155240 u_32_t st_ip; 21342393Syz155240 ipnat_t *np; 21352393Syz155240 nat_t *natl; 21362393Syz155240 int l; 21373448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 21382393Syz155240 21392393Syz155240 /* 21402393Syz155240 * If it's an outbound packet which doesn't match any existing 21412393Syz155240 * record, then create a new port 21422393Syz155240 */ 21432393Syz155240 l = 0; 21442393Syz155240 hm = NULL; 21452393Syz155240 np = ni->nai_np; 21462393Syz155240 st_ip = np->in_nip; 21472393Syz155240 st_port = np->in_pnext; 21482393Syz155240 flags = ni->nai_flags; 21492393Syz155240 sport = ni->nai_sport; 21502393Syz155240 dport = ni->nai_dport; 21512393Syz155240 21522393Syz155240 /* 21532393Syz155240 * Do a loop until we either run out of entries to try or we find 21542393Syz155240 * a NAT mapping that isn't currently being used. This is done 21552393Syz155240 * because the change to the source is not (usually) being fixed. 21562393Syz155240 */ 21572393Syz155240 do { 21582393Syz155240 port = 0; 21592393Syz155240 in.s_addr = htonl(np->in_nip); 21602393Syz155240 if (l == 0) { 21612393Syz155240 /* 21622393Syz155240 * Check to see if there is an existing NAT 21632393Syz155240 * setup for this IP address pair. 21642393Syz155240 */ 21652393Syz155240 hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, 21663448Sdh155122 in, 0, ifs); 21672393Syz155240 if (hm != NULL) 21682393Syz155240 in.s_addr = hm->hm_mapip.s_addr; 21692393Syz155240 } else if ((l == 1) && (hm != NULL)) { 21705417Sjojemann fr_hostmapdel(&hm); 21712393Syz155240 } 21722393Syz155240 in.s_addr = ntohl(in.s_addr); 21732393Syz155240 21742393Syz155240 nat->nat_hm = hm; 21752393Syz155240 21762393Syz155240 if ((np->in_outmsk == 0xffffffff) && (np->in_pnext == 0)) { 21772393Syz155240 if (l > 0) 21782393Syz155240 return -1; 21792393Syz155240 } 21802393Syz155240 21812393Syz155240 if (np->in_redir == NAT_BIMAP && 21822393Syz155240 np->in_inmsk == np->in_outmsk) { 21832393Syz155240 /* 21842393Syz155240 * map the address block in a 1:1 fashion 21852393Syz155240 */ 21862393Syz155240 in.s_addr = np->in_outip; 21872393Syz155240 in.s_addr |= fin->fin_saddr & ~np->in_inmsk; 21882393Syz155240 in.s_addr = ntohl(in.s_addr); 21892393Syz155240 21902393Syz155240 } else if (np->in_redir & NAT_MAPBLK) { 21912393Syz155240 if ((l >= np->in_ppip) || ((l > 0) && 21922393Syz155240 !(flags & IPN_TCPUDP))) 21932393Syz155240 return -1; 21942393Syz155240 /* 21952393Syz155240 * map-block - Calculate destination address. 21962393Syz155240 */ 21972393Syz155240 in.s_addr = ntohl(fin->fin_saddr); 21982393Syz155240 in.s_addr &= ntohl(~np->in_inmsk); 21992393Syz155240 inb.s_addr = in.s_addr; 22002393Syz155240 in.s_addr /= np->in_ippip; 22012393Syz155240 in.s_addr &= ntohl(~np->in_outmsk); 22022393Syz155240 in.s_addr += ntohl(np->in_outip); 22032393Syz155240 /* 22042393Syz155240 * Calculate destination port. 22052393Syz155240 */ 22062393Syz155240 if ((flags & IPN_TCPUDP) && 22072393Syz155240 (np->in_ppip != 0)) { 22082393Syz155240 port = ntohs(sport) + l; 22092393Syz155240 port %= np->in_ppip; 22102393Syz155240 port += np->in_ppip * 22112393Syz155240 (inb.s_addr % np->in_ippip); 22122393Syz155240 port += MAPBLK_MINPORT; 22132393Syz155240 port = htons(port); 22142393Syz155240 } 22152393Syz155240 22162393Syz155240 } else if ((np->in_outip == 0) && 22172393Syz155240 (np->in_outmsk == 0xffffffff)) { 22182393Syz155240 /* 22192393Syz155240 * 0/32 - use the interface's IP address. 22202393Syz155240 */ 22212393Syz155240 if ((l > 0) || 22222393Syz155240 fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp, 22233448Sdh155122 &in, NULL, fin->fin_ifs) == -1) 22242393Syz155240 return -1; 22252393Syz155240 in.s_addr = ntohl(in.s_addr); 22262393Syz155240 22272393Syz155240 } else if ((np->in_outip == 0) && (np->in_outmsk == 0)) { 22282393Syz155240 /* 22292393Syz155240 * 0/0 - use the original source address/port. 22302393Syz155240 */ 22312393Syz155240 if (l > 0) 22322393Syz155240 return -1; 22332393Syz155240 in.s_addr = ntohl(fin->fin_saddr); 22342393Syz155240 22352393Syz155240 } else if ((np->in_outmsk != 0xffffffff) && 22362393Syz155240 (np->in_pnext == 0) && ((l > 0) || (hm == NULL))) 22372393Syz155240 np->in_nip++; 22382393Syz155240 22392393Syz155240 natl = NULL; 22402393Syz155240 22412393Syz155240 if ((flags & IPN_TCPUDP) && 22422393Syz155240 ((np->in_redir & NAT_MAPBLK) == 0) && 22432393Syz155240 (np->in_flags & IPN_AUTOPORTMAP)) { 22442393Syz155240 /* 22452393Syz155240 * "ports auto" (without map-block) 22462393Syz155240 */ 22472393Syz155240 if ((l > 0) && (l % np->in_ppip == 0)) { 22482393Syz155240 if (l > np->in_space) { 22492393Syz155240 return -1; 22502393Syz155240 } else if ((l > np->in_ppip) && 22512393Syz155240 np->in_outmsk != 0xffffffff) 22522393Syz155240 np->in_nip++; 22532393Syz155240 } 22542393Syz155240 if (np->in_ppip != 0) { 22552393Syz155240 port = ntohs(sport); 22562393Syz155240 port += (l % np->in_ppip); 22572393Syz155240 port %= np->in_ppip; 22582393Syz155240 port += np->in_ppip * 22592393Syz155240 (ntohl(fin->fin_saddr) % 22602393Syz155240 np->in_ippip); 22612393Syz155240 port += MAPBLK_MINPORT; 22622393Syz155240 port = htons(port); 22632393Syz155240 } 22642393Syz155240 22652393Syz155240 } else if (((np->in_redir & NAT_MAPBLK) == 0) && 22662393Syz155240 (flags & IPN_TCPUDPICMP) && (np->in_pnext != 0)) { 22672393Syz155240 /* 22682393Syz155240 * Standard port translation. Select next port. 22692393Syz155240 */ 22707259Sdr146992 if (np->in_flags & IPN_SEQUENTIAL) { 22717259Sdr146992 port = np->in_pnext; 22727259Sdr146992 } else { 22737259Sdr146992 port = ipf_random() % (ntohs(np->in_pmax) - 22747259Sdr146992 ntohs(np->in_pmin)); 22757259Sdr146992 port += ntohs(np->in_pmin); 22767259Sdr146992 } 22777259Sdr146992 port = htons(port); 22787259Sdr146992 np->in_pnext++; 22792393Syz155240 22802393Syz155240 if (np->in_pnext > ntohs(np->in_pmax)) { 22812393Syz155240 np->in_pnext = ntohs(np->in_pmin); 22822393Syz155240 if (np->in_outmsk != 0xffffffff) 22832393Syz155240 np->in_nip++; 22842393Syz155240 } 22852393Syz155240 } 22862393Syz155240 22872393Syz155240 if (np->in_flags & IPN_IPRANGE) { 22882393Syz155240 if (np->in_nip > ntohl(np->in_outmsk)) 22892393Syz155240 np->in_nip = ntohl(np->in_outip); 22902393Syz155240 } else { 22912393Syz155240 if ((np->in_outmsk != 0xffffffff) && 22922393Syz155240 ((np->in_nip + 1) & ntohl(np->in_outmsk)) > 22932393Syz155240 ntohl(np->in_outip)) 22942393Syz155240 np->in_nip = ntohl(np->in_outip) + 1; 22952393Syz155240 } 22962393Syz155240 22972393Syz155240 if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) 22982393Syz155240 port = sport; 22992393Syz155240 23002393Syz155240 /* 23012393Syz155240 * Here we do a lookup of the connection as seen from 23022393Syz155240 * the outside. If an IP# pair already exists, try 23032393Syz155240 * again. So if you have A->B becomes C->B, you can 23042393Syz155240 * also have D->E become C->E but not D->B causing 23052393Syz155240 * another C->B. Also take protocol and ports into 23062393Syz155240 * account when determining whether a pre-existing 23072393Syz155240 * NAT setup will cause an external conflict where 23082393Syz155240 * this is appropriate. 23092393Syz155240 */ 23102393Syz155240 inb.s_addr = htonl(in.s_addr); 23112393Syz155240 sp = fin->fin_data[0]; 23122393Syz155240 dp = fin->fin_data[1]; 23132393Syz155240 fin->fin_data[0] = fin->fin_data[1]; 23142393Syz155240 fin->fin_data[1] = htons(port); 23152393Syz155240 natl = nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 23162393Syz155240 (u_int)fin->fin_p, fin->fin_dst, inb); 23172393Syz155240 fin->fin_data[0] = sp; 23182393Syz155240 fin->fin_data[1] = dp; 23192393Syz155240 23202393Syz155240 /* 23212393Syz155240 * Has the search wrapped around and come back to the 23222393Syz155240 * start ? 23232393Syz155240 */ 23242393Syz155240 if ((natl != NULL) && 23252393Syz155240 (np->in_pnext != 0) && (st_port == np->in_pnext) && 23262393Syz155240 (np->in_nip != 0) && (st_ip == np->in_nip)) 23272393Syz155240 return -1; 23282393Syz155240 l++; 23292393Syz155240 } while (natl != NULL); 23302393Syz155240 23312393Syz155240 if (np->in_space > 0) 23322393Syz155240 np->in_space--; 23332393Syz155240 23342393Syz155240 /* Setup the NAT table */ 23352393Syz155240 nat->nat_inip = fin->fin_src; 23362393Syz155240 nat->nat_outip.s_addr = htonl(in.s_addr); 23372393Syz155240 nat->nat_oip = fin->fin_dst; 23382393Syz155240 if (nat->nat_hm == NULL) 23392393Syz155240 nat->nat_hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, 23403448Sdh155122 nat->nat_outip, 0, ifs); 23412393Syz155240 23422393Syz155240 if (flags & IPN_TCPUDP) { 23432393Syz155240 nat->nat_inport = sport; 23442393Syz155240 nat->nat_outport = port; /* sport */ 23452393Syz155240 nat->nat_oport = dport; 23462393Syz155240 ((tcphdr_t *)fin->fin_dp)->th_sport = port; 23472393Syz155240 } else if (flags & IPN_ICMPQUERY) { 23482393Syz155240 ((icmphdr_t *)fin->fin_dp)->icmp_id = port; 23492393Syz155240 nat->nat_inport = port; 23502393Syz155240 nat->nat_outport = port; 23512393Syz155240 } 23522393Syz155240 23532393Syz155240 ni->nai_ip.s_addr = in.s_addr; 23542393Syz155240 ni->nai_port = port; 23552393Syz155240 ni->nai_nport = dport; 23562393Syz155240 return 0; 23572393Syz155240 } 23582393Syz155240 23592393Syz155240 23602393Syz155240 /* ------------------------------------------------------------------------ */ 23612393Syz155240 /* Function: nat_newrdr */ 23622393Syz155240 /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ 23632393Syz155240 /* allow rule to be moved if IPN_ROUNDR is set. */ 23642393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 23652393Syz155240 /* nat(I) - pointer to NAT entry */ 23662393Syz155240 /* ni(I) - pointer to structure with misc. information needed */ 23672393Syz155240 /* to create new NAT entry. */ 23682393Syz155240 /* */ 23692393Syz155240 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 23702393Syz155240 /* to the new IP address for the translation. */ 23712393Syz155240 /* ------------------------------------------------------------------------ */ 23722393Syz155240 static INLINE int nat_newrdr(fin, nat, ni) 23732393Syz155240 fr_info_t *fin; 23742393Syz155240 nat_t *nat; 23752393Syz155240 natinfo_t *ni; 23762393Syz155240 { 23772393Syz155240 u_short nport, dport, sport; 23785828Sjojemann struct in_addr in, inb; 23795828Sjojemann u_short sp, dp; 23802393Syz155240 hostmap_t *hm; 23812393Syz155240 u_32_t flags; 23822393Syz155240 ipnat_t *np; 23835828Sjojemann nat_t *natl; 23842393Syz155240 int move; 23853448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 23862393Syz155240 23872393Syz155240 move = 1; 23882393Syz155240 hm = NULL; 23892393Syz155240 in.s_addr = 0; 23902393Syz155240 np = ni->nai_np; 23912393Syz155240 flags = ni->nai_flags; 23922393Syz155240 sport = ni->nai_sport; 23932393Syz155240 dport = ni->nai_dport; 23942393Syz155240 23952393Syz155240 /* 23962393Syz155240 * If the matching rule has IPN_STICKY set, then we want to have the 23972393Syz155240 * same rule kick in as before. Why would this happen? If you have 23982393Syz155240 * a collection of rdr rules with "round-robin sticky", the current 23992393Syz155240 * packet might match a different one to the previous connection but 24002393Syz155240 * we want the same destination to be used. 24012393Syz155240 */ 24022393Syz155240 if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == 24032393Syz155240 (IPN_ROUNDR|IPN_STICKY)) { 24042393Syz155240 hm = nat_hostmap(NULL, fin->fin_src, fin->fin_dst, in, 24053448Sdh155122 (u_32_t)dport, ifs); 24062393Syz155240 if (hm != NULL) { 24072393Syz155240 in.s_addr = ntohl(hm->hm_mapip.s_addr); 24082393Syz155240 np = hm->hm_ipnat; 24092393Syz155240 ni->nai_np = np; 24102393Syz155240 move = 0; 24112393Syz155240 } 24122393Syz155240 } 24132393Syz155240 24142393Syz155240 /* 24152393Syz155240 * Otherwise, it's an inbound packet. Most likely, we don't 24162393Syz155240 * want to rewrite source ports and source addresses. Instead, 24172393Syz155240 * we want to rewrite to a fixed internal address and fixed 24182393Syz155240 * internal port. 24192393Syz155240 */ 24202393Syz155240 if (np->in_flags & IPN_SPLIT) { 24212393Syz155240 in.s_addr = np->in_nip; 24222393Syz155240 24232393Syz155240 if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { 24242393Syz155240 hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, 24253448Sdh155122 in, (u_32_t)dport, ifs); 24262393Syz155240 if (hm != NULL) { 24272393Syz155240 in.s_addr = hm->hm_mapip.s_addr; 24282393Syz155240 move = 0; 24292393Syz155240 } 24302393Syz155240 } 24312393Syz155240 24322393Syz155240 if (hm == NULL || hm->hm_ref == 1) { 24332393Syz155240 if (np->in_inip == htonl(in.s_addr)) { 24342393Syz155240 np->in_nip = ntohl(np->in_inmsk); 24352393Syz155240 move = 0; 24362393Syz155240 } else { 24372393Syz155240 np->in_nip = ntohl(np->in_inip); 24382393Syz155240 } 24392393Syz155240 } 24402393Syz155240 24412393Syz155240 } else if ((np->in_inip == 0) && (np->in_inmsk == 0xffffffff)) { 24422393Syz155240 /* 24432393Syz155240 * 0/32 - use the interface's IP address. 24442393Syz155240 */ 24453448Sdh155122 if (fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp, &in, NULL, 24463448Sdh155122 fin->fin_ifs) == -1) 24472393Syz155240 return -1; 24482393Syz155240 in.s_addr = ntohl(in.s_addr); 24492393Syz155240 24502393Syz155240 } else if ((np->in_inip == 0) && (np->in_inmsk== 0)) { 24512393Syz155240 /* 24522393Syz155240 * 0/0 - use the original destination address/port. 24532393Syz155240 */ 24542393Syz155240 in.s_addr = ntohl(fin->fin_daddr); 24552393Syz155240 24562393Syz155240 } else if (np->in_redir == NAT_BIMAP && 24572393Syz155240 np->in_inmsk == np->in_outmsk) { 24582393Syz155240 /* 24592393Syz155240 * map the address block in a 1:1 fashion 24602393Syz155240 */ 24612393Syz155240 in.s_addr = np->in_inip; 24622393Syz155240 in.s_addr |= fin->fin_daddr & ~np->in_inmsk; 24632393Syz155240 in.s_addr = ntohl(in.s_addr); 24642393Syz155240 } else { 24652393Syz155240 in.s_addr = ntohl(np->in_inip); 24662393Syz155240 } 24672393Syz155240 24682393Syz155240 if ((np->in_pnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) 24692393Syz155240 nport = dport; 24702393Syz155240 else { 24712393Syz155240 /* 24722393Syz155240 * Whilst not optimized for the case where 24732393Syz155240 * pmin == pmax, the gain is not significant. 24742393Syz155240 */ 24752393Syz155240 if (((np->in_flags & IPN_FIXEDDPORT) == 0) && 24762393Syz155240 (np->in_pmin != np->in_pmax)) { 24772393Syz155240 nport = ntohs(dport) - ntohs(np->in_pmin) + 24782393Syz155240 ntohs(np->in_pnext); 24792393Syz155240 nport = htons(nport); 24802393Syz155240 } else 24812393Syz155240 nport = np->in_pnext; 24822393Syz155240 } 24832393Syz155240 24842393Syz155240 /* 24852393Syz155240 * When the redirect-to address is set to 0.0.0.0, just 24862393Syz155240 * assume a blank `forwarding' of the packet. We don't 24872393Syz155240 * setup any translation for this either. 24882393Syz155240 */ 24892393Syz155240 if (in.s_addr == 0) { 24902393Syz155240 if (nport == dport) 24912393Syz155240 return -1; 24922393Syz155240 in.s_addr = ntohl(fin->fin_daddr); 24932393Syz155240 } 24942393Syz155240 24955828Sjojemann /* 24965828Sjojemann * Check to see if this redirect mapping already exists and if 24975828Sjojemann * it does, return "failure" (allowing it to be created will just 24985828Sjojemann * cause one or both of these "connections" to stop working.) 24995828Sjojemann */ 25005828Sjojemann inb.s_addr = htonl(in.s_addr); 25015828Sjojemann sp = fin->fin_data[0]; 25025828Sjojemann dp = fin->fin_data[1]; 25035828Sjojemann fin->fin_data[1] = fin->fin_data[0]; 25045828Sjojemann fin->fin_data[0] = ntohs(nport); 25055828Sjojemann natl = nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 25065828Sjojemann (u_int)fin->fin_p, inb, fin->fin_src); 25075828Sjojemann fin->fin_data[0] = sp; 25085828Sjojemann fin->fin_data[1] = dp; 25095828Sjojemann if (natl != NULL) 25105828Sjojemann return (-1); 25115828Sjojemann 25122393Syz155240 nat->nat_inip.s_addr = htonl(in.s_addr); 25132393Syz155240 nat->nat_outip = fin->fin_dst; 25142393Syz155240 nat->nat_oip = fin->fin_src; 25152393Syz155240 25162393Syz155240 ni->nai_ip.s_addr = in.s_addr; 25172393Syz155240 ni->nai_nport = nport; 25182393Syz155240 ni->nai_port = sport; 25192393Syz155240 25202393Syz155240 if (flags & IPN_TCPUDP) { 25212393Syz155240 nat->nat_inport = nport; 25222393Syz155240 nat->nat_outport = dport; 25232393Syz155240 nat->nat_oport = sport; 25242393Syz155240 ((tcphdr_t *)fin->fin_dp)->th_dport = nport; 25252393Syz155240 } else if (flags & IPN_ICMPQUERY) { 25262393Syz155240 ((icmphdr_t *)fin->fin_dp)->icmp_id = nport; 25272393Syz155240 nat->nat_inport = nport; 25282393Syz155240 nat->nat_outport = nport; 25292393Syz155240 } 25302393Syz155240 25312393Syz155240 return move; 25322393Syz155240 } 25332393Syz155240 25342393Syz155240 /* ------------------------------------------------------------------------ */ 25352393Syz155240 /* Function: nat_new */ 25362393Syz155240 /* Returns: nat_t* - NULL == failure to create new NAT structure, */ 25372393Syz155240 /* else pointer to new NAT structure */ 25382393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 25392393Syz155240 /* np(I) - pointer to NAT rule */ 25402393Syz155240 /* natsave(I) - pointer to where to store NAT struct pointer */ 25412393Syz155240 /* flags(I) - flags describing the current packet */ 25422393Syz155240 /* direction(I) - direction of packet (in/out) */ 25432393Syz155240 /* Write Lock: ipf_nat */ 25442393Syz155240 /* */ 25452393Syz155240 /* Attempts to create a new NAT entry. Does not actually change the packet */ 25462393Syz155240 /* in any way. */ 25472393Syz155240 /* */ 25482393Syz155240 /* This fucntion is in three main parts: (1) deal with creating a new NAT */ 25492393Syz155240 /* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */ 25502393Syz155240 /* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */ 25512393Syz155240 /* and (3) building that structure and putting it into the NAT table(s). */ 25522393Syz155240 /* ------------------------------------------------------------------------ */ 25532393Syz155240 nat_t *nat_new(fin, np, natsave, flags, direction) 25542393Syz155240 fr_info_t *fin; 25552393Syz155240 ipnat_t *np; 25562393Syz155240 nat_t **natsave; 25572393Syz155240 u_int flags; 25582393Syz155240 int direction; 25592393Syz155240 { 25602393Syz155240 tcphdr_t *tcp = NULL; 25612393Syz155240 hostmap_t *hm = NULL; 25622393Syz155240 nat_t *nat, *natl; 25632393Syz155240 u_int nflags; 25642393Syz155240 natinfo_t ni; 25652393Syz155240 int move; 25663448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 25673448Sdh155122 25684817San207044 /* 25694817San207044 * Trigger automatic call to nat_extraflush() if the 25704817San207044 * table has reached capcity specified by hi watermark. 25714817San207044 */ 25724817San207044 if (NAT_TAB_WATER_LEVEL(ifs) > ifs->ifs_nat_flush_lvl_hi) 25734817San207044 ifs->ifs_nat_doflush = 1; 25744817San207044 2575*7432SJohn.Ojemann@Sun.COM /* 2576*7432SJohn.Ojemann@Sun.COM * If automatic flushing did not do its job, and the table 2577*7432SJohn.Ojemann@Sun.COM * has filled up, don't try to create a new entry. 2578*7432SJohn.Ojemann@Sun.COM */ 25793448Sdh155122 if (ifs->ifs_nat_stats.ns_inuse >= ifs->ifs_ipf_nattable_max) { 25803448Sdh155122 ifs->ifs_nat_stats.ns_memfail++; 25812393Syz155240 return NULL; 25822393Syz155240 } 25832393Syz155240 25842393Syz155240 move = 1; 25852393Syz155240 nflags = np->in_flags & flags; 25862393Syz155240 nflags &= NAT_FROMRULE; 25872393Syz155240 25882393Syz155240 ni.nai_np = np; 25892393Syz155240 ni.nai_nflags = nflags; 25902393Syz155240 ni.nai_flags = flags; 25912393Syz155240 25922393Syz155240 /* Give me a new nat */ 25932393Syz155240 KMALLOC(nat, nat_t *); 25942393Syz155240 if (nat == NULL) { 25953448Sdh155122 ifs->ifs_nat_stats.ns_memfail++; 25962393Syz155240 /* 25972393Syz155240 * Try to automatically tune the max # of entries in the 25982393Syz155240 * table allowed to be less than what will cause kmem_alloc() 25992393Syz155240 * to fail and try to eliminate panics due to out of memory 26002393Syz155240 * conditions arising. 26012393Syz155240 */ 26023448Sdh155122 if (ifs->ifs_ipf_nattable_max > ifs->ifs_ipf_nattable_sz) { 26033448Sdh155122 ifs->ifs_ipf_nattable_max = ifs->ifs_nat_stats.ns_inuse - 100; 26042393Syz155240 printf("ipf_nattable_max reduced to %d\n", 26053448Sdh155122 ifs->ifs_ipf_nattable_max); 26062393Syz155240 } 26072393Syz155240 return NULL; 26082393Syz155240 } 26092393Syz155240 26102393Syz155240 if (flags & IPN_TCPUDP) { 26112393Syz155240 tcp = fin->fin_dp; 26122393Syz155240 ni.nai_sport = htons(fin->fin_sport); 26132393Syz155240 ni.nai_dport = htons(fin->fin_dport); 26142393Syz155240 } else if (flags & IPN_ICMPQUERY) { 26152393Syz155240 /* 26162393Syz155240 * In the ICMP query NAT code, we translate the ICMP id fields 26172393Syz155240 * to make them unique. This is indepedent of the ICMP type 26182393Syz155240 * (e.g. in the unlikely event that a host sends an echo and 26192393Syz155240 * an tstamp request with the same id, both packets will have 26202393Syz155240 * their ip address/id field changed in the same way). 26212393Syz155240 */ 26222393Syz155240 /* The icmp_id field is used by the sender to identify the 26232393Syz155240 * process making the icmp request. (the receiver justs 26242393Syz155240 * copies it back in its response). So, it closely matches 26252393Syz155240 * the concept of source port. We overlay sport, so we can 26262393Syz155240 * maximally reuse the existing code. 26272393Syz155240 */ 26282393Syz155240 ni.nai_sport = ((icmphdr_t *)fin->fin_dp)->icmp_id; 26292393Syz155240 ni.nai_dport = ni.nai_sport; 26302393Syz155240 } 26312393Syz155240 26322393Syz155240 bzero((char *)nat, sizeof(*nat)); 26332393Syz155240 nat->nat_flags = flags; 26343448Sdh155122 nat->nat_redir = np->in_redir; 26352393Syz155240 26362393Syz155240 if ((flags & NAT_SLAVE) == 0) { 26373448Sdh155122 MUTEX_ENTER(&ifs->ifs_ipf_nat_new); 26382393Syz155240 } 26392393Syz155240 26402393Syz155240 /* 26412393Syz155240 * Search the current table for a match. 26422393Syz155240 */ 26432393Syz155240 if (direction == NAT_OUTBOUND) { 26442393Syz155240 /* 26452393Syz155240 * We can now arrange to call this for the same connection 26462393Syz155240 * because ipf_nat_new doesn't protect the code path into 26472393Syz155240 * this function. 26482393Syz155240 */ 26492393Syz155240 natl = nat_outlookup(fin, nflags, (u_int)fin->fin_p, 26502393Syz155240 fin->fin_src, fin->fin_dst); 26512393Syz155240 if (natl != NULL) { 26524380Sjojemann KFREE(nat); 26532393Syz155240 nat = natl; 26542393Syz155240 goto done; 26552393Syz155240 } 26562393Syz155240 26572393Syz155240 move = nat_newmap(fin, nat, &ni); 26582393Syz155240 if (move == -1) 26592393Syz155240 goto badnat; 26602393Syz155240 26612393Syz155240 np = ni.nai_np; 26622393Syz155240 } else { 26632393Syz155240 /* 26642393Syz155240 * NAT_INBOUND is used only for redirects rules 26652393Syz155240 */ 26662393Syz155240 natl = nat_inlookup(fin, nflags, (u_int)fin->fin_p, 26672393Syz155240 fin->fin_src, fin->fin_dst); 26682393Syz155240 if (natl != NULL) { 26694380Sjojemann KFREE(nat); 26702393Syz155240 nat = natl; 26712393Syz155240 goto done; 26722393Syz155240 } 26732393Syz155240 26742393Syz155240 move = nat_newrdr(fin, nat, &ni); 26752393Syz155240 if (move == -1) 26762393Syz155240 goto badnat; 26772393Syz155240 26782393Syz155240 np = ni.nai_np; 26792393Syz155240 } 26802393Syz155240 26812393Syz155240 if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { 26822393Syz155240 if (np->in_redir == NAT_REDIRECT) { 26832393Syz155240 nat_delrdr(np); 26843448Sdh155122 nat_addrdr(np, ifs); 26852393Syz155240 } else if (np->in_redir == NAT_MAP) { 26862393Syz155240 nat_delnat(np); 26873448Sdh155122 nat_addnat(np, ifs); 26882393Syz155240 } 26892393Syz155240 } 26902393Syz155240 26912393Syz155240 if (nat_finalise(fin, nat, &ni, tcp, natsave, direction) == -1) { 26922393Syz155240 goto badnat; 26932393Syz155240 } 26946253San207044 26956253San207044 nat_calc_chksum_diffs(nat); 26966253San207044 26972393Syz155240 if (flags & SI_WILDP) 26983448Sdh155122 ifs->ifs_nat_stats.ns_wilds++; 2699*7432SJohn.Ojemann@Sun.COM fin->fin_flx |= FI_NEWNAT; 27002393Syz155240 goto done; 27012393Syz155240 badnat: 27023448Sdh155122 ifs->ifs_nat_stats.ns_badnat++; 27032393Syz155240 if ((hm = nat->nat_hm) != NULL) 27045417Sjojemann fr_hostmapdel(&hm); 27052393Syz155240 KFREE(nat); 27062393Syz155240 nat = NULL; 27072393Syz155240 done: 27082393Syz155240 if ((flags & NAT_SLAVE) == 0) { 27093448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_nat_new); 27102393Syz155240 } 27112393Syz155240 return nat; 27122393Syz155240 } 27132393Syz155240 27142393Syz155240 27152393Syz155240 /* ------------------------------------------------------------------------ */ 27162393Syz155240 /* Function: nat_finalise */ 27172393Syz155240 /* Returns: int - 0 == sucess, -1 == failure */ 27182393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 27192393Syz155240 /* nat(I) - pointer to NAT entry */ 27202393Syz155240 /* ni(I) - pointer to structure with misc. information needed */ 27212393Syz155240 /* to create new NAT entry. */ 27222393Syz155240 /* Write Lock: ipf_nat */ 27232393Syz155240 /* */ 27242393Syz155240 /* This is the tail end of constructing a new NAT entry and is the same */ 27252393Syz155240 /* for both IPv4 and IPv6. */ 27262393Syz155240 /* ------------------------------------------------------------------------ */ 27272393Syz155240 /*ARGSUSED*/ 27282393Syz155240 static INLINE int nat_finalise(fin, nat, ni, tcp, natsave, direction) 27292393Syz155240 fr_info_t *fin; 27302393Syz155240 nat_t *nat; 27312393Syz155240 natinfo_t *ni; 27322393Syz155240 tcphdr_t *tcp; 27332393Syz155240 nat_t **natsave; 27342393Syz155240 int direction; 27352393Syz155240 { 27362393Syz155240 frentry_t *fr; 27372393Syz155240 ipnat_t *np; 27383448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 27392393Syz155240 27402393Syz155240 np = ni->nai_np; 27412393Syz155240 27422958Sdr146992 COPYIFNAME(fin->fin_ifp, nat->nat_ifnames[0], fin->fin_v); 27432958Sdr146992 27442393Syz155240 #ifdef IPFILTER_SYNC 27452393Syz155240 if ((nat->nat_flags & SI_CLONE) == 0) 27462393Syz155240 nat->nat_sync = ipfsync_new(SMC_NAT, fin, nat); 27472393Syz155240 #endif 27482393Syz155240 27492393Syz155240 nat->nat_me = natsave; 27502393Syz155240 nat->nat_dir = direction; 27512508Syz155240 nat->nat_ifps[0] = np->in_ifps[0]; 27522508Syz155240 nat->nat_ifps[1] = np->in_ifps[1]; 27532393Syz155240 nat->nat_ptr = np; 27542393Syz155240 nat->nat_p = fin->fin_p; 27557176Syx160601 nat->nat_v = fin->fin_v; 27562393Syz155240 nat->nat_mssclamp = np->in_mssclamp; 27572393Syz155240 fr = fin->fin_fr; 27582393Syz155240 nat->nat_fr = fr; 27592393Syz155240 27602393Syz155240 if ((np->in_apr != NULL) && ((ni->nai_flags & NAT_SLAVE) == 0)) 27612393Syz155240 if (appr_new(fin, nat) == -1) 27622393Syz155240 return -1; 27632393Syz155240 27643448Sdh155122 if (nat_insert(nat, fin->fin_rev, ifs) == 0) { 27653448Sdh155122 if (ifs->ifs_nat_logging) 27663448Sdh155122 nat_log(nat, (u_int)np->in_redir, ifs); 27672393Syz155240 np->in_use++; 27682393Syz155240 if (fr != NULL) { 27692393Syz155240 MUTEX_ENTER(&fr->fr_lock); 27702393Syz155240 fr->fr_ref++; 27712393Syz155240 MUTEX_EXIT(&fr->fr_lock); 27722393Syz155240 } 27732393Syz155240 return 0; 27742393Syz155240 } 27752393Syz155240 27762393Syz155240 /* 27772393Syz155240 * nat_insert failed, so cleanup time... 27782393Syz155240 */ 27792393Syz155240 return -1; 27802393Syz155240 } 27812393Syz155240 27822393Syz155240 27832393Syz155240 /* ------------------------------------------------------------------------ */ 27842393Syz155240 /* Function: nat_insert */ 27852393Syz155240 /* Returns: int - 0 == sucess, -1 == failure */ 27862393Syz155240 /* Parameters: nat(I) - pointer to NAT structure */ 27872393Syz155240 /* rev(I) - flag indicating forward/reverse direction of packet */ 27882393Syz155240 /* Write Lock: ipf_nat */ 27892393Syz155240 /* */ 27902393Syz155240 /* Insert a NAT entry into the hash tables for searching and add it to the */ 27912393Syz155240 /* list of active NAT entries. Adjust global counters when complete. */ 27922393Syz155240 /* ------------------------------------------------------------------------ */ 27933448Sdh155122 int nat_insert(nat, rev, ifs) 27942393Syz155240 nat_t *nat; 27952393Syz155240 int rev; 27963448Sdh155122 ipf_stack_t *ifs; 27972393Syz155240 { 27982393Syz155240 u_int hv1, hv2; 27992393Syz155240 nat_t **natp; 28002393Syz155240 28012393Syz155240 /* 28022393Syz155240 * Try and return an error as early as possible, so calculate the hash 28032393Syz155240 * entry numbers first and then proceed. 28042393Syz155240 */ 28052393Syz155240 if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { 28062393Syz155240 hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 28072393Syz155240 0xffffffff); 28082393Syz155240 hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1 + nat->nat_oport, 28093448Sdh155122 ifs->ifs_ipf_nattable_sz); 28102393Syz155240 hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, 28112393Syz155240 0xffffffff); 28122393Syz155240 hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2 + nat->nat_oport, 28133448Sdh155122 ifs->ifs_ipf_nattable_sz); 28142393Syz155240 } else { 28152393Syz155240 hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, 0, 0xffffffff); 28163448Sdh155122 hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1, 28173448Sdh155122 ifs->ifs_ipf_nattable_sz); 28182393Syz155240 hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, 0, 0xffffffff); 28193448Sdh155122 hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2, 28203448Sdh155122 ifs->ifs_ipf_nattable_sz); 28212393Syz155240 } 28222393Syz155240 28233448Sdh155122 if (ifs->ifs_nat_stats.ns_bucketlen[0][hv1] >= ifs->ifs_fr_nat_maxbucket || 28243448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[1][hv2] >= ifs->ifs_fr_nat_maxbucket) { 28252393Syz155240 return -1; 28262393Syz155240 } 28272393Syz155240 28282393Syz155240 nat->nat_hv[0] = hv1; 28292393Syz155240 nat->nat_hv[1] = hv2; 28302393Syz155240 28312393Syz155240 MUTEX_INIT(&nat->nat_lock, "nat entry lock"); 28322393Syz155240 28332393Syz155240 nat->nat_rev = rev; 28342393Syz155240 nat->nat_ref = 1; 28352393Syz155240 nat->nat_bytes[0] = 0; 28362393Syz155240 nat->nat_pkts[0] = 0; 28372393Syz155240 nat->nat_bytes[1] = 0; 28382393Syz155240 nat->nat_pkts[1] = 0; 28392393Syz155240 28402393Syz155240 nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; 28413448Sdh155122 nat->nat_ifps[0] = fr_resolvenic(nat->nat_ifnames[0], 4, ifs); 28422393Syz155240 28432393Syz155240 if (nat->nat_ifnames[1][0] !='\0') { 28442393Syz155240 nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 28453448Sdh155122 nat->nat_ifps[1] = fr_resolvenic(nat->nat_ifnames[1], 4, ifs); 28462393Syz155240 } else { 28472393Syz155240 (void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0], 28482393Syz155240 LIFNAMSIZ); 28492393Syz155240 nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 28502393Syz155240 nat->nat_ifps[1] = nat->nat_ifps[0]; 28512393Syz155240 } 28522393Syz155240 28533448Sdh155122 nat->nat_next = ifs->ifs_nat_instances; 28543448Sdh155122 nat->nat_pnext = &ifs->ifs_nat_instances; 28553448Sdh155122 if (ifs->ifs_nat_instances) 28563448Sdh155122 ifs->ifs_nat_instances->nat_pnext = &nat->nat_next; 28573448Sdh155122 ifs->ifs_nat_instances = nat; 28583448Sdh155122 28593448Sdh155122 natp = &ifs->ifs_nat_table[0][hv1]; 28602393Syz155240 if (*natp) 28612393Syz155240 (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 28622393Syz155240 nat->nat_phnext[0] = natp; 28632393Syz155240 nat->nat_hnext[0] = *natp; 28642393Syz155240 *natp = nat; 28653448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[0][hv1]++; 28663448Sdh155122 28673448Sdh155122 natp = &ifs->ifs_nat_table[1][hv2]; 28682393Syz155240 if (*natp) 28692393Syz155240 (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 28702393Syz155240 nat->nat_phnext[1] = natp; 28712393Syz155240 nat->nat_hnext[1] = *natp; 28722393Syz155240 *natp = nat; 28733448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[1][hv2]++; 28743448Sdh155122 28753448Sdh155122 fr_setnatqueue(nat, rev, ifs); 28763448Sdh155122 28773448Sdh155122 ifs->ifs_nat_stats.ns_added++; 28783448Sdh155122 ifs->ifs_nat_stats.ns_inuse++; 28792393Syz155240 return 0; 28802393Syz155240 } 28812393Syz155240 28822393Syz155240 28832393Syz155240 /* ------------------------------------------------------------------------ */ 28842393Syz155240 /* Function: nat_icmperrorlookup */ 28852393Syz155240 /* Returns: nat_t* - point to matching NAT structure */ 28862393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 28872393Syz155240 /* dir(I) - direction of packet (in/out) */ 28882393Syz155240 /* */ 28892393Syz155240 /* Check if the ICMP error message is related to an existing TCP, UDP or */ 28902393Syz155240 /* ICMP query nat entry. It is assumed that the packet is already of the */ 28912393Syz155240 /* the required length. */ 28922393Syz155240 /* ------------------------------------------------------------------------ */ 28932393Syz155240 nat_t *nat_icmperrorlookup(fin, dir) 28942393Syz155240 fr_info_t *fin; 28952393Syz155240 int dir; 28962393Syz155240 { 28972393Syz155240 int flags = 0, minlen; 28982393Syz155240 icmphdr_t *orgicmp; 28992393Syz155240 tcphdr_t *tcp = NULL; 29002393Syz155240 u_short data[2]; 29012393Syz155240 nat_t *nat; 29022393Syz155240 ip_t *oip; 29032393Syz155240 u_int p; 29042393Syz155240 29052393Syz155240 /* 29062393Syz155240 * Does it at least have the return (basic) IP header ? 29072393Syz155240 * Only a basic IP header (no options) should be with an ICMP error 29082393Syz155240 * header. Also, if it's not an error type, then return. 29092393Syz155240 */ 29102393Syz155240 if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) 29112393Syz155240 return NULL; 29122393Syz155240 29132393Syz155240 /* 29142393Syz155240 * Check packet size 29152393Syz155240 */ 29162393Syz155240 oip = (ip_t *)((char *)fin->fin_dp + 8); 29172393Syz155240 minlen = IP_HL(oip) << 2; 29182393Syz155240 if ((minlen < sizeof(ip_t)) || 29192393Syz155240 (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) 29202393Syz155240 return NULL; 29212393Syz155240 /* 29222393Syz155240 * Is the buffer big enough for all of it ? It's the size of the IP 29232393Syz155240 * header claimed in the encapsulated part which is of concern. It 29242393Syz155240 * may be too big to be in this buffer but not so big that it's 29252393Syz155240 * outside the ICMP packet, leading to TCP deref's causing problems. 29262393Syz155240 * This is possible because we don't know how big oip_hl is when we 29272393Syz155240 * do the pullup early in fr_check() and thus can't gaurantee it is 29282393Syz155240 * all here now. 29292393Syz155240 */ 29302393Syz155240 #ifdef _KERNEL 29312393Syz155240 { 29322393Syz155240 mb_t *m; 29332393Syz155240 29342393Syz155240 m = fin->fin_m; 29352393Syz155240 # if defined(MENTAT) 29362393Syz155240 if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr) 29372393Syz155240 return NULL; 29382393Syz155240 # else 29392393Syz155240 if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > 29402393Syz155240 (char *)fin->fin_ip + M_LEN(m)) 29412393Syz155240 return NULL; 29422393Syz155240 # endif 29432393Syz155240 } 29442393Syz155240 #endif 29452393Syz155240 29462393Syz155240 if (fin->fin_daddr != oip->ip_src.s_addr) 29472393Syz155240 return NULL; 29482393Syz155240 29492393Syz155240 p = oip->ip_p; 29502393Syz155240 if (p == IPPROTO_TCP) 29512393Syz155240 flags = IPN_TCP; 29522393Syz155240 else if (p == IPPROTO_UDP) 29532393Syz155240 flags = IPN_UDP; 29542393Syz155240 else if (p == IPPROTO_ICMP) { 29552393Syz155240 orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 29562393Syz155240 29572393Syz155240 /* see if this is related to an ICMP query */ 29582393Syz155240 if (nat_icmpquerytype4(orgicmp->icmp_type)) { 29592393Syz155240 data[0] = fin->fin_data[0]; 29602393Syz155240 data[1] = fin->fin_data[1]; 29612393Syz155240 fin->fin_data[0] = 0; 29622393Syz155240 fin->fin_data[1] = orgicmp->icmp_id; 29632393Syz155240 29642393Syz155240 flags = IPN_ICMPERR|IPN_ICMPQUERY; 29652393Syz155240 /* 29662393Syz155240 * NOTE : dir refers to the direction of the original 29672393Syz155240 * ip packet. By definition the icmp error 29682393Syz155240 * message flows in the opposite direction. 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 fin->fin_data[0] = data[0]; 29772393Syz155240 fin->fin_data[1] = data[1]; 29782393Syz155240 return nat; 29792393Syz155240 } 29802393Syz155240 } 29812393Syz155240 29822393Syz155240 if (flags & IPN_TCPUDP) { 29832393Syz155240 minlen += 8; /* + 64bits of data to get ports */ 29842393Syz155240 if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) 29852393Syz155240 return NULL; 29862393Syz155240 29872393Syz155240 data[0] = fin->fin_data[0]; 29882393Syz155240 data[1] = fin->fin_data[1]; 29892393Syz155240 tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 29902393Syz155240 fin->fin_data[0] = ntohs(tcp->th_dport); 29912393Syz155240 fin->fin_data[1] = ntohs(tcp->th_sport); 29922393Syz155240 29932393Syz155240 if (dir == NAT_INBOUND) { 29942393Syz155240 nat = nat_inlookup(fin, flags, p, oip->ip_dst, 29952393Syz155240 oip->ip_src); 29962393Syz155240 } else { 29972393Syz155240 nat = nat_outlookup(fin, flags, p, oip->ip_dst, 29982393Syz155240 oip->ip_src); 29992393Syz155240 } 30002393Syz155240 fin->fin_data[0] = data[0]; 30012393Syz155240 fin->fin_data[1] = data[1]; 30022393Syz155240 return nat; 30032393Syz155240 } 30042393Syz155240 if (dir == NAT_INBOUND) 30052393Syz155240 return nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src); 30062393Syz155240 else 30072393Syz155240 return nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src); 30082393Syz155240 } 30092393Syz155240 30102393Syz155240 30112393Syz155240 /* ------------------------------------------------------------------------ */ 30122393Syz155240 /* Function: nat_icmperror */ 30132393Syz155240 /* Returns: nat_t* - point to matching NAT structure */ 30142393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 30152393Syz155240 /* nflags(I) - NAT flags for this packet */ 30162393Syz155240 /* dir(I) - direction of packet (in/out) */ 30172393Syz155240 /* */ 30182393Syz155240 /* Fix up an ICMP packet which is an error message for an existing NAT */ 30192393Syz155240 /* session. This will correct both packet header data and checksums. */ 30202393Syz155240 /* */ 30212393Syz155240 /* This should *ONLY* be used for incoming ICMP error packets to make sure */ 30222393Syz155240 /* a NAT'd ICMP packet gets correctly recognised. */ 30232393Syz155240 /* ------------------------------------------------------------------------ */ 30242393Syz155240 nat_t *nat_icmperror(fin, nflags, dir) 30252393Syz155240 fr_info_t *fin; 30262393Syz155240 u_int *nflags; 30272393Syz155240 int dir; 30282393Syz155240 { 30292693Sjojemann u_32_t sum1, sum2, sumd, psum1, psum2, psumd, sumd2; 30302393Syz155240 struct in_addr in; 30312693Sjojemann icmphdr_t *icmp, *orgicmp; 30322693Sjojemann int dlen; 30332693Sjojemann udphdr_t *udp; 30342393Syz155240 tcphdr_t *tcp; 30352393Syz155240 nat_t *nat; 30362393Syz155240 ip_t *oip; 30372393Syz155240 if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) 30382393Syz155240 return NULL; 30392693Sjojemann 30402393Syz155240 /* 30412693Sjojemann * nat_icmperrorlookup() looks up nat entry associated with the 30422693Sjojemann * offending IP packet and returns pointer to the entry, or NULL 30432693Sjojemann * if packet wasn't natted or for `defective' packets. 30442393Syz155240 */ 30452693Sjojemann 30462393Syz155240 if ((fin->fin_v != 4) || !(nat = nat_icmperrorlookup(fin, dir))) 30472393Syz155240 return NULL; 30482393Syz155240 30492393Syz155240 sumd2 = 0; 30502393Syz155240 *nflags = IPN_ICMPERR; 30512393Syz155240 icmp = fin->fin_dp; 30522393Syz155240 oip = (ip_t *)&icmp->icmp_ip; 30532693Sjojemann udp = (udphdr_t *)((((char *)oip) + (IP_HL(oip) << 2))); 30542693Sjojemann tcp = (tcphdr_t *)udp; 30552693Sjojemann dlen = fin->fin_plen - ((char *)udp - (char *)fin->fin_ip); 30562393Syz155240 30572393Syz155240 /* 30582393Syz155240 * Need to adjust ICMP header to include the real IP#'s and 30592693Sjojemann * port #'s. There are three steps required. 30602693Sjojemann * 30612393Syz155240 * Step 1 30622693Sjojemann * Fix the IP addresses in the offending IP packet and update 30632693Sjojemann * ip header checksum to compensate for the change. 30642393Syz155240 * 30652693Sjojemann * No update needed here for icmp_cksum because the ICMP checksum 30662693Sjojemann * is calculated over the complete ICMP packet, which includes the 30672693Sjojemann * changed oip IP addresses and oip->ip_sum. These two changes 30682693Sjojemann * cancel each other out (if the delta for the IP address is x, 30692693Sjojemann * then the delta for ip_sum is minus x). 30702393Syz155240 */ 30712393Syz155240 30722393Syz155240 if (oip->ip_dst.s_addr == nat->nat_oip.s_addr) { 30732393Syz155240 sum1 = LONG_SUM(ntohl(oip->ip_src.s_addr)); 30742393Syz155240 in = nat->nat_inip; 30752393Syz155240 oip->ip_src = in; 30762393Syz155240 } else { 30772393Syz155240 sum1 = LONG_SUM(ntohl(oip->ip_dst.s_addr)); 30782393Syz155240 in = nat->nat_outip; 30792393Syz155240 oip->ip_dst = in; 30802393Syz155240 } 30812393Syz155240 30822393Syz155240 sum2 = LONG_SUM(ntohl(in.s_addr)); 30832393Syz155240 CALC_SUMD(sum1, sum2, sumd); 30842393Syz155240 fix_datacksum(&oip->ip_sum, sumd); 30852393Syz155240 30862393Syz155240 /* 30872693Sjojemann * Step 2 30882693Sjojemann * Perform other adjustments based on protocol of offending packet. 30892393Syz155240 */ 30902693Sjojemann 30912693Sjojemann switch (oip->ip_p) { 30922693Sjojemann case IPPROTO_TCP : 30932693Sjojemann case IPPROTO_UDP : 30942393Syz155240 30952393Syz155240 /* 30962693Sjojemann * For offending TCP/UDP IP packets, translate the ports 30972693Sjojemann * based on the NAT specification. 30982693Sjojemann * 30992693Sjojemann * Advance notice : Now it becomes complicated :-) 31002693Sjojemann * 31012693Sjojemann * Since the port and IP addresse fields are both part 31022693Sjojemann * of the TCP/UDP checksum of the offending IP packet, 31032693Sjojemann * we need to adjust that checksum as well. 31042693Sjojemann * 31052693Sjojemann * To further complicate things, the TCP/UDP checksum 31062693Sjojemann * may not be present. We must check to see if the 31072693Sjojemann * length of the data portion is big enough to hold 31082693Sjojemann * the checksum. In the UDP case, a test to determine 31092693Sjojemann * if the checksum is even set is also required. 31102693Sjojemann * 31112693Sjojemann * Any changes to an IP address, port or checksum within 31122693Sjojemann * the ICMP packet requires a change to icmp_cksum. 31132693Sjojemann * 31142693Sjojemann * Be extremely careful here ... The change is dependent 31152693Sjojemann * upon whether or not the TCP/UPD checksum is present. 31162693Sjojemann * 31172693Sjojemann * If TCP/UPD checksum is present, the icmp_cksum must 31182693Sjojemann * compensate for checksum modification resulting from 31192693Sjojemann * IP address change only. Port change and resulting 31202693Sjojemann * data checksum adjustments cancel each other out. 31212693Sjojemann * 31222693Sjojemann * If TCP/UDP checksum is not present, icmp_cksum must 31232693Sjojemann * compensate for port change only. The IP address 31242693Sjojemann * change does not modify anything else in this case. 31252693Sjojemann */ 31262693Sjojemann 31272693Sjojemann psum1 = 0; 31282693Sjojemann psum2 = 0; 31292693Sjojemann psumd = 0; 31302693Sjojemann 31312693Sjojemann if ((tcp->th_dport == nat->nat_oport) && 31322693Sjojemann (tcp->th_sport != nat->nat_inport)) { 31332393Syz155240 31342393Syz155240 /* 31352693Sjojemann * Translate the source port. 31362693Sjojemann */ 31372693Sjojemann 31382693Sjojemann psum1 = ntohs(tcp->th_sport); 31392693Sjojemann psum2 = ntohs(nat->nat_inport); 31402693Sjojemann tcp->th_sport = nat->nat_inport; 31412693Sjojemann 31422693Sjojemann } else if ((tcp->th_sport == nat->nat_oport) && 31432693Sjojemann (tcp->th_dport != nat->nat_outport)) { 31442693Sjojemann 31452693Sjojemann /* 31462693Sjojemann * Translate the destination port. 31472393Syz155240 */ 31482693Sjojemann 31492693Sjojemann psum1 = ntohs(tcp->th_dport); 31502693Sjojemann psum2 = ntohs(nat->nat_outport); 31512693Sjojemann tcp->th_dport = nat->nat_outport; 31522693Sjojemann } 31532693Sjojemann 31542693Sjojemann if ((oip->ip_p == IPPROTO_TCP) && (dlen >= 18)) { 31552693Sjojemann 31562693Sjojemann /* 31572693Sjojemann * TCP checksum present. 31582693Sjojemann * 31592693Sjojemann * Adjust data checksum and icmp checksum to 31602693Sjojemann * compensate for any IP address change. 31612693Sjojemann */ 31622693Sjojemann 31632693Sjojemann sum1 = ntohs(tcp->th_sum); 31642693Sjojemann fix_datacksum(&tcp->th_sum, sumd); 31652693Sjojemann sum2 = ntohs(tcp->th_sum); 31662693Sjojemann sumd2 = sumd << 1; 31672393Syz155240 CALC_SUMD(sum1, sum2, sumd); 31682393Syz155240 sumd2 += sumd; 31692693Sjojemann 31702693Sjojemann /* 31712693Sjojemann * Also make data checksum adjustment to 31722693Sjojemann * compensate for any port change. 31732693Sjojemann */ 31742693Sjojemann 31752693Sjojemann if (psum1 != psum2) { 31762693Sjojemann CALC_SUMD(psum1, psum2, psumd); 31772693Sjojemann fix_datacksum(&tcp->th_sum, psumd); 31782393Syz155240 } 31792693Sjojemann 31802693Sjojemann } else if ((oip->ip_p == IPPROTO_UDP) && 31812693Sjojemann (dlen >= 8) && (udp->uh_sum != 0)) { 31822693Sjojemann 31832693Sjojemann /* 31842693Sjojemann * The UDP checksum is present and set. 31852693Sjojemann * 31862693Sjojemann * Adjust data checksum and icmp checksum to 31872693Sjojemann * compensate for any IP address change. 31882693Sjojemann */ 31892693Sjojemann 31902693Sjojemann sum1 = ntohs(udp->uh_sum); 31912693Sjojemann fix_datacksum(&udp->uh_sum, sumd); 31922693Sjojemann sum2 = ntohs(udp->uh_sum); 31932693Sjojemann sumd2 = sumd << 1; 31942693Sjojemann CALC_SUMD(sum1, sum2, sumd); 31952393Syz155240 sumd2 += sumd; 31962393Syz155240 31972393Syz155240 /* 31982693Sjojemann * Also make data checksum adjustment to 31992693Sjojemann * compensate for any port change. 32002393Syz155240 */ 32012693Sjojemann 32022693Sjojemann if (psum1 != psum2) { 32032693Sjojemann CALC_SUMD(psum1, psum2, psumd); 32042693Sjojemann fix_datacksum(&udp->uh_sum, psumd); 32052693Sjojemann } 32062693Sjojemann 32072693Sjojemann } else { 32082693Sjojemann 32092693Sjojemann /* 32102693Sjojemann * Data checksum was not present. 32112693Sjojemann * 32122693Sjojemann * Compensate for any port change. 32132693Sjojemann */ 32142693Sjojemann 32152693Sjojemann CALC_SUMD(psum2, psum1, psumd); 32162693Sjojemann sumd2 += psumd; 32172393Syz155240 } 32182693Sjojemann break; 32192693Sjojemann 32202693Sjojemann case IPPROTO_ICMP : 32212693Sjojemann 32222693Sjojemann orgicmp = (icmphdr_t *)udp; 32232693Sjojemann 32242693Sjojemann if ((nat->nat_dir == NAT_OUTBOUND) && 32252693Sjojemann (orgicmp->icmp_id != nat->nat_inport) && 32262693Sjojemann (dlen >= 8)) { 32272393Syz155240 32282393Syz155240 /* 32292393Syz155240 * Fix ICMP checksum (of the offening ICMP 32302393Syz155240 * query packet) to compensate the change 32312393Syz155240 * in the ICMP id of the offending ICMP 32322393Syz155240 * packet. 32332393Syz155240 * 32342393Syz155240 * Since you modify orgicmp->icmp_id with 32352393Syz155240 * a delta (say x) and you compensate that 32362393Syz155240 * in origicmp->icmp_cksum with a delta 32372393Syz155240 * minus x, you don't have to adjust the 32382393Syz155240 * overall icmp->icmp_cksum 32392393Syz155240 */ 32402693Sjojemann 32412393Syz155240 sum1 = ntohs(orgicmp->icmp_id); 32422393Syz155240 sum2 = ntohs(nat->nat_inport); 32432393Syz155240 CALC_SUMD(sum1, sum2, sumd); 32442393Syz155240 orgicmp->icmp_id = nat->nat_inport; 32452393Syz155240 fix_datacksum(&orgicmp->icmp_cksum, sumd); 32462693Sjojemann 32472693Sjojemann } /* nat_dir can't be NAT_INBOUND for icmp queries */ 32482693Sjojemann 32492693Sjojemann break; 32502693Sjojemann 32512693Sjojemann default : 32522693Sjojemann 32532693Sjojemann break; 32542693Sjojemann 32552693Sjojemann } /* switch (oip->ip_p) */ 32562693Sjojemann 32572693Sjojemann /* 32582693Sjojemann * Step 3 32592693Sjojemann * Make the adjustments to icmp checksum. 32602693Sjojemann */ 32612693Sjojemann 32622693Sjojemann if (sumd2 != 0) { 32632693Sjojemann sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 32642693Sjojemann sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 32652958Sdr146992 fix_incksum(&icmp->icmp_cksum, sumd2); 32662393Syz155240 } 32672393Syz155240 return nat; 32682393Syz155240 } 32692393Syz155240 32702393Syz155240 32712393Syz155240 /* 32722393Syz155240 * NB: these lookups don't lock access to the list, it assumed that it has 32732393Syz155240 * already been done! 32742393Syz155240 */ 32752393Syz155240 32762393Syz155240 /* ------------------------------------------------------------------------ */ 32772393Syz155240 /* Function: nat_inlookup */ 32782393Syz155240 /* Returns: nat_t* - NULL == no match, */ 32792393Syz155240 /* else pointer to matching NAT entry */ 32802393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 32812393Syz155240 /* flags(I) - NAT flags for this packet */ 32822393Syz155240 /* p(I) - protocol for this packet */ 32832393Syz155240 /* src(I) - source IP address */ 32842393Syz155240 /* mapdst(I) - destination IP address */ 32852393Syz155240 /* */ 32862393Syz155240 /* Lookup a nat entry based on the mapped destination ip address/port and */ 32872393Syz155240 /* real source address/port. We use this lookup when receiving a packet, */ 32882393Syz155240 /* we're looking for a table entry, based on the destination address. */ 32892393Syz155240 /* */ 32902393Syz155240 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 32912393Syz155240 /* */ 32922393Syz155240 /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */ 32932393Syz155240 /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 32942393Syz155240 /* */ 32952393Syz155240 /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 32962393Syz155240 /* the packet is of said protocol */ 32972393Syz155240 /* ------------------------------------------------------------------------ */ 32982393Syz155240 nat_t *nat_inlookup(fin, flags, p, src, mapdst) 32992393Syz155240 fr_info_t *fin; 33002393Syz155240 u_int flags, p; 33012393Syz155240 struct in_addr src , mapdst; 33022393Syz155240 { 33032393Syz155240 u_short sport, dport; 33042393Syz155240 ipnat_t *ipn; 33052393Syz155240 u_int sflags; 33062393Syz155240 nat_t *nat; 33072393Syz155240 int nflags; 33082393Syz155240 u_32_t dst; 33092393Syz155240 void *ifp; 33102393Syz155240 u_int hv; 33113448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 33122393Syz155240 33132393Syz155240 if (fin != NULL) 33142393Syz155240 ifp = fin->fin_ifp; 33152393Syz155240 else 33162393Syz155240 ifp = NULL; 33172393Syz155240 sport = 0; 33182393Syz155240 dport = 0; 33192393Syz155240 dst = mapdst.s_addr; 33202393Syz155240 sflags = flags & NAT_TCPUDPICMP; 33212393Syz155240 33222393Syz155240 switch (p) 33232393Syz155240 { 33242393Syz155240 case IPPROTO_TCP : 33252393Syz155240 case IPPROTO_UDP : 33262393Syz155240 sport = htons(fin->fin_data[0]); 33272393Syz155240 dport = htons(fin->fin_data[1]); 33282393Syz155240 break; 33292393Syz155240 case IPPROTO_ICMP : 33302393Syz155240 if (flags & IPN_ICMPERR) 33312393Syz155240 sport = fin->fin_data[1]; 33322393Syz155240 else 33332393Syz155240 dport = fin->fin_data[1]; 33342393Syz155240 break; 33352393Syz155240 default : 33362393Syz155240 break; 33372393Syz155240 } 33382393Syz155240 33392393Syz155240 33402393Syz155240 if ((flags & SI_WILDP) != 0) 33412393Syz155240 goto find_in_wild_ports; 33422393Syz155240 33432393Syz155240 hv = NAT_HASH_FN(dst, dport, 0xffffffff); 33443448Sdh155122 hv = NAT_HASH_FN(src.s_addr, hv + sport, ifs->ifs_ipf_nattable_sz); 33453448Sdh155122 nat = ifs->ifs_nat_table[1][hv]; 33462393Syz155240 for (; nat; nat = nat->nat_hnext[1]) { 33477176Syx160601 if (nat->nat_v != 4) 33487176Syx160601 continue; 33497176Syx160601 33502508Syz155240 if (nat->nat_ifps[0] != NULL) { 33512508Syz155240 if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 33522508Syz155240 continue; 33532508Syz155240 } else if (ifp != NULL) 33542508Syz155240 nat->nat_ifps[0] = ifp; 33552508Syz155240 33562393Syz155240 nflags = nat->nat_flags; 33572393Syz155240 33582393Syz155240 if (nat->nat_oip.s_addr == src.s_addr && 33592393Syz155240 nat->nat_outip.s_addr == dst && 33602393Syz155240 (((p == 0) && 33612393Syz155240 (sflags == (nat->nat_flags & IPN_TCPUDPICMP))) 33622393Syz155240 || (p == nat->nat_p))) { 33632393Syz155240 switch (p) 33642393Syz155240 { 33652393Syz155240 #if 0 33662393Syz155240 case IPPROTO_GRE : 33672393Syz155240 if (nat->nat_call[1] != fin->fin_data[0]) 33682393Syz155240 continue; 33692393Syz155240 break; 33702393Syz155240 #endif 33712393Syz155240 case IPPROTO_ICMP : 33722393Syz155240 if ((flags & IPN_ICMPERR) != 0) { 33732393Syz155240 if (nat->nat_outport != sport) 33742393Syz155240 continue; 33752393Syz155240 } else { 33762393Syz155240 if (nat->nat_outport != dport) 33772393Syz155240 continue; 33782393Syz155240 } 33792393Syz155240 break; 33802393Syz155240 case IPPROTO_TCP : 33812393Syz155240 case IPPROTO_UDP : 33822393Syz155240 if (nat->nat_oport != sport) 33832393Syz155240 continue; 33842393Syz155240 if (nat->nat_outport != dport) 33852393Syz155240 continue; 33862393Syz155240 break; 33872393Syz155240 default : 33882393Syz155240 break; 33892393Syz155240 } 33902393Syz155240 33912393Syz155240 ipn = nat->nat_ptr; 33922393Syz155240 if ((ipn != NULL) && (nat->nat_aps != NULL)) 33932393Syz155240 if (appr_match(fin, nat) != 0) 33942393Syz155240 continue; 33952393Syz155240 return nat; 33962393Syz155240 } 33972393Syz155240 } 33982393Syz155240 33992393Syz155240 /* 34002393Syz155240 * So if we didn't find it but there are wildcard members in the hash 34012393Syz155240 * table, go back and look for them. We do this search and update here 34022393Syz155240 * because it is modifying the NAT table and we want to do this only 34032393Syz155240 * for the first packet that matches. The exception, of course, is 34042393Syz155240 * for "dummy" (FI_IGNORE) lookups. 34052393Syz155240 */ 34062393Syz155240 find_in_wild_ports: 34072393Syz155240 if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) 34082393Syz155240 return NULL; 34093448Sdh155122 if (ifs->ifs_nat_stats.ns_wilds == 0) 34102393Syz155240 return NULL; 34112393Syz155240 34123448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 34132393Syz155240 34142393Syz155240 hv = NAT_HASH_FN(dst, 0, 0xffffffff); 34153448Sdh155122 hv = NAT_HASH_FN(src.s_addr, hv, ifs->ifs_ipf_nattable_sz); 34163448Sdh155122 34173448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 34183448Sdh155122 34193448Sdh155122 nat = ifs->ifs_nat_table[1][hv]; 34202393Syz155240 for (; nat; nat = nat->nat_hnext[1]) { 34217176Syx160601 if (nat->nat_v != 4) 34227176Syx160601 continue; 34237176Syx160601 34242508Syz155240 if (nat->nat_ifps[0] != NULL) { 34252508Syz155240 if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 34262508Syz155240 continue; 34272508Syz155240 } else if (ifp != NULL) 34282508Syz155240 nat->nat_ifps[0] = ifp; 34292393Syz155240 34302393Syz155240 if (nat->nat_p != fin->fin_p) 34312393Syz155240 continue; 34322393Syz155240 if (nat->nat_oip.s_addr != src.s_addr || 34332393Syz155240 nat->nat_outip.s_addr != dst) 34342393Syz155240 continue; 34352393Syz155240 34362393Syz155240 nflags = nat->nat_flags; 34372393Syz155240 if (!(nflags & (NAT_TCPUDP|SI_WILDP))) 34382393Syz155240 continue; 34392393Syz155240 34402393Syz155240 if (nat_wildok(nat, (int)sport, (int)dport, nflags, 34412393Syz155240 NAT_INBOUND) == 1) { 34422393Syz155240 if ((fin->fin_flx & FI_IGNORE) != 0) 34432393Syz155240 break; 34442393Syz155240 if ((nflags & SI_CLONE) != 0) { 34452393Syz155240 nat = fr_natclone(fin, nat); 34462393Syz155240 if (nat == NULL) 34472393Syz155240 break; 34482393Syz155240 } else { 34493448Sdh155122 MUTEX_ENTER(&ifs->ifs_ipf_nat_new); 34503448Sdh155122 ifs->ifs_nat_stats.ns_wilds--; 34513448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_nat_new); 34522393Syz155240 } 34532393Syz155240 nat->nat_oport = sport; 34542393Syz155240 nat->nat_outport = dport; 34552393Syz155240 nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 34563448Sdh155122 nat_tabmove(nat, ifs); 34572393Syz155240 break; 34582393Syz155240 } 34592393Syz155240 } 34602393Syz155240 34613448Sdh155122 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); 34622393Syz155240 34632393Syz155240 return nat; 34642393Syz155240 } 34652393Syz155240 34662393Syz155240 34672393Syz155240 /* ------------------------------------------------------------------------ */ 34682393Syz155240 /* Function: nat_tabmove */ 34692393Syz155240 /* Returns: Nil */ 34702393Syz155240 /* Parameters: nat(I) - pointer to NAT structure */ 34712393Syz155240 /* Write Lock: ipf_nat */ 34722393Syz155240 /* */ 34732393Syz155240 /* This function is only called for TCP/UDP NAT table entries where the */ 34742393Syz155240 /* original was placed in the table without hashing on the ports and we now */ 34752393Syz155240 /* want to include hashing on port numbers. */ 34762393Syz155240 /* ------------------------------------------------------------------------ */ 34773448Sdh155122 static void nat_tabmove(nat, ifs) 34782393Syz155240 nat_t *nat; 34793448Sdh155122 ipf_stack_t *ifs; 34802393Syz155240 { 34812393Syz155240 nat_t **natp; 34822393Syz155240 u_int hv; 34832393Syz155240 34842393Syz155240 if (nat->nat_flags & SI_CLONE) 34852393Syz155240 return; 34862393Syz155240 34872393Syz155240 /* 34882393Syz155240 * Remove the NAT entry from the old location 34892393Syz155240 */ 34902393Syz155240 if (nat->nat_hnext[0]) 34912393Syz155240 nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 34922393Syz155240 *nat->nat_phnext[0] = nat->nat_hnext[0]; 34933448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--; 34942393Syz155240 34952393Syz155240 if (nat->nat_hnext[1]) 34962393Syz155240 nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 34972393Syz155240 *nat->nat_phnext[1] = nat->nat_hnext[1]; 34983448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--; 34992393Syz155240 35002393Syz155240 /* 35012393Syz155240 * Add into the NAT table in the new position 35022393Syz155240 */ 35032393Syz155240 hv = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 0xffffffff); 35042393Syz155240 hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport, 35053448Sdh155122 ifs->ifs_ipf_nattable_sz); 35062393Syz155240 nat->nat_hv[0] = hv; 35073448Sdh155122 natp = &ifs->ifs_nat_table[0][hv]; 35082393Syz155240 if (*natp) 35092393Syz155240 (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 35102393Syz155240 nat->nat_phnext[0] = natp; 35112393Syz155240 nat->nat_hnext[0] = *natp; 35122393Syz155240 *natp = nat; 35133448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[0][hv]++; 35142393Syz155240 35152393Syz155240 hv = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, 0xffffffff); 35162393Syz155240 hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport, 35173448Sdh155122 ifs->ifs_ipf_nattable_sz); 35182393Syz155240 nat->nat_hv[1] = hv; 35193448Sdh155122 natp = &ifs->ifs_nat_table[1][hv]; 35202393Syz155240 if (*natp) 35212393Syz155240 (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 35222393Syz155240 nat->nat_phnext[1] = natp; 35232393Syz155240 nat->nat_hnext[1] = *natp; 35242393Syz155240 *natp = nat; 35253448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[1][hv]++; 35262393Syz155240 } 35272393Syz155240 35282393Syz155240 35292393Syz155240 /* ------------------------------------------------------------------------ */ 35302393Syz155240 /* Function: nat_outlookup */ 35312393Syz155240 /* Returns: nat_t* - NULL == no match, */ 35322393Syz155240 /* else pointer to matching NAT entry */ 35332393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 35342393Syz155240 /* flags(I) - NAT flags for this packet */ 35352393Syz155240 /* p(I) - protocol for this packet */ 35362393Syz155240 /* src(I) - source IP address */ 35372393Syz155240 /* dst(I) - destination IP address */ 35382393Syz155240 /* rw(I) - 1 == write lock on ipf_nat held, 0 == read lock. */ 35392393Syz155240 /* */ 35402393Syz155240 /* Lookup a nat entry based on the source 'real' ip address/port and */ 35412393Syz155240 /* destination address/port. We use this lookup when sending a packet out, */ 35422393Syz155240 /* we're looking for a table entry, based on the source address. */ 35432393Syz155240 /* */ 35442393Syz155240 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 35452393Syz155240 /* */ 35462393Syz155240 /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */ 35472393Syz155240 /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 35482393Syz155240 /* */ 35492393Syz155240 /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 35502393Syz155240 /* the packet is of said protocol */ 35512393Syz155240 /* ------------------------------------------------------------------------ */ 35522393Syz155240 nat_t *nat_outlookup(fin, flags, p, src, dst) 35532393Syz155240 fr_info_t *fin; 35542393Syz155240 u_int flags, p; 35552393Syz155240 struct in_addr src , dst; 35562393Syz155240 { 35572393Syz155240 u_short sport, dport; 35582393Syz155240 u_int sflags; 35592393Syz155240 ipnat_t *ipn; 35602393Syz155240 u_32_t srcip; 35612393Syz155240 nat_t *nat; 35622393Syz155240 int nflags; 35632393Syz155240 void *ifp; 35642393Syz155240 u_int hv; 35653448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 35662508Syz155240 35673894Sjojemann ifp = fin->fin_ifp; 35682508Syz155240 35692393Syz155240 srcip = src.s_addr; 35702393Syz155240 sflags = flags & IPN_TCPUDPICMP; 35712393Syz155240 sport = 0; 35722393Syz155240 dport = 0; 35732393Syz155240 35742393Syz155240 switch (p) 35752393Syz155240 { 35762393Syz155240 case IPPROTO_TCP : 35772393Syz155240 case IPPROTO_UDP : 35782393Syz155240 sport = htons(fin->fin_data[0]); 35792393Syz155240 dport = htons(fin->fin_data[1]); 35802393Syz155240 break; 35812393Syz155240 case IPPROTO_ICMP : 35822393Syz155240 if (flags & IPN_ICMPERR) 35832393Syz155240 sport = fin->fin_data[1]; 35842393Syz155240 else 35852393Syz155240 dport = fin->fin_data[1]; 35862393Syz155240 break; 35872393Syz155240 default : 35882393Syz155240 break; 35892393Syz155240 } 35902393Syz155240 35912393Syz155240 if ((flags & SI_WILDP) != 0) 35922393Syz155240 goto find_out_wild_ports; 35932393Syz155240 35942393Syz155240 hv = NAT_HASH_FN(srcip, sport, 0xffffffff); 35953448Sdh155122 hv = NAT_HASH_FN(dst.s_addr, hv + dport, ifs->ifs_ipf_nattable_sz); 35963448Sdh155122 nat = ifs->ifs_nat_table[0][hv]; 35972393Syz155240 for (; nat; nat = nat->nat_hnext[0]) { 35987176Syx160601 if (nat->nat_v != 4) 35997176Syx160601 continue; 36007176Syx160601 36012508Syz155240 if (nat->nat_ifps[1] != NULL) { 36022508Syz155240 if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 36032508Syz155240 continue; 36042508Syz155240 } else if (ifp != NULL) 36052508Syz155240 nat->nat_ifps[1] = ifp; 36062508Syz155240 36072393Syz155240 nflags = nat->nat_flags; 36082508Syz155240 36092393Syz155240 if (nat->nat_inip.s_addr == srcip && 36102393Syz155240 nat->nat_oip.s_addr == dst.s_addr && 36112393Syz155240 (((p == 0) && (sflags == (nflags & NAT_TCPUDPICMP))) 36122393Syz155240 || (p == nat->nat_p))) { 36132393Syz155240 switch (p) 36142393Syz155240 { 36152393Syz155240 #if 0 36162393Syz155240 case IPPROTO_GRE : 36172393Syz155240 if (nat->nat_call[1] != fin->fin_data[0]) 36182393Syz155240 continue; 36192393Syz155240 break; 36202393Syz155240 #endif 36212393Syz155240 case IPPROTO_TCP : 36222393Syz155240 case IPPROTO_UDP : 36232393Syz155240 if (nat->nat_oport != dport) 36242393Syz155240 continue; 36252393Syz155240 if (nat->nat_inport != sport) 36262393Syz155240 continue; 36272393Syz155240 break; 36282393Syz155240 default : 36292393Syz155240 break; 36302393Syz155240 } 36312393Syz155240 36322393Syz155240 ipn = nat->nat_ptr; 36332393Syz155240 if ((ipn != NULL) && (nat->nat_aps != NULL)) 36342393Syz155240 if (appr_match(fin, nat) != 0) 36352393Syz155240 continue; 36362393Syz155240 return nat; 36372393Syz155240 } 36382393Syz155240 } 36392393Syz155240 36402393Syz155240 /* 36412393Syz155240 * So if we didn't find it but there are wildcard members in the hash 36422393Syz155240 * table, go back and look for them. We do this search and update here 36432393Syz155240 * because it is modifying the NAT table and we want to do this only 36442393Syz155240 * for the first packet that matches. The exception, of course, is 36452393Syz155240 * for "dummy" (FI_IGNORE) lookups. 36462393Syz155240 */ 36472393Syz155240 find_out_wild_ports: 36482393Syz155240 if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) 36492393Syz155240 return NULL; 36503448Sdh155122 if (ifs->ifs_nat_stats.ns_wilds == 0) 36512393Syz155240 return NULL; 36522393Syz155240 36533448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 36542393Syz155240 36552393Syz155240 hv = NAT_HASH_FN(srcip, 0, 0xffffffff); 36563448Sdh155122 hv = NAT_HASH_FN(dst.s_addr, hv, ifs->ifs_ipf_nattable_sz); 36573448Sdh155122 36583448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 36593448Sdh155122 36603448Sdh155122 nat = ifs->ifs_nat_table[0][hv]; 36612393Syz155240 for (; nat; nat = nat->nat_hnext[0]) { 36627176Syx160601 if (nat->nat_v != 4) 36637176Syx160601 continue; 36647176Syx160601 36652508Syz155240 if (nat->nat_ifps[1] != NULL) { 36662508Syz155240 if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 36673448Sdh155122 continue; 36682508Syz155240 } else if (ifp != NULL) 36692508Syz155240 nat->nat_ifps[1] = ifp; 36702393Syz155240 36712393Syz155240 if (nat->nat_p != fin->fin_p) 36722393Syz155240 continue; 36732393Syz155240 if ((nat->nat_inip.s_addr != srcip) || 36742393Syz155240 (nat->nat_oip.s_addr != dst.s_addr)) 36752393Syz155240 continue; 36762393Syz155240 36772393Syz155240 nflags = nat->nat_flags; 36782393Syz155240 if (!(nflags & (NAT_TCPUDP|SI_WILDP))) 36792393Syz155240 continue; 36802393Syz155240 36812393Syz155240 if (nat_wildok(nat, (int)sport, (int)dport, nflags, 36822393Syz155240 NAT_OUTBOUND) == 1) { 36832393Syz155240 if ((fin->fin_flx & FI_IGNORE) != 0) 36842393Syz155240 break; 36852393Syz155240 if ((nflags & SI_CLONE) != 0) { 36862393Syz155240 nat = fr_natclone(fin, nat); 36872393Syz155240 if (nat == NULL) 36882393Syz155240 break; 36892393Syz155240 } else { 36903448Sdh155122 MUTEX_ENTER(&ifs->ifs_ipf_nat_new); 36913448Sdh155122 ifs->ifs_nat_stats.ns_wilds--; 36923448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_nat_new); 36932393Syz155240 } 36942393Syz155240 nat->nat_inport = sport; 36952393Syz155240 nat->nat_oport = dport; 36962393Syz155240 if (nat->nat_outport == 0) 36972393Syz155240 nat->nat_outport = sport; 36982393Syz155240 nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 36993448Sdh155122 nat_tabmove(nat, ifs); 37002393Syz155240 break; 37012393Syz155240 } 37022393Syz155240 } 37032393Syz155240 37043448Sdh155122 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); 37052393Syz155240 37062393Syz155240 return nat; 37072393Syz155240 } 37082393Syz155240 37092393Syz155240 37102393Syz155240 /* ------------------------------------------------------------------------ */ 37112393Syz155240 /* Function: nat_lookupredir */ 37122393Syz155240 /* Returns: nat_t* - NULL == no match, */ 37132393Syz155240 /* else pointer to matching NAT entry */ 37142393Syz155240 /* Parameters: np(I) - pointer to description of packet to find NAT table */ 37152393Syz155240 /* entry for. */ 37162393Syz155240 /* */ 37172393Syz155240 /* Lookup the NAT tables to search for a matching redirect */ 37182393Syz155240 /* ------------------------------------------------------------------------ */ 37193448Sdh155122 nat_t *nat_lookupredir(np, ifs) 37202393Syz155240 natlookup_t *np; 37213448Sdh155122 ipf_stack_t *ifs; 37222393Syz155240 { 37232393Syz155240 fr_info_t fi; 37242393Syz155240 nat_t *nat; 37252393Syz155240 37262393Syz155240 bzero((char *)&fi, sizeof(fi)); 37272393Syz155240 if (np->nl_flags & IPN_IN) { 37282393Syz155240 fi.fin_data[0] = ntohs(np->nl_realport); 37292393Syz155240 fi.fin_data[1] = ntohs(np->nl_outport); 37302393Syz155240 } else { 37312393Syz155240 fi.fin_data[0] = ntohs(np->nl_inport); 37322393Syz155240 fi.fin_data[1] = ntohs(np->nl_outport); 37332393Syz155240 } 37342393Syz155240 if (np->nl_flags & IPN_TCP) 37352393Syz155240 fi.fin_p = IPPROTO_TCP; 37362393Syz155240 else if (np->nl_flags & IPN_UDP) 37372393Syz155240 fi.fin_p = IPPROTO_UDP; 37382393Syz155240 else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) 37392393Syz155240 fi.fin_p = IPPROTO_ICMP; 37402393Syz155240 37413448Sdh155122 fi.fin_ifs = ifs; 37422393Syz155240 /* 37432393Syz155240 * We can do two sorts of lookups: 37442393Syz155240 * - IPN_IN: we have the `real' and `out' address, look for `in'. 37452393Syz155240 * - default: we have the `in' and `out' address, look for `real'. 37462393Syz155240 */ 37472393Syz155240 if (np->nl_flags & IPN_IN) { 37482393Syz155240 if ((nat = nat_inlookup(&fi, np->nl_flags, fi.fin_p, 37492393Syz155240 np->nl_realip, np->nl_outip))) { 37502393Syz155240 np->nl_inip = nat->nat_inip; 37512393Syz155240 np->nl_inport = nat->nat_inport; 37522393Syz155240 } 37532393Syz155240 } else { 37542393Syz155240 /* 37552393Syz155240 * If nl_inip is non null, this is a lookup based on the real 37562393Syz155240 * ip address. Else, we use the fake. 37572393Syz155240 */ 37582393Syz155240 if ((nat = nat_outlookup(&fi, np->nl_flags, fi.fin_p, 37592393Syz155240 np->nl_inip, np->nl_outip))) { 37602393Syz155240 37612393Syz155240 if ((np->nl_flags & IPN_FINDFORWARD) != 0) { 37622393Syz155240 fr_info_t fin; 37632393Syz155240 bzero((char *)&fin, sizeof(fin)); 37642393Syz155240 fin.fin_p = nat->nat_p; 37652393Syz155240 fin.fin_data[0] = ntohs(nat->nat_outport); 37662393Syz155240 fin.fin_data[1] = ntohs(nat->nat_oport); 37673448Sdh155122 fin.fin_ifs = ifs; 37682393Syz155240 if (nat_inlookup(&fin, np->nl_flags, fin.fin_p, 37692393Syz155240 nat->nat_outip, 37702393Syz155240 nat->nat_oip) != NULL) { 37712393Syz155240 np->nl_flags &= ~IPN_FINDFORWARD; 37722393Syz155240 } 37732393Syz155240 } 37742393Syz155240 37752393Syz155240 np->nl_realip = nat->nat_outip; 37762393Syz155240 np->nl_realport = nat->nat_outport; 37772393Syz155240 } 37782393Syz155240 } 37792393Syz155240 37802393Syz155240 return nat; 37812393Syz155240 } 37822393Syz155240 37832393Syz155240 37842393Syz155240 /* ------------------------------------------------------------------------ */ 37852393Syz155240 /* Function: nat_match */ 37862393Syz155240 /* Returns: int - 0 == no match, 1 == match */ 37872393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 37882393Syz155240 /* np(I) - pointer to NAT rule */ 37892393Syz155240 /* */ 37902393Syz155240 /* Pull the matching of a packet against a NAT rule out of that complex */ 37912393Syz155240 /* loop inside fr_checknatin() and lay it out properly in its own function. */ 37922393Syz155240 /* ------------------------------------------------------------------------ */ 37932393Syz155240 static int nat_match(fin, np) 37942393Syz155240 fr_info_t *fin; 37952393Syz155240 ipnat_t *np; 37962393Syz155240 { 37972393Syz155240 frtuc_t *ft; 37982393Syz155240 37992393Syz155240 if (fin->fin_v != 4) 38002393Syz155240 return 0; 38012393Syz155240 38022393Syz155240 if (np->in_p && fin->fin_p != np->in_p) 38032393Syz155240 return 0; 38042393Syz155240 38052393Syz155240 if (fin->fin_out) { 38062393Syz155240 if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK))) 38072393Syz155240 return 0; 38082393Syz155240 if (((fin->fin_fi.fi_saddr & np->in_inmsk) != np->in_inip) 38092393Syz155240 ^ ((np->in_flags & IPN_NOTSRC) != 0)) 38102393Syz155240 return 0; 38112393Syz155240 if (((fin->fin_fi.fi_daddr & np->in_srcmsk) != np->in_srcip) 38122393Syz155240 ^ ((np->in_flags & IPN_NOTDST) != 0)) 38132393Syz155240 return 0; 38142393Syz155240 } else { 38152393Syz155240 if (!(np->in_redir & NAT_REDIRECT)) 38162393Syz155240 return 0; 38172393Syz155240 if (((fin->fin_fi.fi_saddr & np->in_srcmsk) != np->in_srcip) 38182393Syz155240 ^ ((np->in_flags & IPN_NOTSRC) != 0)) 38192393Syz155240 return 0; 38202393Syz155240 if (((fin->fin_fi.fi_daddr & np->in_outmsk) != np->in_outip) 38212393Syz155240 ^ ((np->in_flags & IPN_NOTDST) != 0)) 38222393Syz155240 return 0; 38232393Syz155240 } 38242393Syz155240 38252393Syz155240 ft = &np->in_tuc; 38262393Syz155240 if (!(fin->fin_flx & FI_TCPUDP) || 38272393Syz155240 (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { 38282393Syz155240 if (ft->ftu_scmp || ft->ftu_dcmp) 38292393Syz155240 return 0; 38302393Syz155240 return 1; 38312393Syz155240 } 38322393Syz155240 38332393Syz155240 return fr_tcpudpchk(fin, ft); 38342393Syz155240 } 38352393Syz155240 38362393Syz155240 38372393Syz155240 /* ------------------------------------------------------------------------ */ 38382393Syz155240 /* Function: nat_update */ 38392393Syz155240 /* Returns: Nil */ 38407333SJohn.Ojemann@Sun.COM /* Parameters: fin(I) - pointer to packet information */ 38417333SJohn.Ojemann@Sun.COM /* nat(I) - pointer to NAT structure */ 38422393Syz155240 /* np(I) - pointer to NAT rule */ 38437333SJohn.Ojemann@Sun.COM /* Locks: nat_lock */ 38442393Syz155240 /* */ 38452393Syz155240 /* Updates the lifetime of a NAT table entry for non-TCP packets. Must be */ 38462393Syz155240 /* called with fin_rev updated - i.e. after calling nat_proto(). */ 38472393Syz155240 /* ------------------------------------------------------------------------ */ 38482393Syz155240 void nat_update(fin, nat, np) 38492393Syz155240 fr_info_t *fin; 38502393Syz155240 nat_t *nat; 38512393Syz155240 ipnat_t *np; 38522393Syz155240 { 38532393Syz155240 ipftq_t *ifq, *ifq2; 38542393Syz155240 ipftqent_t *tqe; 38553448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 38562393Syz155240 38572393Syz155240 tqe = &nat->nat_tqe; 38582393Syz155240 ifq = tqe->tqe_ifq; 38592393Syz155240 38602393Syz155240 /* 38612393Syz155240 * We allow over-riding of NAT timeouts from NAT rules, even for 38622393Syz155240 * TCP, however, if it is TCP and there is no rule timeout set, 38632393Syz155240 * then do not update the timeout here. 38642393Syz155240 */ 38652393Syz155240 if (np != NULL) 38662393Syz155240 ifq2 = np->in_tqehead[fin->fin_rev]; 38672393Syz155240 else 38682393Syz155240 ifq2 = NULL; 38692393Syz155240 38702393Syz155240 if (nat->nat_p == IPPROTO_TCP && ifq2 == NULL) { 38713448Sdh155122 (void) fr_tcp_age(&nat->nat_tqe, fin, ifs->ifs_nat_tqb, 0); 38722393Syz155240 } else { 38732393Syz155240 if (ifq2 == NULL) { 38742393Syz155240 if (nat->nat_p == IPPROTO_UDP) 38753448Sdh155122 ifq2 = &ifs->ifs_nat_udptq; 38762393Syz155240 else if (nat->nat_p == IPPROTO_ICMP) 38773448Sdh155122 ifq2 = &ifs->ifs_nat_icmptq; 38782393Syz155240 else 38793448Sdh155122 ifq2 = &ifs->ifs_nat_iptq; 38802393Syz155240 } 38812393Syz155240 38823448Sdh155122 fr_movequeue(tqe, ifq, ifq2, ifs); 38832393Syz155240 } 38842393Syz155240 } 38852393Syz155240 38862393Syz155240 38872393Syz155240 /* ------------------------------------------------------------------------ */ 38882393Syz155240 /* Function: fr_checknatout */ 38892393Syz155240 /* Returns: int - -1 == packet failed NAT checks so block it, */ 38902393Syz155240 /* 0 == no packet translation occurred, */ 38912393Syz155240 /* 1 == packet was successfully translated. */ 38922393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 38932393Syz155240 /* passp(I) - pointer to filtering result flags */ 38942393Syz155240 /* */ 38952393Syz155240 /* Check to see if an outcoming packet should be changed. ICMP packets are */ 38962393Syz155240 /* first checked to see if they match an existing entry (if an error), */ 38972393Syz155240 /* otherwise a search of the current NAT table is made. If neither results */ 38982393Syz155240 /* in a match then a search for a matching NAT rule is made. Create a new */ 38992393Syz155240 /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 39002393Syz155240 /* packet header(s) as required. */ 39012393Syz155240 /* ------------------------------------------------------------------------ */ 39022393Syz155240 int fr_checknatout(fin, passp) 39032393Syz155240 fr_info_t *fin; 39042393Syz155240 u_32_t *passp; 39052393Syz155240 { 39067131Sdr146992 ipnat_t *np = NULL, *npnext; 39072393Syz155240 struct ifnet *ifp, *sifp; 39082393Syz155240 icmphdr_t *icmp = NULL; 39092393Syz155240 tcphdr_t *tcp = NULL; 39102393Syz155240 int rval, natfailed; 39112393Syz155240 u_int nflags = 0; 39122393Syz155240 u_32_t ipa, iph; 39132393Syz155240 int natadd = 1; 39142393Syz155240 frentry_t *fr; 39152393Syz155240 nat_t *nat; 39163448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 39173448Sdh155122 3918*7432SJohn.Ojemann@Sun.COM if (ifs->ifs_fr_nat_lock != 0) 3919*7432SJohn.Ojemann@Sun.COM return 0; 3920*7432SJohn.Ojemann@Sun.COM if (ifs->ifs_nat_stats.ns_rules == 0 && ifs->ifs_nat_instances == NULL) 39212393Syz155240 return 0; 39222393Syz155240 39232393Syz155240 natfailed = 0; 39242393Syz155240 fr = fin->fin_fr; 39252393Syz155240 sifp = fin->fin_ifp; 39262393Syz155240 if ((fr != NULL) && !(fr->fr_flags & FR_DUP) && 39273894Sjojemann fr->fr_tifs[fin->fin_rev].fd_ifp && 39283894Sjojemann fr->fr_tifs[fin->fin_rev].fd_ifp != (void *)-1) 39293894Sjojemann fin->fin_ifp = fr->fr_tifs[fin->fin_rev].fd_ifp; 39302393Syz155240 ifp = fin->fin_ifp; 39312393Syz155240 39322393Syz155240 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 39332393Syz155240 switch (fin->fin_p) 39342393Syz155240 { 39352393Syz155240 case IPPROTO_TCP : 39362393Syz155240 nflags = IPN_TCP; 39372393Syz155240 break; 39382393Syz155240 case IPPROTO_UDP : 39392393Syz155240 nflags = IPN_UDP; 39402393Syz155240 break; 39412393Syz155240 case IPPROTO_ICMP : 39422393Syz155240 icmp = fin->fin_dp; 39432393Syz155240 39442393Syz155240 /* 39452393Syz155240 * This is an incoming packet, so the destination is 39462393Syz155240 * the icmp_id and the source port equals 0 39472393Syz155240 */ 39482393Syz155240 if (nat_icmpquerytype4(icmp->icmp_type)) 39492393Syz155240 nflags = IPN_ICMPQUERY; 39502393Syz155240 break; 39512393Syz155240 default : 39522393Syz155240 break; 39532393Syz155240 } 39542393Syz155240 39552393Syz155240 if ((nflags & IPN_TCPUDP)) 39562393Syz155240 tcp = fin->fin_dp; 39572393Syz155240 } 39582393Syz155240 39592393Syz155240 ipa = fin->fin_saddr; 39602393Syz155240 39613448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 39622393Syz155240 39632393Syz155240 if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && 39642393Syz155240 (nat = nat_icmperror(fin, &nflags, NAT_OUTBOUND))) 39652393Syz155240 /*EMPTY*/; 39662393Syz155240 else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin))) 39672393Syz155240 natadd = 0; 39682393Syz155240 else if ((nat = nat_outlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, 39692393Syz155240 fin->fin_src, fin->fin_dst))) { 39702393Syz155240 nflags = nat->nat_flags; 39712393Syz155240 } else { 39722393Syz155240 u_32_t hv, msk, nmsk; 39732393Syz155240 39742393Syz155240 /* 39752393Syz155240 * If there is no current entry in the nat table for this IP#, 39762393Syz155240 * create one for it (if there is a matching rule). 39772393Syz155240 */ 39782393Syz155240 msk = 0xffffffff; 39793448Sdh155122 nmsk = ifs->ifs_nat_masks; 39802393Syz155240 maskloop: 39812393Syz155240 iph = ipa & htonl(msk); 39823448Sdh155122 hv = NAT_HASH_FN(iph, 0, ifs->ifs_ipf_natrules_sz); 39837131Sdr146992 for (np = ifs->ifs_nat_rules[hv]; np; np = npnext) { 39847131Sdr146992 npnext = np->in_mnext; 39852508Syz155240 if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) 39862393Syz155240 continue; 39872393Syz155240 if (np->in_v != fin->fin_v) 39882393Syz155240 continue; 39892393Syz155240 if (np->in_p && (np->in_p != fin->fin_p)) 39902393Syz155240 continue; 39912393Syz155240 if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) 39922393Syz155240 continue; 39932393Syz155240 if (np->in_flags & IPN_FILTER) { 39942393Syz155240 if (!nat_match(fin, np)) 39952393Syz155240 continue; 39962393Syz155240 } else if ((ipa & np->in_inmsk) != np->in_inip) 39972393Syz155240 continue; 39982393Syz155240 39992393Syz155240 if ((fr != NULL) && 40002393Syz155240 !fr_matchtag(&np->in_tag, &fr->fr_nattag)) 40012393Syz155240 continue; 40022393Syz155240 40032393Syz155240 if (*np->in_plabel != '\0') { 40042393Syz155240 if (((np->in_flags & IPN_FILTER) == 0) && 40052393Syz155240 (np->in_dport != tcp->th_dport)) 40062393Syz155240 continue; 40072393Syz155240 if (appr_ok(fin, tcp, np) == 0) 40082393Syz155240 continue; 40092393Syz155240 } 40102393Syz155240 40117131Sdr146992 ATOMIC_INC32(np->in_use); 40127131Sdr146992 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 40137131Sdr146992 WRITE_ENTER(&ifs->ifs_ipf_nat); 40147131Sdr146992 nat = nat_new(fin, np, NULL, nflags, NAT_OUTBOUND); 40157131Sdr146992 if (nat != NULL) { 40167131Sdr146992 np->in_use--; 40172393Syz155240 np->in_hits++; 40187131Sdr146992 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); 40192393Syz155240 break; 40207131Sdr146992 } 40217131Sdr146992 natfailed = -1; 40227131Sdr146992 npnext = np->in_mnext; 40237131Sdr146992 fr_ipnatderef(&np, ifs); 40247131Sdr146992 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); 40252393Syz155240 } 40262393Syz155240 if ((np == NULL) && (nmsk != 0)) { 40272393Syz155240 while (nmsk) { 40282393Syz155240 msk <<= 1; 40292393Syz155240 if (nmsk & 0x80000000) 40302393Syz155240 break; 40312393Syz155240 nmsk <<= 1; 40322393Syz155240 } 40332393Syz155240 if (nmsk != 0) { 40342393Syz155240 nmsk <<= 1; 40352393Syz155240 goto maskloop; 40362393Syz155240 } 40372393Syz155240 } 40382393Syz155240 } 40392393Syz155240 40402393Syz155240 if (nat != NULL) { 40412393Syz155240 rval = fr_natout(fin, nat, natadd, nflags); 40422393Syz155240 if (rval == 1) { 40432393Syz155240 MUTEX_ENTER(&nat->nat_lock); 40447333SJohn.Ojemann@Sun.COM nat_update(fin, nat, nat->nat_ptr); 40457333SJohn.Ojemann@Sun.COM nat->nat_bytes[1] += fin->fin_plen; 40467333SJohn.Ojemann@Sun.COM nat->nat_pkts[1]++; 40472393Syz155240 nat->nat_ref++; 40482393Syz155240 MUTEX_EXIT(&nat->nat_lock); 40494817San207044 nat->nat_touched = ifs->ifs_fr_ticks; 40502393Syz155240 fin->fin_nat = nat; 40512393Syz155240 } 40522393Syz155240 } else 40532393Syz155240 rval = natfailed; 40543448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 40552393Syz155240 40562393Syz155240 if (rval == -1) { 40572393Syz155240 if (passp != NULL) 40582393Syz155240 *passp = FR_BLOCK; 40592393Syz155240 fin->fin_flx |= FI_BADNAT; 40602393Syz155240 } 40612393Syz155240 fin->fin_ifp = sifp; 40622393Syz155240 return rval; 40632393Syz155240 } 40642393Syz155240 40652393Syz155240 /* ------------------------------------------------------------------------ */ 40662393Syz155240 /* Function: fr_natout */ 40672393Syz155240 /* Returns: int - -1 == packet failed NAT checks so block it, */ 40682393Syz155240 /* 1 == packet was successfully translated. */ 40692393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 40702393Syz155240 /* nat(I) - pointer to NAT structure */ 40712393Syz155240 /* natadd(I) - flag indicating if it is safe to add frag cache */ 40722393Syz155240 /* nflags(I) - NAT flags set for this packet */ 40732393Syz155240 /* */ 40742393Syz155240 /* Translate a packet coming "out" on an interface. */ 40752393Syz155240 /* ------------------------------------------------------------------------ */ 40762393Syz155240 int fr_natout(fin, nat, natadd, nflags) 40772393Syz155240 fr_info_t *fin; 40782393Syz155240 nat_t *nat; 40792393Syz155240 int natadd; 40802393Syz155240 u_32_t nflags; 40812393Syz155240 { 40822393Syz155240 icmphdr_t *icmp; 40832393Syz155240 u_short *csump; 40842958Sdr146992 u_32_t sumd; 40852393Syz155240 tcphdr_t *tcp; 40862393Syz155240 ipnat_t *np; 40872393Syz155240 int i; 40883448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 40892393Syz155240 40907176Syx160601 if (fin->fin_v == 6) { 40917176Syx160601 #ifdef USE_INET6 40927176Syx160601 return fr_nat6out(fin, nat, natadd, nflags); 40937176Syx160601 #else 40947176Syx160601 return NULL; 40957176Syx160601 #endif 40967176Syx160601 } 40977176Syx160601 40982958Sdr146992 #if SOLARIS && defined(_KERNEL) 40997176Syx160601 net_data_t net_data_p = ifs->ifs_ipf_ipv4; 41002958Sdr146992 #endif 41012958Sdr146992 41022393Syz155240 tcp = NULL; 41032393Syz155240 icmp = NULL; 41042393Syz155240 csump = NULL; 41052393Syz155240 np = nat->nat_ptr; 41062393Syz155240 41074712Szf203873 if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) 41082393Syz155240 (void) fr_nat_newfrag(fin, 0, nat); 41092958Sdr146992 41102393Syz155240 /* 41112393Syz155240 * Fix up checksums, not by recalculating them, but 41122393Syz155240 * simply computing adjustments. 41132393Syz155240 * This is only done for STREAMS based IP implementations where the 41142393Syz155240 * checksum has already been calculated by IP. In all other cases, 41152393Syz155240 * IPFilter is called before the checksum needs calculating so there 41162393Syz155240 * is no call to modify whatever is in the header now. 41172393Syz155240 */ 41182958Sdr146992 ASSERT(fin->fin_m != NULL); 41192958Sdr146992 if (fin->fin_v == 4 && !NET_IS_HCK_L3_FULL(net_data_p, fin->fin_m)) { 41202393Syz155240 if (nflags == IPN_ICMPERR) { 41212958Sdr146992 u_32_t s1, s2; 41222393Syz155240 41232393Syz155240 s1 = LONG_SUM(ntohl(fin->fin_saddr)); 41242393Syz155240 s2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)); 41252393Syz155240 CALC_SUMD(s1, s2, sumd); 41262958Sdr146992 41272958Sdr146992 fix_outcksum(&fin->fin_ip->ip_sum, sumd); 41282393Syz155240 } 41292393Syz155240 #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 41302393Syz155240 defined(linux) || defined(BRIDGE_IPF) 41312393Syz155240 else { 41322393Syz155240 /* 41332393Syz155240 * Strictly speaking, this isn't necessary on BSD 41342393Syz155240 * kernels because they do checksum calculation after 41352393Syz155240 * this code has run BUT if ipfilter is being used 41362393Syz155240 * to do NAT as a bridge, that code doesn't exist. 41372393Syz155240 */ 41382393Syz155240 if (nat->nat_dir == NAT_OUTBOUND) 41392958Sdr146992 fix_outcksum(&fin->fin_ip->ip_sum, 41402958Sdr146992 nat->nat_ipsumd); 41412393Syz155240 else 41422958Sdr146992 fix_incksum(&fin->fin_ip->ip_sum, 41432958Sdr146992 nat->nat_ipsumd); 41442393Syz155240 } 41452393Syz155240 #endif 41462393Syz155240 } 41472393Syz155240 41482393Syz155240 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 41492393Syz155240 if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) { 41502393Syz155240 tcp = fin->fin_dp; 41512393Syz155240 41522393Syz155240 tcp->th_sport = nat->nat_outport; 41532393Syz155240 fin->fin_data[0] = ntohs(nat->nat_outport); 41542393Syz155240 } 41552393Syz155240 41562393Syz155240 if ((nat->nat_outport != 0) && (nflags & IPN_ICMPQUERY)) { 41572393Syz155240 icmp = fin->fin_dp; 41582393Syz155240 icmp->icmp_id = nat->nat_outport; 41592393Syz155240 } 41602393Syz155240 41612393Syz155240 csump = nat_proto(fin, nat, nflags); 41622393Syz155240 } 41632393Syz155240 41642393Syz155240 fin->fin_ip->ip_src = nat->nat_outip; 41652393Syz155240 41662393Syz155240 /* 41672393Syz155240 * The above comments do not hold for layer 4 (or higher) checksums... 41682393Syz155240 */ 41692958Sdr146992 if (csump != NULL && !NET_IS_HCK_L4_FULL(net_data_p, fin->fin_m)) { 41702958Sdr146992 if (nflags & IPN_TCPUDP && 41712958Sdr146992 NET_IS_HCK_L4_PART(net_data_p, fin->fin_m)) 41722958Sdr146992 sumd = nat->nat_sumd[1]; 41732958Sdr146992 else 41742958Sdr146992 sumd = nat->nat_sumd[0]; 41752958Sdr146992 41762393Syz155240 if (nat->nat_dir == NAT_OUTBOUND) 41772958Sdr146992 fix_outcksum(csump, sumd); 41782393Syz155240 else 41792958Sdr146992 fix_incksum(csump, sumd); 41802393Syz155240 } 41812393Syz155240 #ifdef IPFILTER_SYNC 41822393Syz155240 ipfsync_update(SMC_NAT, fin, nat->nat_sync); 41832393Syz155240 #endif 41842393Syz155240 /* ------------------------------------------------------------- */ 41852393Syz155240 /* A few quick notes: */ 41862393Syz155240 /* Following are test conditions prior to calling the */ 41872393Syz155240 /* appr_check routine. */ 41882393Syz155240 /* */ 41892393Syz155240 /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 41902393Syz155240 /* with a redirect rule, we attempt to match the packet's */ 41912393Syz155240 /* source port against in_dport, otherwise we'd compare the */ 41922393Syz155240 /* packet's destination. */ 41932393Syz155240 /* ------------------------------------------------------------- */ 41942393Syz155240 if ((np != NULL) && (np->in_apr != NULL)) { 41952393Syz155240 i = appr_check(fin, nat); 41962393Syz155240 if (i == 0) 41972393Syz155240 i = 1; 41982393Syz155240 } else 41992393Syz155240 i = 1; 42007131Sdr146992 ifs->ifs_nat_stats.ns_mapped[1]++; 42012393Syz155240 fin->fin_flx |= FI_NATED; 42022393Syz155240 return i; 42032393Syz155240 } 42042393Syz155240 42052393Syz155240 42062393Syz155240 /* ------------------------------------------------------------------------ */ 42072393Syz155240 /* Function: fr_checknatin */ 42082393Syz155240 /* Returns: int - -1 == packet failed NAT checks so block it, */ 42092393Syz155240 /* 0 == no packet translation occurred, */ 42102393Syz155240 /* 1 == packet was successfully translated. */ 42112393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 42122393Syz155240 /* passp(I) - pointer to filtering result flags */ 42132393Syz155240 /* */ 42142393Syz155240 /* Check to see if an incoming packet should be changed. ICMP packets are */ 42152393Syz155240 /* first checked to see if they match an existing entry (if an error), */ 42162393Syz155240 /* otherwise a search of the current NAT table is made. If neither results */ 42172393Syz155240 /* in a match then a search for a matching NAT rule is made. Create a new */ 42182393Syz155240 /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 42192393Syz155240 /* packet header(s) as required. */ 42202393Syz155240 /* ------------------------------------------------------------------------ */ 42212393Syz155240 int fr_checknatin(fin, passp) 42222393Syz155240 fr_info_t *fin; 42232393Syz155240 u_32_t *passp; 42242393Syz155240 { 42252393Syz155240 u_int nflags, natadd; 42267131Sdr146992 ipnat_t *np, *npnext; 42272393Syz155240 int rval, natfailed; 42282393Syz155240 struct ifnet *ifp; 42292393Syz155240 struct in_addr in; 42302393Syz155240 icmphdr_t *icmp; 42312393Syz155240 tcphdr_t *tcp; 42322393Syz155240 u_short dport; 42332393Syz155240 nat_t *nat; 42342393Syz155240 u_32_t iph; 42353448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 42363448Sdh155122 4237*7432SJohn.Ojemann@Sun.COM if (ifs->ifs_fr_nat_lock != 0) 4238*7432SJohn.Ojemann@Sun.COM return 0; 4239*7432SJohn.Ojemann@Sun.COM if (ifs->ifs_nat_stats.ns_rules == 0 && ifs->ifs_nat_instances == NULL) 42402393Syz155240 return 0; 42412393Syz155240 42422393Syz155240 tcp = NULL; 42432393Syz155240 icmp = NULL; 42442393Syz155240 dport = 0; 42452393Syz155240 natadd = 1; 42462393Syz155240 nflags = 0; 42472393Syz155240 natfailed = 0; 42482393Syz155240 ifp = fin->fin_ifp; 42492393Syz155240 42502393Syz155240 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 42512393Syz155240 switch (fin->fin_p) 42522393Syz155240 { 42532393Syz155240 case IPPROTO_TCP : 42542393Syz155240 nflags = IPN_TCP; 42552393Syz155240 break; 42562393Syz155240 case IPPROTO_UDP : 42572393Syz155240 nflags = IPN_UDP; 42582393Syz155240 break; 42592393Syz155240 case IPPROTO_ICMP : 42602393Syz155240 icmp = fin->fin_dp; 42612393Syz155240 42622393Syz155240 /* 42632393Syz155240 * This is an incoming packet, so the destination is 42642393Syz155240 * the icmp_id and the source port equals 0 42652393Syz155240 */ 42662393Syz155240 if (nat_icmpquerytype4(icmp->icmp_type)) { 42672393Syz155240 nflags = IPN_ICMPQUERY; 42682393Syz155240 dport = icmp->icmp_id; 42692393Syz155240 } break; 42702393Syz155240 default : 42712393Syz155240 break; 42722393Syz155240 } 42732393Syz155240 42742393Syz155240 if ((nflags & IPN_TCPUDP)) { 42752393Syz155240 tcp = fin->fin_dp; 42762393Syz155240 dport = tcp->th_dport; 42772393Syz155240 } 42782393Syz155240 } 42792393Syz155240 42802393Syz155240 in = fin->fin_dst; 42812393Syz155240 42823448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 42832393Syz155240 42842393Syz155240 if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && 42852393Syz155240 (nat = nat_icmperror(fin, &nflags, NAT_INBOUND))) 42862393Syz155240 /*EMPTY*/; 42872393Syz155240 else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin))) 42882393Syz155240 natadd = 0; 42892393Syz155240 else if ((nat = nat_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, 42902393Syz155240 fin->fin_src, in))) { 42912393Syz155240 nflags = nat->nat_flags; 42922393Syz155240 } else { 42932393Syz155240 u_32_t hv, msk, rmsk; 42942393Syz155240 42953448Sdh155122 rmsk = ifs->ifs_rdr_masks; 42962393Syz155240 msk = 0xffffffff; 42972393Syz155240 /* 42982393Syz155240 * If there is no current entry in the nat table for this IP#, 42992393Syz155240 * create one for it (if there is a matching rule). 43002393Syz155240 */ 43012393Syz155240 maskloop: 43022393Syz155240 iph = in.s_addr & htonl(msk); 43033448Sdh155122 hv = NAT_HASH_FN(iph, 0, ifs->ifs_ipf_rdrrules_sz); 43047131Sdr146992 for (np = ifs->ifs_rdr_rules[hv]; np; np = npnext) { 43057131Sdr146992 npnext = np->in_rnext; 43062393Syz155240 if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) 43072393Syz155240 continue; 43082393Syz155240 if (np->in_v != fin->fin_v) 43092393Syz155240 continue; 43102393Syz155240 if (np->in_p && (np->in_p != fin->fin_p)) 43112393Syz155240 continue; 43122393Syz155240 if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) 43132393Syz155240 continue; 43142393Syz155240 if (np->in_flags & IPN_FILTER) { 43152393Syz155240 if (!nat_match(fin, np)) 43162393Syz155240 continue; 43172393Syz155240 } else { 43182393Syz155240 if ((in.s_addr & np->in_outmsk) != np->in_outip) 43192393Syz155240 continue; 43202393Syz155240 if (np->in_pmin && 43212393Syz155240 ((ntohs(np->in_pmax) < ntohs(dport)) || 43222393Syz155240 (ntohs(dport) < ntohs(np->in_pmin)))) 43232393Syz155240 continue; 43242393Syz155240 } 43252393Syz155240 43262393Syz155240 if (*np->in_plabel != '\0') { 43272393Syz155240 if (!appr_ok(fin, tcp, np)) { 43282393Syz155240 continue; 43292393Syz155240 } 43302393Syz155240 } 43312393Syz155240 43327131Sdr146992 ATOMIC_INC32(np->in_use); 43337131Sdr146992 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 43347131Sdr146992 WRITE_ENTER(&ifs->ifs_ipf_nat); 43352393Syz155240 nat = nat_new(fin, np, NULL, nflags, NAT_INBOUND); 43362393Syz155240 if (nat != NULL) { 43377131Sdr146992 np->in_use--; 43382393Syz155240 np->in_hits++; 43397131Sdr146992 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); 43402393Syz155240 break; 43417131Sdr146992 } 43427131Sdr146992 natfailed = -1; 43437131Sdr146992 npnext = np->in_rnext; 43447131Sdr146992 fr_ipnatderef(&np, ifs); 43457131Sdr146992 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); 43462393Syz155240 } 43472393Syz155240 43482393Syz155240 if ((np == NULL) && (rmsk != 0)) { 43492393Syz155240 while (rmsk) { 43502393Syz155240 msk <<= 1; 43512393Syz155240 if (rmsk & 0x80000000) 43522393Syz155240 break; 43532393Syz155240 rmsk <<= 1; 43542393Syz155240 } 43552393Syz155240 if (rmsk != 0) { 43562393Syz155240 rmsk <<= 1; 43572393Syz155240 goto maskloop; 43582393Syz155240 } 43592393Syz155240 } 43602393Syz155240 } 43612393Syz155240 if (nat != NULL) { 43622393Syz155240 rval = fr_natin(fin, nat, natadd, nflags); 43632393Syz155240 if (rval == 1) { 43642393Syz155240 MUTEX_ENTER(&nat->nat_lock); 43657333SJohn.Ojemann@Sun.COM nat_update(fin, nat, nat->nat_ptr); 43667333SJohn.Ojemann@Sun.COM nat->nat_bytes[0] += fin->fin_plen; 43677333SJohn.Ojemann@Sun.COM nat->nat_pkts[0]++; 43682393Syz155240 nat->nat_ref++; 43692393Syz155240 MUTEX_EXIT(&nat->nat_lock); 43704817San207044 nat->nat_touched = ifs->ifs_fr_ticks; 43712393Syz155240 fin->fin_nat = nat; 43722393Syz155240 fin->fin_state = nat->nat_state; 43732393Syz155240 } 43742393Syz155240 } else 43752393Syz155240 rval = natfailed; 43763448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 43772393Syz155240 43782393Syz155240 if (rval == -1) { 43792393Syz155240 if (passp != NULL) 43802393Syz155240 *passp = FR_BLOCK; 43812393Syz155240 fin->fin_flx |= FI_BADNAT; 43822393Syz155240 } 43832393Syz155240 return rval; 43842393Syz155240 } 43852393Syz155240 43862393Syz155240 43872393Syz155240 /* ------------------------------------------------------------------------ */ 43882393Syz155240 /* Function: fr_natin */ 43892393Syz155240 /* Returns: int - -1 == packet failed NAT checks so block it, */ 43902393Syz155240 /* 1 == packet was successfully translated. */ 43912393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 43922393Syz155240 /* nat(I) - pointer to NAT structure */ 43932393Syz155240 /* natadd(I) - flag indicating if it is safe to add frag cache */ 43942393Syz155240 /* nflags(I) - NAT flags set for this packet */ 43952393Syz155240 /* Locks Held: ipf_nat (READ) */ 43962393Syz155240 /* */ 43972393Syz155240 /* Translate a packet coming "in" on an interface. */ 43982393Syz155240 /* ------------------------------------------------------------------------ */ 43992393Syz155240 int fr_natin(fin, nat, natadd, nflags) 44002393Syz155240 fr_info_t *fin; 44012393Syz155240 nat_t *nat; 44022393Syz155240 int natadd; 44032393Syz155240 u_32_t nflags; 44042393Syz155240 { 44052393Syz155240 icmphdr_t *icmp; 44065274Syx160601 u_short *csump; 44072393Syz155240 tcphdr_t *tcp; 44082393Syz155240 ipnat_t *np; 44092393Syz155240 int i; 44103448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 44112393Syz155240 44127176Syx160601 if (fin->fin_v == 6) { 44137176Syx160601 #ifdef USE_INET6 44147176Syx160601 return fr_nat6in(fin, nat, natadd, nflags); 44157176Syx160601 #else 44167176Syx160601 return NULL; 44177176Syx160601 #endif 44187176Syx160601 } 44197176Syx160601 44202958Sdr146992 #if SOLARIS && defined(_KERNEL) 44217176Syx160601 net_data_t net_data_p = ifs->ifs_ipf_ipv4; 44222958Sdr146992 #endif 44232958Sdr146992 44242393Syz155240 tcp = NULL; 44252393Syz155240 csump = NULL; 44262393Syz155240 np = nat->nat_ptr; 44272393Syz155240 fin->fin_fr = nat->nat_fr; 44282393Syz155240 44294712Szf203873 if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) 44304712Szf203873 (void) fr_nat_newfrag(fin, 0, nat); 44314712Szf203873 44322393Syz155240 if (np != NULL) { 44332393Syz155240 44342393Syz155240 /* ------------------------------------------------------------- */ 44352393Syz155240 /* A few quick notes: */ 44362393Syz155240 /* Following are test conditions prior to calling the */ 44372393Syz155240 /* appr_check routine. */ 44382393Syz155240 /* */ 44392393Syz155240 /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 44402393Syz155240 /* with a map rule, we attempt to match the packet's */ 44412393Syz155240 /* source port against in_dport, otherwise we'd compare the */ 44422393Syz155240 /* packet's destination. */ 44432393Syz155240 /* ------------------------------------------------------------- */ 44442393Syz155240 if (np->in_apr != NULL) { 44452393Syz155240 i = appr_check(fin, nat); 44462393Syz155240 if (i == -1) { 44472393Syz155240 return -1; 44482393Syz155240 } 44492393Syz155240 } 44502393Syz155240 } 44512393Syz155240 44522393Syz155240 #ifdef IPFILTER_SYNC 44532393Syz155240 ipfsync_update(SMC_NAT, fin, nat->nat_sync); 44542393Syz155240 #endif 44552393Syz155240 44562393Syz155240 fin->fin_ip->ip_dst = nat->nat_inip; 44572393Syz155240 fin->fin_fi.fi_daddr = nat->nat_inip.s_addr; 44582393Syz155240 if (nflags & IPN_TCPUDP) 44592393Syz155240 tcp = fin->fin_dp; 44602393Syz155240 44612393Syz155240 /* 44622393Syz155240 * Fix up checksums, not by recalculating them, but 44632393Syz155240 * simply computing adjustments. 44642393Syz155240 * Why only do this for some platforms on inbound packets ? 44652393Syz155240 * Because for those that it is done, IP processing is yet to happen 44662393Syz155240 * and so the IPv4 header checksum has not yet been evaluated. 44672393Syz155240 * Perhaps it should always be done for the benefit of things like 44682393Syz155240 * fast forwarding (so that it doesn't need to be recomputed) but with 44692393Syz155240 * header checksum offloading, perhaps it is a moot point. 44702393Syz155240 */ 44712393Syz155240 #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 44722393Syz155240 defined(__osf__) || defined(linux) 44732393Syz155240 if (nat->nat_dir == NAT_OUTBOUND) 44742958Sdr146992 fix_incksum(&fin->fin_ip->ip_sum, nat->nat_ipsumd); 44752393Syz155240 else 44762958Sdr146992 fix_outcksum(&fin->fin_ip->ip_sum, nat->nat_ipsumd); 44772393Syz155240 #endif 44782393Syz155240 44792393Syz155240 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 44802393Syz155240 if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) { 44812393Syz155240 tcp->th_dport = nat->nat_inport; 44822393Syz155240 fin->fin_data[1] = ntohs(nat->nat_inport); 44832393Syz155240 } 44842393Syz155240 44852393Syz155240 44862393Syz155240 if ((nat->nat_inport != 0) && (nflags & IPN_ICMPQUERY)) { 44872393Syz155240 icmp = fin->fin_dp; 44882393Syz155240 44892393Syz155240 icmp->icmp_id = nat->nat_inport; 44902393Syz155240 } 44912393Syz155240 44922393Syz155240 csump = nat_proto(fin, nat, nflags); 44932393Syz155240 } 44942393Syz155240 44955274Syx160601 /* 44965274Syx160601 * In case they are being forwarded, inbound packets always need to have 44975274Syx160601 * their checksum adjusted even if hardware checksum validation said OK. 44985274Syx160601 */ 44995274Syx160601 if (csump != NULL) { 45005274Syx160601 if (nat->nat_dir == NAT_OUTBOUND) 45015274Syx160601 fix_incksum(csump, nat->nat_sumd[0]); 45025274Syx160601 else 45035274Syx160601 fix_outcksum(csump, nat->nat_sumd[0]); 45045274Syx160601 } 45055274Syx160601 45062958Sdr146992 #if SOLARIS && defined(_KERNEL) 45072958Sdr146992 if (nflags & IPN_TCPUDP && 45082958Sdr146992 NET_IS_HCK_L4_PART(net_data_p, fin->fin_m)) { 45095274Syx160601 /* 45105274Syx160601 * Need to adjust the partial checksum result stored in 45115274Syx160601 * db_cksum16, which will be used for validation in IP. 45125274Syx160601 * See IP_CKSUM_RECV(). 45135274Syx160601 * Adjustment data should be the inverse of the IP address 45145274Syx160601 * changes, because db_cksum16 is supposed to be the complement 45155274Syx160601 * of the pesudo header. 45165274Syx160601 */ 45175274Syx160601 csump = &fin->fin_m->b_datap->db_cksum16; 45185274Syx160601 if (nat->nat_dir == NAT_OUTBOUND) 45195274Syx160601 fix_outcksum(csump, nat->nat_sumd[1]); 45205274Syx160601 else 45215274Syx160601 fix_incksum(csump, nat->nat_sumd[1]); 45225274Syx160601 } 45232958Sdr146992 #endif 45245274Syx160601 45257131Sdr146992 ifs->ifs_nat_stats.ns_mapped[0]++; 45262393Syz155240 fin->fin_flx |= FI_NATED; 45272393Syz155240 if (np != NULL && np->in_tag.ipt_num[0] != 0) 45282393Syz155240 fin->fin_nattag = &np->in_tag; 45292393Syz155240 return 1; 45302393Syz155240 } 45312393Syz155240 45322393Syz155240 45332393Syz155240 /* ------------------------------------------------------------------------ */ 45342393Syz155240 /* Function: nat_proto */ 45352393Syz155240 /* Returns: u_short* - pointer to transport header checksum to update, */ 45362393Syz155240 /* NULL if the transport protocol is not recognised */ 45372393Syz155240 /* as needing a checksum update. */ 45382393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 45392393Syz155240 /* nat(I) - pointer to NAT structure */ 45402393Syz155240 /* nflags(I) - NAT flags set for this packet */ 45412393Syz155240 /* */ 45422393Syz155240 /* Return the pointer to the checksum field for each protocol so understood.*/ 45432393Syz155240 /* If support for making other changes to a protocol header is required, */ 45442393Syz155240 /* that is not strictly 'address' translation, such as clamping the MSS in */ 45452393Syz155240 /* TCP down to a specific value, then do it from here. */ 45462393Syz155240 /* ------------------------------------------------------------------------ */ 45472393Syz155240 u_short *nat_proto(fin, nat, nflags) 45482393Syz155240 fr_info_t *fin; 45492393Syz155240 nat_t *nat; 45502393Syz155240 u_int nflags; 45512393Syz155240 { 45522393Syz155240 icmphdr_t *icmp; 45537176Syx160601 struct icmp6_hdr *icmp6; 45542393Syz155240 u_short *csump; 45552393Syz155240 tcphdr_t *tcp; 45562393Syz155240 udphdr_t *udp; 45572393Syz155240 45582393Syz155240 csump = NULL; 45592393Syz155240 if (fin->fin_out == 0) { 45602393Syz155240 fin->fin_rev = (nat->nat_dir == NAT_OUTBOUND); 45612393Syz155240 } else { 45622393Syz155240 fin->fin_rev = (nat->nat_dir == NAT_INBOUND); 45632393Syz155240 } 45642393Syz155240 45652393Syz155240 switch (fin->fin_p) 45662393Syz155240 { 45672393Syz155240 case IPPROTO_TCP : 45682393Syz155240 tcp = fin->fin_dp; 45692393Syz155240 45702393Syz155240 csump = &tcp->th_sum; 45712393Syz155240 45722393Syz155240 /* 45732393Syz155240 * Do a MSS CLAMPING on a SYN packet, 45742393Syz155240 * only deal IPv4 for now. 45752393Syz155240 */ 45762393Syz155240 if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0) 45772958Sdr146992 nat_mssclamp(tcp, nat->nat_mssclamp, csump); 45782393Syz155240 45792393Syz155240 break; 45802393Syz155240 45812393Syz155240 case IPPROTO_UDP : 45822393Syz155240 udp = fin->fin_dp; 45832393Syz155240 45842393Syz155240 if (udp->uh_sum) 45852393Syz155240 csump = &udp->uh_sum; 45862393Syz155240 break; 45872393Syz155240 45882393Syz155240 case IPPROTO_ICMP : 45892393Syz155240 icmp = fin->fin_dp; 45902393Syz155240 45912393Syz155240 if ((nflags & IPN_ICMPQUERY) != 0) { 45922393Syz155240 if (icmp->icmp_cksum != 0) 45932393Syz155240 csump = &icmp->icmp_cksum; 45942393Syz155240 } 45952393Syz155240 break; 45967176Syx160601 45977176Syx160601 case IPPROTO_ICMPV6 : 45987176Syx160601 icmp6 = fin->fin_dp; 45997176Syx160601 46007176Syx160601 if ((nflags & IPN_ICMPQUERY) != 0) { 46017176Syx160601 if (icmp6->icmp6_cksum != 0) 46027176Syx160601 csump = &icmp6->icmp6_cksum; 46037176Syx160601 } 46047176Syx160601 break; 46052393Syz155240 } 46062393Syz155240 return csump; 46072393Syz155240 } 46082393Syz155240 46092393Syz155240 46102393Syz155240 /* ------------------------------------------------------------------------ */ 46112393Syz155240 /* Function: fr_natunload */ 46122393Syz155240 /* Returns: Nil */ 46132393Syz155240 /* Parameters: Nil */ 46142393Syz155240 /* */ 46152393Syz155240 /* Free all memory used by NAT structures allocated at runtime. */ 46162393Syz155240 /* ------------------------------------------------------------------------ */ 46173448Sdh155122 void fr_natunload(ifs) 46183448Sdh155122 ipf_stack_t *ifs; 46192393Syz155240 { 46202393Syz155240 ipftq_t *ifq, *ifqnext; 46212393Syz155240 46223448Sdh155122 (void) nat_clearlist(ifs); 46233448Sdh155122 (void) nat_flushtable(ifs); 46242393Syz155240 46252393Syz155240 /* 46262393Syz155240 * Proxy timeout queues are not cleaned here because although they 46272393Syz155240 * exist on the NAT list, appr_unload is called after fr_natunload 46282393Syz155240 * and the proxies actually are responsible for them being created. 46292393Syz155240 * Should the proxy timeouts have their own list? There's no real 46302393Syz155240 * justification as this is the only complication. 46312393Syz155240 */ 46323448Sdh155122 for (ifq = ifs->ifs_nat_utqe; ifq != NULL; ifq = ifqnext) { 46332393Syz155240 ifqnext = ifq->ifq_next; 46342393Syz155240 if (((ifq->ifq_flags & IFQF_PROXY) == 0) && 46352393Syz155240 (fr_deletetimeoutqueue(ifq) == 0)) 46363448Sdh155122 fr_freetimeoutqueue(ifq, ifs); 46372393Syz155240 } 46382393Syz155240 46393448Sdh155122 if (ifs->ifs_nat_table[0] != NULL) { 46403448Sdh155122 KFREES(ifs->ifs_nat_table[0], 46413448Sdh155122 sizeof(nat_t *) * ifs->ifs_ipf_nattable_sz); 46423448Sdh155122 ifs->ifs_nat_table[0] = NULL; 46432393Syz155240 } 46443448Sdh155122 if (ifs->ifs_nat_table[1] != NULL) { 46453448Sdh155122 KFREES(ifs->ifs_nat_table[1], 46463448Sdh155122 sizeof(nat_t *) * ifs->ifs_ipf_nattable_sz); 46473448Sdh155122 ifs->ifs_nat_table[1] = NULL; 46482393Syz155240 } 46493448Sdh155122 if (ifs->ifs_nat_rules != NULL) { 46503448Sdh155122 KFREES(ifs->ifs_nat_rules, 46513448Sdh155122 sizeof(ipnat_t *) * ifs->ifs_ipf_natrules_sz); 46523448Sdh155122 ifs->ifs_nat_rules = NULL; 46532393Syz155240 } 46543448Sdh155122 if (ifs->ifs_rdr_rules != NULL) { 46553448Sdh155122 KFREES(ifs->ifs_rdr_rules, 46563448Sdh155122 sizeof(ipnat_t *) * ifs->ifs_ipf_rdrrules_sz); 46573448Sdh155122 ifs->ifs_rdr_rules = NULL; 46582393Syz155240 } 46593448Sdh155122 if (ifs->ifs_maptable != NULL) { 46603448Sdh155122 KFREES(ifs->ifs_maptable, 46613448Sdh155122 sizeof(hostmap_t *) * ifs->ifs_ipf_hostmap_sz); 46623448Sdh155122 ifs->ifs_maptable = NULL; 46632393Syz155240 } 46643448Sdh155122 if (ifs->ifs_nat_stats.ns_bucketlen[0] != NULL) { 46653448Sdh155122 KFREES(ifs->ifs_nat_stats.ns_bucketlen[0], 46663448Sdh155122 sizeof(u_long *) * ifs->ifs_ipf_nattable_sz); 46673448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[0] = NULL; 46682393Syz155240 } 46693448Sdh155122 if (ifs->ifs_nat_stats.ns_bucketlen[1] != NULL) { 46703448Sdh155122 KFREES(ifs->ifs_nat_stats.ns_bucketlen[1], 46713448Sdh155122 sizeof(u_long *) * ifs->ifs_ipf_nattable_sz); 46723448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[1] = NULL; 46732393Syz155240 } 46742393Syz155240 46753448Sdh155122 if (ifs->ifs_fr_nat_maxbucket_reset == 1) 46763448Sdh155122 ifs->ifs_fr_nat_maxbucket = 0; 46773448Sdh155122 46783448Sdh155122 if (ifs->ifs_fr_nat_init == 1) { 46793448Sdh155122 ifs->ifs_fr_nat_init = 0; 46803448Sdh155122 fr_sttab_destroy(ifs->ifs_nat_tqb); 46813448Sdh155122 46823448Sdh155122 RW_DESTROY(&ifs->ifs_ipf_natfrag); 46833448Sdh155122 RW_DESTROY(&ifs->ifs_ipf_nat); 46843448Sdh155122 46853448Sdh155122 MUTEX_DESTROY(&ifs->ifs_ipf_nat_new); 46863448Sdh155122 MUTEX_DESTROY(&ifs->ifs_ipf_natio); 46873448Sdh155122 46883448Sdh155122 MUTEX_DESTROY(&ifs->ifs_nat_udptq.ifq_lock); 46893448Sdh155122 MUTEX_DESTROY(&ifs->ifs_nat_icmptq.ifq_lock); 46903448Sdh155122 MUTEX_DESTROY(&ifs->ifs_nat_iptq.ifq_lock); 46912393Syz155240 } 46922393Syz155240 } 46932393Syz155240 46942393Syz155240 46952393Syz155240 /* ------------------------------------------------------------------------ */ 46962393Syz155240 /* Function: fr_natexpire */ 46972393Syz155240 /* Returns: Nil */ 46982393Syz155240 /* Parameters: Nil */ 46992393Syz155240 /* */ 47002393Syz155240 /* Check all of the timeout queues for entries at the top which need to be */ 47012393Syz155240 /* expired. */ 47022393Syz155240 /* ------------------------------------------------------------------------ */ 47033448Sdh155122 void fr_natexpire(ifs) 47043448Sdh155122 ipf_stack_t *ifs; 47052393Syz155240 { 47062393Syz155240 ipftq_t *ifq, *ifqnext; 47072393Syz155240 ipftqent_t *tqe, *tqn; 47082393Syz155240 int i; 47092393Syz155240 SPL_INT(s); 47102393Syz155240 47112393Syz155240 SPL_NET(s); 47123448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 47133448Sdh155122 for (ifq = ifs->ifs_nat_tqb, i = 0; ifq != NULL; ifq = ifq->ifq_next) { 47142393Syz155240 for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { 47153448Sdh155122 if (tqe->tqe_die > ifs->ifs_fr_ticks) 47162393Syz155240 break; 47172393Syz155240 tqn = tqe->tqe_next; 47183448Sdh155122 nat_delete(tqe->tqe_parent, NL_EXPIRE, ifs); 47192393Syz155240 } 47202393Syz155240 } 47212393Syz155240 47223448Sdh155122 for (ifq = ifs->ifs_nat_utqe; ifq != NULL; ifq = ifqnext) { 47232393Syz155240 ifqnext = ifq->ifq_next; 47242393Syz155240 47252393Syz155240 for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { 47263448Sdh155122 if (tqe->tqe_die > ifs->ifs_fr_ticks) 47272393Syz155240 break; 47282393Syz155240 tqn = tqe->tqe_next; 47293448Sdh155122 nat_delete(tqe->tqe_parent, NL_EXPIRE, ifs); 47302393Syz155240 } 47312393Syz155240 } 47322393Syz155240 47333448Sdh155122 for (ifq = ifs->ifs_nat_utqe; ifq != NULL; ifq = ifqnext) { 47342393Syz155240 ifqnext = ifq->ifq_next; 47352393Syz155240 47362393Syz155240 if (((ifq->ifq_flags & IFQF_DELETE) != 0) && 47372393Syz155240 (ifq->ifq_ref == 0)) { 47383448Sdh155122 fr_freetimeoutqueue(ifq, ifs); 47392393Syz155240 } 47402393Syz155240 } 47412393Syz155240 47424817San207044 if (ifs->ifs_nat_doflush != 0) { 47434817San207044 (void) nat_extraflush(2, ifs); 47444817San207044 ifs->ifs_nat_doflush = 0; 47454817San207044 } 47464817San207044 47473448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 47482393Syz155240 SPL_X(s); 47492393Syz155240 } 47502393Syz155240 47512393Syz155240 47522393Syz155240 /* ------------------------------------------------------------------------ */ 47532958Sdr146992 /* Function: fr_nataddrsync */ 47542393Syz155240 /* Returns: Nil */ 47552958Sdr146992 /* Parameters: ifp(I) - pointer to network interface */ 47562958Sdr146992 /* addr(I) - pointer to new network address */ 47572393Syz155240 /* */ 47582393Syz155240 /* Walk through all of the currently active NAT sessions, looking for those */ 47592958Sdr146992 /* which need to have their translated address updated (where the interface */ 47602958Sdr146992 /* matches the one passed in) and change it, recalculating the checksum sum */ 47612958Sdr146992 /* difference too. */ 47622393Syz155240 /* ------------------------------------------------------------------------ */ 47637176Syx160601 void fr_nataddrsync(v, ifp, addr, ifs) 47647176Syx160601 int v; 47652393Syz155240 void *ifp; 47667176Syx160601 void *addr; 47673448Sdh155122 ipf_stack_t *ifs; 47682393Syz155240 { 47692393Syz155240 u_32_t sum1, sum2, sumd; 47702393Syz155240 nat_t *nat; 47712958Sdr146992 ipnat_t *np; 47722393Syz155240 SPL_INT(s); 47732393Syz155240 47743448Sdh155122 if (ifs->ifs_fr_running <= 0) 47752393Syz155240 return; 47762393Syz155240 47772393Syz155240 SPL_NET(s); 47783448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 47793448Sdh155122 47803448Sdh155122 if (ifs->ifs_fr_running <= 0) { 47813448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 47822393Syz155240 return; 47832393Syz155240 } 47842393Syz155240 47852958Sdr146992 /* 47862958Sdr146992 * Change IP addresses for NAT sessions for any protocol except TCP 47872958Sdr146992 * since it will break the TCP connection anyway. The only rules 47882958Sdr146992 * which will get changed are those which are "map ... -> 0/32", 47892958Sdr146992 * where the rule specifies the address is taken from the interface. 47902958Sdr146992 */ 47913448Sdh155122 for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) { 47922958Sdr146992 if (addr != NULL) { 47932958Sdr146992 if (((ifp != NULL) && ifp != (nat->nat_ifps[0])) || 47942958Sdr146992 ((nat->nat_flags & IPN_TCP) != 0)) 47952958Sdr146992 continue; 47967176Syx160601 if ((np = nat->nat_ptr) == NULL) 47972393Syz155240 continue; 47987176Syx160601 if (v == 4 && np->in_v == 4) { 47997176Syx160601 if (np->in_nip || np->in_outmsk != 0xffffffff) 48007176Syx160601 continue; 48017176Syx160601 /* 48027176Syx160601 * Change the map-to address to be the same as 48037176Syx160601 * the new one. 48047176Syx160601 */ 48057176Syx160601 sum1 = nat->nat_outip.s_addr; 48067176Syx160601 nat->nat_outip = *(struct in_addr *)addr; 48077176Syx160601 sum2 = nat->nat_outip.s_addr; 48087176Syx160601 } else if (v == 6 && np->in_v == 6) { 48097176Syx160601 if (!IP6_ISZERO(&np->in_next6.in6) || 48107176Syx160601 !IP6_ISONES(&np->in_out[1].in6)) 48117176Syx160601 continue; 48127176Syx160601 /* 48137176Syx160601 * Change the map-to address to be the same as 48147176Syx160601 * the new one. 48157176Syx160601 */ 48167176Syx160601 nat->nat_outip6.in6 = *(struct in6_addr *)addr; 48177176Syx160601 } else 48187176Syx160601 continue; 48192958Sdr146992 48202958Sdr146992 } else if (((ifp == NULL) || (ifp == nat->nat_ifps[0])) && 48217176Syx160601 !(nat->nat_flags & IPN_TCP) && (np = nat->nat_ptr)) { 48227176Syx160601 if (np->in_v == 4 && (v == 4 || v == 0)) { 48237176Syx160601 struct in_addr in; 48247176Syx160601 if (np->in_outmsk != 0xffffffff || np->in_nip) 48257176Syx160601 continue; 48267176Syx160601 /* 48277176Syx160601 * Change the map-to address to be the same as 48287176Syx160601 * the new one. 48297176Syx160601 */ 48307176Syx160601 sum1 = nat->nat_outip.s_addr; 48317176Syx160601 if (fr_ifpaddr(4, FRI_NORMAL, nat->nat_ifps[0], 48327176Syx160601 &in, NULL, ifs) != -1) 48337176Syx160601 nat->nat_outip = in; 48347176Syx160601 sum2 = nat->nat_outip.s_addr; 48357176Syx160601 } else if (np->in_v == 6 && (v == 6 || v == 0)) { 48367176Syx160601 struct in6_addr in6; 48377176Syx160601 if (!IP6_ISZERO(&np->in_next6.in6) || 48387176Syx160601 !IP6_ISONES(&np->in_out[1].in6)) 48397176Syx160601 continue; 48407176Syx160601 /* 48417176Syx160601 * Change the map-to address to be the same as 48427176Syx160601 * the new one. 48437176Syx160601 */ 48447176Syx160601 if (fr_ifpaddr(6, FRI_NORMAL, nat->nat_ifps[0], 48457176Syx160601 (void *)&in6, NULL, ifs) != -1) 48467176Syx160601 nat->nat_outip6.in6 = in6; 48477176Syx160601 } else 48487176Syx160601 continue; 48492958Sdr146992 } else { 48502958Sdr146992 continue; 48512393Syz155240 } 48522958Sdr146992 48532958Sdr146992 if (sum1 == sum2) 48542958Sdr146992 continue; 48552958Sdr146992 /* 48562958Sdr146992 * Readjust the checksum adjustment to take into 48572958Sdr146992 * account the new IP#. 48582958Sdr146992 */ 48592958Sdr146992 CALC_SUMD(sum1, sum2, sumd); 48602958Sdr146992 /* XXX - dont change for TCP when solaris does 48612958Sdr146992 * hardware checksumming. 48622958Sdr146992 */ 48632958Sdr146992 sumd += nat->nat_sumd[0]; 48642958Sdr146992 nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 48652958Sdr146992 nat->nat_sumd[1] = nat->nat_sumd[0]; 48662393Syz155240 } 48672393Syz155240 48683448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 48692958Sdr146992 SPL_X(s); 48702958Sdr146992 } 48712958Sdr146992 48722958Sdr146992 48732958Sdr146992 /* ------------------------------------------------------------------------ */ 48742958Sdr146992 /* Function: fr_natifpsync */ 48752958Sdr146992 /* Returns: Nil */ 48762958Sdr146992 /* Parameters: action(I) - how we are syncing */ 48772958Sdr146992 /* ifp(I) - pointer to network interface */ 48782958Sdr146992 /* name(I) - name of interface to sync to */ 48792958Sdr146992 /* */ 48802958Sdr146992 /* This function is used to resync the mapping of interface names and their */ 48812958Sdr146992 /* respective 'pointers'. For "action == IPFSYNC_RESYNC", resync all */ 48822958Sdr146992 /* interfaces by doing a new lookup of name to 'pointer'. For "action == */ 48832958Sdr146992 /* IPFSYNC_NEWIFP", treat ifp as the new pointer value associated with */ 48842958Sdr146992 /* "name" and for "action == IPFSYNC_OLDIFP", ifp is a pointer for which */ 48852958Sdr146992 /* there is no longer any interface associated with it. */ 48862958Sdr146992 /* ------------------------------------------------------------------------ */ 48877176Syx160601 void fr_natifpsync(action, v, ifp, name, ifs) 48887176Syx160601 int action, v; 48892958Sdr146992 void *ifp; 48902958Sdr146992 char *name; 48913448Sdh155122 ipf_stack_t *ifs; 48922958Sdr146992 { 48932958Sdr146992 #if defined(_KERNEL) && !defined(MENTAT) && defined(USE_SPL) 48942958Sdr146992 int s; 48952958Sdr146992 #endif 48962958Sdr146992 nat_t *nat; 48972958Sdr146992 ipnat_t *n; 48987176Syx160601 int nv; 48992958Sdr146992 49003448Sdh155122 if (ifs->ifs_fr_running <= 0) 49012958Sdr146992 return; 49022958Sdr146992 49032958Sdr146992 SPL_NET(s); 49043448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 49053448Sdh155122 49063448Sdh155122 if (ifs->ifs_fr_running <= 0) { 49073448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 49082958Sdr146992 return; 49092958Sdr146992 } 49102958Sdr146992 49112958Sdr146992 switch (action) 49122958Sdr146992 { 49132958Sdr146992 case IPFSYNC_RESYNC : 49143448Sdh155122 for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) { 49157176Syx160601 nv = (v == 0) ? nat->nat_v : v; 49167176Syx160601 if (nat->nat_v != nv) 49177176Syx160601 continue; 49182958Sdr146992 if ((ifp == nat->nat_ifps[0]) || 49192958Sdr146992 (nat->nat_ifps[0] == (void *)-1)) { 49202958Sdr146992 nat->nat_ifps[0] = 49217176Syx160601 fr_resolvenic(nat->nat_ifnames[0], nv, ifs); 49222958Sdr146992 } 49232958Sdr146992 49242958Sdr146992 if ((ifp == nat->nat_ifps[1]) || 49252958Sdr146992 (nat->nat_ifps[1] == (void *)-1)) { 49262958Sdr146992 nat->nat_ifps[1] = 49277176Syx160601 fr_resolvenic(nat->nat_ifnames[1], nv, ifs); 49282958Sdr146992 } 49292958Sdr146992 } 49302958Sdr146992 49313448Sdh155122 for (n = ifs->ifs_nat_list; (n != NULL); n = n->in_next) { 49327176Syx160601 nv = (v == 0) ? (int)n->in_v : v; 49337176Syx160601 if ((int)n->in_v != nv) 49347176Syx160601 continue; 49352958Sdr146992 if (n->in_ifps[0] == ifp || 49362958Sdr146992 n->in_ifps[0] == (void *)-1) { 49372958Sdr146992 n->in_ifps[0] = 49387176Syx160601 fr_resolvenic(n->in_ifnames[0], nv, ifs); 49392958Sdr146992 } 49402958Sdr146992 if (n->in_ifps[1] == ifp || 49412958Sdr146992 n->in_ifps[1] == (void *)-1) { 49422958Sdr146992 n->in_ifps[1] = 49437176Syx160601 fr_resolvenic(n->in_ifnames[1], nv, ifs); 49442958Sdr146992 } 49452958Sdr146992 } 49462958Sdr146992 break; 49472958Sdr146992 case IPFSYNC_NEWIFP : 49483448Sdh155122 for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) { 49497176Syx160601 if (nat->nat_v != v) 49507176Syx160601 continue; 49512958Sdr146992 if (!strncmp(name, nat->nat_ifnames[0], 49522958Sdr146992 sizeof(nat->nat_ifnames[0]))) 49532958Sdr146992 nat->nat_ifps[0] = ifp; 49542958Sdr146992 if (!strncmp(name, nat->nat_ifnames[1], 49552958Sdr146992 sizeof(nat->nat_ifnames[1]))) 49562958Sdr146992 nat->nat_ifps[1] = ifp; 49572958Sdr146992 } 49583448Sdh155122 for (n = ifs->ifs_nat_list; (n != NULL); n = n->in_next) { 49597176Syx160601 if ((int)n->in_v != v) 49607176Syx160601 continue; 49612958Sdr146992 if (!strncmp(name, n->in_ifnames[0], 49622958Sdr146992 sizeof(n->in_ifnames[0]))) 49632958Sdr146992 n->in_ifps[0] = ifp; 49642958Sdr146992 if (!strncmp(name, n->in_ifnames[1], 49652958Sdr146992 sizeof(n->in_ifnames[1]))) 49662958Sdr146992 n->in_ifps[1] = ifp; 49672958Sdr146992 } 49682958Sdr146992 break; 49692958Sdr146992 case IPFSYNC_OLDIFP : 49703448Sdh155122 for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) { 49717176Syx160601 if (nat->nat_v != v) 49727176Syx160601 continue; 49732958Sdr146992 if (ifp == nat->nat_ifps[0]) 49742958Sdr146992 nat->nat_ifps[0] = (void *)-1; 49752958Sdr146992 if (ifp == nat->nat_ifps[1]) 49762958Sdr146992 nat->nat_ifps[1] = (void *)-1; 49772958Sdr146992 } 49783448Sdh155122 for (n = ifs->ifs_nat_list; (n != NULL); n = n->in_next) { 49797176Syx160601 if ((int)n->in_v != v) 49807176Syx160601 continue; 49812958Sdr146992 if (n->in_ifps[0] == ifp) 49822958Sdr146992 n->in_ifps[0] = (void *)-1; 49832958Sdr146992 if (n->in_ifps[1] == ifp) 49842958Sdr146992 n->in_ifps[1] = (void *)-1; 49852958Sdr146992 } 49862958Sdr146992 break; 49872393Syz155240 } 49883448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 49892393Syz155240 SPL_X(s); 49902393Syz155240 } 49912393Syz155240 49922393Syz155240 49932393Syz155240 /* ------------------------------------------------------------------------ */ 49942393Syz155240 /* Function: nat_icmpquerytype4 */ 49952393Syz155240 /* Returns: int - 1 == success, 0 == failure */ 49962393Syz155240 /* Parameters: icmptype(I) - ICMP type number */ 49972393Syz155240 /* */ 49982393Syz155240 /* Tests to see if the ICMP type number passed is a query/response type or */ 49992393Syz155240 /* not. */ 50002393Syz155240 /* ------------------------------------------------------------------------ */ 50012393Syz155240 static INLINE int nat_icmpquerytype4(icmptype) 50022393Syz155240 int icmptype; 50032393Syz155240 { 50042393Syz155240 50052393Syz155240 /* 50062393Syz155240 * For the ICMP query NAT code, it is essential that both the query 50072393Syz155240 * and the reply match on the NAT rule. Because the NAT structure 50082393Syz155240 * does not keep track of the icmptype, and a single NAT structure 50092393Syz155240 * is used for all icmp types with the same src, dest and id, we 50102393Syz155240 * simply define the replies as queries as well. The funny thing is, 50112393Syz155240 * altough it seems silly to call a reply a query, this is exactly 50122393Syz155240 * as it is defined in the IPv4 specification 50132393Syz155240 */ 50142393Syz155240 50152393Syz155240 switch (icmptype) 50162393Syz155240 { 50172393Syz155240 50182393Syz155240 case ICMP_ECHOREPLY: 50192393Syz155240 case ICMP_ECHO: 50202393Syz155240 /* route aedvertisement/solliciation is currently unsupported: */ 50212393Syz155240 /* it would require rewriting the ICMP data section */ 50222393Syz155240 case ICMP_TSTAMP: 50232393Syz155240 case ICMP_TSTAMPREPLY: 50242393Syz155240 case ICMP_IREQ: 50252393Syz155240 case ICMP_IREQREPLY: 50262393Syz155240 case ICMP_MASKREQ: 50272393Syz155240 case ICMP_MASKREPLY: 50282393Syz155240 return 1; 50292393Syz155240 default: 50302393Syz155240 return 0; 50312393Syz155240 } 50322393Syz155240 } 50332393Syz155240 50342393Syz155240 50352393Syz155240 /* ------------------------------------------------------------------------ */ 50362393Syz155240 /* Function: nat_log */ 50372393Syz155240 /* Returns: Nil */ 50382393Syz155240 /* Parameters: nat(I) - pointer to NAT structure */ 50392393Syz155240 /* type(I) - type of log entry to create */ 50402393Syz155240 /* */ 50412393Syz155240 /* Creates a NAT log entry. */ 50422393Syz155240 /* ------------------------------------------------------------------------ */ 50433448Sdh155122 void nat_log(nat, type, ifs) 50442393Syz155240 struct nat *nat; 50452393Syz155240 u_int type; 50463448Sdh155122 ipf_stack_t *ifs; 50472393Syz155240 { 50482393Syz155240 #ifdef IPFILTER_LOG 50492393Syz155240 # ifndef LARGE_NAT 50502393Syz155240 struct ipnat *np; 50512393Syz155240 int rulen; 50522393Syz155240 # endif 50532393Syz155240 struct natlog natl; 50542393Syz155240 void *items[1]; 50552393Syz155240 size_t sizes[1]; 50562393Syz155240 int types[1]; 50572393Syz155240 50587176Syx160601 natl.nlg_inip = nat->nat_inip6; 50597176Syx160601 natl.nlg_outip = nat->nat_outip6; 50607176Syx160601 natl.nlg_origip = nat->nat_oip6; 50617176Syx160601 natl.nlg_bytes[0] = nat->nat_bytes[0]; 50627176Syx160601 natl.nlg_bytes[1] = nat->nat_bytes[1]; 50637176Syx160601 natl.nlg_pkts[0] = nat->nat_pkts[0]; 50647176Syx160601 natl.nlg_pkts[1] = nat->nat_pkts[1]; 50657176Syx160601 natl.nlg_origport = nat->nat_oport; 50667176Syx160601 natl.nlg_inport = nat->nat_inport; 50677176Syx160601 natl.nlg_outport = nat->nat_outport; 50687176Syx160601 natl.nlg_p = nat->nat_p; 50697176Syx160601 natl.nlg_type = type; 50707176Syx160601 natl.nlg_rule = -1; 50717176Syx160601 natl.nlg_v = nat->nat_v; 50722393Syz155240 # ifndef LARGE_NAT 50732393Syz155240 if (nat->nat_ptr != NULL) { 50743448Sdh155122 for (rulen = 0, np = ifs->ifs_nat_list; np; 50753448Sdh155122 np = np->in_next, rulen++) 50762393Syz155240 if (np == nat->nat_ptr) { 50777176Syx160601 natl.nlg_rule = rulen; 50782393Syz155240 break; 50792393Syz155240 } 50802393Syz155240 } 50812393Syz155240 # endif 50822393Syz155240 items[0] = &natl; 50832393Syz155240 sizes[0] = sizeof(natl); 50842393Syz155240 types[0] = 0; 50852393Syz155240 50863448Sdh155122 (void) ipllog(IPL_LOGNAT, NULL, items, sizes, types, 1, ifs); 50872393Syz155240 #endif 50882393Syz155240 } 50892393Syz155240 50902393Syz155240 50912393Syz155240 #if defined(__OpenBSD__) 50922393Syz155240 /* ------------------------------------------------------------------------ */ 50932393Syz155240 /* Function: nat_ifdetach */ 50942393Syz155240 /* Returns: Nil */ 50952393Syz155240 /* Parameters: ifp(I) - pointer to network interface */ 50962393Syz155240 /* */ 50972393Syz155240 /* Compatibility interface for OpenBSD to trigger the correct updating of */ 50982393Syz155240 /* interface references within IPFilter. */ 50992393Syz155240 /* ------------------------------------------------------------------------ */ 51003448Sdh155122 void nat_ifdetach(ifp, ifs) 51012393Syz155240 void *ifp; 51023448Sdh155122 ipf_stack_t *ifs; 51032393Syz155240 { 51043448Sdh155122 frsync(ifp, ifs); 51052393Syz155240 return; 51062393Syz155240 } 51072393Syz155240 #endif 51082393Syz155240 51092393Syz155240 51102393Syz155240 /* ------------------------------------------------------------------------ */ 51113448Sdh155122 /* Function: fr_ipnatderef */ 51123448Sdh155122 /* Returns: Nil */ 51137131Sdr146992 /* Parameters: inp(I) - pointer to pointer to NAT rule */ 51143448Sdh155122 /* Write Locks: ipf_nat */ 51153448Sdh155122 /* */ 51163448Sdh155122 /* ------------------------------------------------------------------------ */ 51173448Sdh155122 void fr_ipnatderef(inp, ifs) 51183448Sdh155122 ipnat_t **inp; 51193448Sdh155122 ipf_stack_t *ifs; 51203448Sdh155122 { 51213448Sdh155122 ipnat_t *in; 51223448Sdh155122 51233448Sdh155122 in = *inp; 51243448Sdh155122 *inp = NULL; 51253448Sdh155122 in->in_use--; 51263448Sdh155122 if (in->in_use == 0 && (in->in_flags & IPN_DELETE)) { 51273448Sdh155122 if (in->in_apr) 51283448Sdh155122 appr_free(in->in_apr); 51293448Sdh155122 KFREE(in); 51303448Sdh155122 ifs->ifs_nat_stats.ns_rules--; 51313448Sdh155122 #ifdef notdef 51323448Sdh155122 #if SOLARIS 51333448Sdh155122 if (ifs->ifs_nat_stats.ns_rules == 0) 51343448Sdh155122 ifs->ifs_pfil_delayed_copy = 1; 51353448Sdh155122 #endif 51363448Sdh155122 #endif 51373448Sdh155122 } 51383448Sdh155122 } 51393448Sdh155122 51403448Sdh155122 51413448Sdh155122 /* ------------------------------------------------------------------------ */ 51422393Syz155240 /* Function: fr_natderef */ 51432393Syz155240 /* Returns: Nil */ 51442393Syz155240 /* Parameters: isp(I) - pointer to pointer to NAT table entry */ 51452393Syz155240 /* */ 51462393Syz155240 /* Decrement the reference counter for this NAT table entry and free it if */ 51472393Syz155240 /* there are no more things using it. */ 51485055Sdr146992 /* */ 51495055Sdr146992 /* IF nat_ref == 1 when this function is called, then we have an orphan nat */ 51505055Sdr146992 /* structure *because* it only gets called on paths _after_ nat_ref has been*/ 51515055Sdr146992 /* incremented. If nat_ref == 1 then we shouldn't decrement it here */ 51525055Sdr146992 /* because nat_delete() will do that and send nat_ref to -1. */ 51535055Sdr146992 /* */ 51545055Sdr146992 /* Holding the lock on nat_lock is required to serialise nat_delete() being */ 51555055Sdr146992 /* called from a NAT flush ioctl with a deref happening because of a packet.*/ 51562393Syz155240 /* ------------------------------------------------------------------------ */ 51573448Sdh155122 void fr_natderef(natp, ifs) 51582393Syz155240 nat_t **natp; 51593448Sdh155122 ipf_stack_t *ifs; 51602393Syz155240 { 51612393Syz155240 nat_t *nat; 51622393Syz155240 51632393Syz155240 nat = *natp; 51642393Syz155240 *natp = NULL; 51655055Sdr146992 51665055Sdr146992 MUTEX_ENTER(&nat->nat_lock); 51675055Sdr146992 if (nat->nat_ref > 1) { 51685055Sdr146992 nat->nat_ref--; 51695055Sdr146992 MUTEX_EXIT(&nat->nat_lock); 51705055Sdr146992 return; 51715055Sdr146992 } 51725055Sdr146992 MUTEX_EXIT(&nat->nat_lock); 51735055Sdr146992 51743448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 51755055Sdr146992 nat_delete(nat, NL_EXPIRE, ifs); 51763448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 51772393Syz155240 } 51782393Syz155240 51792393Syz155240 51802393Syz155240 /* ------------------------------------------------------------------------ */ 51812393Syz155240 /* Function: fr_natclone */ 51822393Syz155240 /* Returns: ipstate_t* - NULL == cloning failed, */ 51832393Syz155240 /* else pointer to new state structure */ 51842393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 51852393Syz155240 /* is(I) - pointer to master state structure */ 51862393Syz155240 /* Write Lock: ipf_nat */ 51872393Syz155240 /* */ 51882393Syz155240 /* Create a "duplcate" state table entry from the master. */ 51892393Syz155240 /* ------------------------------------------------------------------------ */ 51907176Syx160601 nat_t *fr_natclone(fin, nat) 51912393Syz155240 fr_info_t *fin; 51922393Syz155240 nat_t *nat; 51932393Syz155240 { 51942393Syz155240 frentry_t *fr; 51952393Syz155240 nat_t *clone; 51962393Syz155240 ipnat_t *np; 51973448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 51982393Syz155240 51992393Syz155240 KMALLOC(clone, nat_t *); 52002393Syz155240 if (clone == NULL) 52012393Syz155240 return NULL; 52022393Syz155240 bcopy((char *)nat, (char *)clone, sizeof(*clone)); 52032393Syz155240 52042393Syz155240 MUTEX_NUKE(&clone->nat_lock); 52052393Syz155240 52062393Syz155240 clone->nat_aps = NULL; 52072393Syz155240 /* 52082393Syz155240 * Initialize all these so that nat_delete() doesn't cause a crash. 52092393Syz155240 */ 52102393Syz155240 clone->nat_tqe.tqe_pnext = NULL; 52112393Syz155240 clone->nat_tqe.tqe_next = NULL; 52122393Syz155240 clone->nat_tqe.tqe_ifq = NULL; 52132393Syz155240 clone->nat_tqe.tqe_parent = clone; 52142393Syz155240 52152393Syz155240 clone->nat_flags &= ~SI_CLONE; 52162393Syz155240 clone->nat_flags |= SI_CLONED; 52172393Syz155240 52182393Syz155240 if (clone->nat_hm) 52192393Syz155240 clone->nat_hm->hm_ref++; 52202393Syz155240 52213448Sdh155122 if (nat_insert(clone, fin->fin_rev, ifs) == -1) { 52222393Syz155240 KFREE(clone); 52232393Syz155240 return NULL; 52242393Syz155240 } 52252393Syz155240 np = clone->nat_ptr; 52262393Syz155240 if (np != NULL) { 52273448Sdh155122 if (ifs->ifs_nat_logging) 52283448Sdh155122 nat_log(clone, (u_int)np->in_redir, ifs); 52292393Syz155240 np->in_use++; 52302393Syz155240 } 52312393Syz155240 fr = clone->nat_fr; 52322393Syz155240 if (fr != NULL) { 52332393Syz155240 MUTEX_ENTER(&fr->fr_lock); 52342393Syz155240 fr->fr_ref++; 52352393Syz155240 MUTEX_EXIT(&fr->fr_lock); 52362393Syz155240 } 52372393Syz155240 52382393Syz155240 /* 52392393Syz155240 * Because the clone is created outside the normal loop of things and 52402393Syz155240 * TCP has special needs in terms of state, initialise the timeout 52412393Syz155240 * state of the new NAT from here. 52422393Syz155240 */ 52432393Syz155240 if (clone->nat_p == IPPROTO_TCP) { 52443448Sdh155122 (void) fr_tcp_age(&clone->nat_tqe, fin, ifs->ifs_nat_tqb, 52452393Syz155240 clone->nat_flags); 52462393Syz155240 } 52472393Syz155240 #ifdef IPFILTER_SYNC 52482393Syz155240 clone->nat_sync = ipfsync_new(SMC_NAT, fin, clone); 52492393Syz155240 #endif 52503448Sdh155122 if (ifs->ifs_nat_logging) 52513448Sdh155122 nat_log(clone, NL_CLONE, ifs); 52522393Syz155240 return clone; 52532393Syz155240 } 52542393Syz155240 52552393Syz155240 52562393Syz155240 /* ------------------------------------------------------------------------ */ 52572393Syz155240 /* Function: nat_wildok */ 52582393Syz155240 /* Returns: int - 1 == packet's ports match wildcards */ 52592393Syz155240 /* 0 == packet's ports don't match wildcards */ 52602393Syz155240 /* Parameters: nat(I) - NAT entry */ 52612393Syz155240 /* sport(I) - source port */ 52622393Syz155240 /* dport(I) - destination port */ 52632393Syz155240 /* flags(I) - wildcard flags */ 52642393Syz155240 /* dir(I) - packet direction */ 52652393Syz155240 /* */ 52662393Syz155240 /* Use NAT entry and packet direction to determine which combination of */ 52672393Syz155240 /* wildcard flags should be used. */ 52682393Syz155240 /* ------------------------------------------------------------------------ */ 52697176Syx160601 int nat_wildok(nat, sport, dport, flags, dir) 52702393Syz155240 nat_t *nat; 52712393Syz155240 int sport; 52722393Syz155240 int dport; 52732393Syz155240 int flags; 52742393Syz155240 int dir; 52752393Syz155240 { 52762393Syz155240 /* 52772393Syz155240 * When called by dir is set to 52782393Syz155240 * nat_inlookup NAT_INBOUND (0) 52792393Syz155240 * nat_outlookup NAT_OUTBOUND (1) 52802393Syz155240 * 52812393Syz155240 * We simply combine the packet's direction in dir with the original 52822393Syz155240 * "intended" direction of that NAT entry in nat->nat_dir to decide 52832393Syz155240 * which combination of wildcard flags to allow. 52842393Syz155240 */ 52852393Syz155240 52862393Syz155240 switch ((dir << 1) | nat->nat_dir) 52872393Syz155240 { 52882393Syz155240 case 3: /* outbound packet / outbound entry */ 52892393Syz155240 if (((nat->nat_inport == sport) || 52902393Syz155240 (flags & SI_W_SPORT)) && 52912393Syz155240 ((nat->nat_oport == dport) || 52922393Syz155240 (flags & SI_W_DPORT))) 52932393Syz155240 return 1; 52942393Syz155240 break; 52952393Syz155240 case 2: /* outbound packet / inbound entry */ 52962393Syz155240 if (((nat->nat_outport == sport) || 52972393Syz155240 (flags & SI_W_DPORT)) && 52982393Syz155240 ((nat->nat_oport == dport) || 52992393Syz155240 (flags & SI_W_SPORT))) 53002393Syz155240 return 1; 53012393Syz155240 break; 53022393Syz155240 case 1: /* inbound packet / outbound entry */ 53032393Syz155240 if (((nat->nat_oport == sport) || 53042393Syz155240 (flags & SI_W_DPORT)) && 53052393Syz155240 ((nat->nat_outport == dport) || 53062393Syz155240 (flags & SI_W_SPORT))) 53072393Syz155240 return 1; 53082393Syz155240 break; 53092393Syz155240 case 0: /* inbound packet / inbound entry */ 53102393Syz155240 if (((nat->nat_oport == sport) || 53112393Syz155240 (flags & SI_W_SPORT)) && 53122393Syz155240 ((nat->nat_outport == dport) || 53132393Syz155240 (flags & SI_W_DPORT))) 53142393Syz155240 return 1; 53152393Syz155240 break; 53162393Syz155240 default: 53172393Syz155240 break; 53182393Syz155240 } 53192393Syz155240 53202393Syz155240 return(0); 53212393Syz155240 } 53222393Syz155240 53232393Syz155240 53242393Syz155240 /* ------------------------------------------------------------------------ */ 53252393Syz155240 /* Function: nat_mssclamp */ 53262393Syz155240 /* Returns: Nil */ 53272393Syz155240 /* Parameters: tcp(I) - pointer to TCP header */ 53282393Syz155240 /* maxmss(I) - value to clamp the TCP MSS to */ 53292393Syz155240 /* csump(I) - pointer to TCP checksum */ 53302393Syz155240 /* */ 53312393Syz155240 /* Check for MSS option and clamp it if necessary. If found and changed, */ 53322393Syz155240 /* then the TCP header checksum will be updated to reflect the change in */ 53332393Syz155240 /* the MSS. */ 53342393Syz155240 /* ------------------------------------------------------------------------ */ 53352958Sdr146992 static void nat_mssclamp(tcp, maxmss, csump) 53362393Syz155240 tcphdr_t *tcp; 53372393Syz155240 u_32_t maxmss; 53382393Syz155240 u_short *csump; 53392393Syz155240 { 53402393Syz155240 u_char *cp, *ep, opt; 53412393Syz155240 int hlen, advance; 53422393Syz155240 u_32_t mss, sumd; 53432393Syz155240 53442393Syz155240 hlen = TCP_OFF(tcp) << 2; 53452393Syz155240 if (hlen > sizeof(*tcp)) { 53462393Syz155240 cp = (u_char *)tcp + sizeof(*tcp); 53472393Syz155240 ep = (u_char *)tcp + hlen; 53482393Syz155240 53492393Syz155240 while (cp < ep) { 53502393Syz155240 opt = cp[0]; 53512393Syz155240 if (opt == TCPOPT_EOL) 53522393Syz155240 break; 53532393Syz155240 else if (opt == TCPOPT_NOP) { 53542393Syz155240 cp++; 53552393Syz155240 continue; 53562393Syz155240 } 53572393Syz155240 53582393Syz155240 if (cp + 1 >= ep) 53592393Syz155240 break; 53602393Syz155240 advance = cp[1]; 53612393Syz155240 if ((cp + advance > ep) || (advance <= 0)) 53622393Syz155240 break; 53632393Syz155240 switch (opt) 53642393Syz155240 { 53652393Syz155240 case TCPOPT_MAXSEG: 53662393Syz155240 if (advance != 4) 53672393Syz155240 break; 53682393Syz155240 mss = cp[2] * 256 + cp[3]; 53692393Syz155240 if (mss > maxmss) { 53702393Syz155240 cp[2] = maxmss / 256; 53712393Syz155240 cp[3] = maxmss & 0xff; 53722393Syz155240 CALC_SUMD(mss, maxmss, sumd); 53732958Sdr146992 fix_outcksum(csump, sumd); 53742393Syz155240 } 53752393Syz155240 break; 53762393Syz155240 default: 53772393Syz155240 /* ignore unknown options */ 53782393Syz155240 break; 53792393Syz155240 } 53802393Syz155240 53812393Syz155240 cp += advance; 53822393Syz155240 } 53832393Syz155240 } 53842393Syz155240 } 53852393Syz155240 53862393Syz155240 53872393Syz155240 /* ------------------------------------------------------------------------ */ 53882393Syz155240 /* Function: fr_setnatqueue */ 53892393Syz155240 /* Returns: Nil */ 53902393Syz155240 /* Parameters: nat(I)- pointer to NAT structure */ 53912393Syz155240 /* rev(I) - forward(0) or reverse(1) direction */ 53922393Syz155240 /* Locks: ipf_nat (read or write) */ 53932393Syz155240 /* */ 53942393Syz155240 /* Put the NAT entry on its default queue entry, using rev as a helped in */ 53952393Syz155240 /* determining which queue it should be placed on. */ 53962393Syz155240 /* ------------------------------------------------------------------------ */ 53973448Sdh155122 void fr_setnatqueue(nat, rev, ifs) 53982393Syz155240 nat_t *nat; 53992393Syz155240 int rev; 54003448Sdh155122 ipf_stack_t *ifs; 54012393Syz155240 { 54022393Syz155240 ipftq_t *oifq, *nifq; 54032393Syz155240 54042393Syz155240 if (nat->nat_ptr != NULL) 54052393Syz155240 nifq = nat->nat_ptr->in_tqehead[rev]; 54062393Syz155240 else 54072393Syz155240 nifq = NULL; 54082393Syz155240 54092393Syz155240 if (nifq == NULL) { 54102393Syz155240 switch (nat->nat_p) 54112393Syz155240 { 54122393Syz155240 case IPPROTO_UDP : 54133448Sdh155122 nifq = &ifs->ifs_nat_udptq; 54142393Syz155240 break; 54152393Syz155240 case IPPROTO_ICMP : 54163448Sdh155122 nifq = &ifs->ifs_nat_icmptq; 54172393Syz155240 break; 54182393Syz155240 case IPPROTO_TCP : 54193448Sdh155122 nifq = ifs->ifs_nat_tqb + nat->nat_tqe.tqe_state[rev]; 54202393Syz155240 break; 54212393Syz155240 default : 54223448Sdh155122 nifq = &ifs->ifs_nat_iptq; 54232393Syz155240 break; 54242393Syz155240 } 54252393Syz155240 } 54262393Syz155240 54272393Syz155240 oifq = nat->nat_tqe.tqe_ifq; 54282393Syz155240 /* 54292393Syz155240 * If it's currently on a timeout queue, move it from one queue to 54302393Syz155240 * another, else put it on the end of the newly determined queue. 54312393Syz155240 */ 54322393Syz155240 if (oifq != NULL) 54333448Sdh155122 fr_movequeue(&nat->nat_tqe, oifq, nifq, ifs); 54342393Syz155240 else 54353448Sdh155122 fr_queueappend(&nat->nat_tqe, nifq, nat, ifs); 54362393Syz155240 return; 54372393Syz155240 } 54383448Sdh155122 54395417Sjojemann /* ------------------------------------------------------------------------ */ 54403448Sdh155122 /* Function: nat_getnext */ 54413448Sdh155122 /* Returns: int - 0 == ok, else error */ 54423448Sdh155122 /* Parameters: t(I) - pointer to ipftoken structure */ 54433448Sdh155122 /* itp(I) - pointer to ipfgeniter_t structure */ 54445417Sjojemann /* ifs - ipf stack instance */ 54453448Sdh155122 /* */ 54465417Sjojemann /* Fetch the next nat/ipnat/hostmap structure pointer from the linked list */ 54475417Sjojemann /* and copy it out to the storage space pointed to by itp. The next item */ 54483448Sdh155122 /* in the list to look at is put back in the ipftoken struture. */ 54493448Sdh155122 /* ------------------------------------------------------------------------ */ 54503448Sdh155122 static int nat_getnext(t, itp, ifs) 54513448Sdh155122 ipftoken_t *t; 54523448Sdh155122 ipfgeniter_t *itp; 54533448Sdh155122 ipf_stack_t *ifs; 54543448Sdh155122 { 54553448Sdh155122 hostmap_t *hm, *nexthm = NULL, zerohm; 54563448Sdh155122 ipnat_t *ipn, *nextipnat = NULL, zeroipn; 54573448Sdh155122 nat_t *nat, *nextnat = NULL, zeronat; 54585417Sjojemann int error = 0, count; 54595417Sjojemann char *dst; 54605417Sjojemann 54615417Sjojemann if (itp->igi_nitems == 0) 54625417Sjojemann return EINVAL; 54633448Sdh155122 54643448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 54655417Sjojemann 54666518Sjojemann /* 54676518Sjojemann * Get "previous" entry from the token and find the next entry. 54686518Sjojemann */ 54693448Sdh155122 switch (itp->igi_type) 54703448Sdh155122 { 54713448Sdh155122 case IPFGENITER_HOSTMAP : 54723448Sdh155122 hm = t->ipt_data; 54733448Sdh155122 if (hm == NULL) { 54743448Sdh155122 nexthm = ifs->ifs_ipf_hm_maplist; 54753448Sdh155122 } else { 54765417Sjojemann nexthm = hm->hm_next; 54773448Sdh155122 } 54783448Sdh155122 break; 54793448Sdh155122 54803448Sdh155122 case IPFGENITER_IPNAT : 54813448Sdh155122 ipn = t->ipt_data; 54823448Sdh155122 if (ipn == NULL) { 54833448Sdh155122 nextipnat = ifs->ifs_nat_list; 54843448Sdh155122 } else { 54853448Sdh155122 nextipnat = ipn->in_next; 54863448Sdh155122 } 54873448Sdh155122 break; 54883448Sdh155122 54893448Sdh155122 case IPFGENITER_NAT : 54903448Sdh155122 nat = t->ipt_data; 54913448Sdh155122 if (nat == NULL) { 54923448Sdh155122 nextnat = ifs->ifs_nat_instances; 54933448Sdh155122 } else { 54943448Sdh155122 nextnat = nat->nat_next; 54953448Sdh155122 } 54965417Sjojemann break; 54975417Sjojemann default : 54985417Sjojemann RWLOCK_EXIT(&ifs->ifs_ipf_nat); 54995417Sjojemann return EINVAL; 55005417Sjojemann } 55015417Sjojemann 55025417Sjojemann dst = itp->igi_data; 55035417Sjojemann for (count = itp->igi_nitems; count > 0; count--) { 55046518Sjojemann /* 55056518Sjojemann * If we found an entry, add a reference to it and update the token. 55066518Sjojemann * Otherwise, zero out data to be returned and NULL out token. 55076518Sjojemann */ 55085417Sjojemann switch (itp->igi_type) 55095417Sjojemann { 55105417Sjojemann case IPFGENITER_HOSTMAP : 55115417Sjojemann if (nexthm != NULL) { 55125417Sjojemann ATOMIC_INC32(nexthm->hm_ref); 55135417Sjojemann t->ipt_data = nexthm; 55143448Sdh155122 } else { 55155417Sjojemann bzero(&zerohm, sizeof(zerohm)); 55165417Sjojemann nexthm = &zerohm; 55175417Sjojemann t->ipt_data = NULL; 55185417Sjojemann } 55195417Sjojemann break; 55205417Sjojemann case IPFGENITER_IPNAT : 55215417Sjojemann if (nextipnat != NULL) { 55225417Sjojemann ATOMIC_INC32(nextipnat->in_use); 55235417Sjojemann t->ipt_data = nextipnat; 55245417Sjojemann } else { 55255417Sjojemann bzero(&zeroipn, sizeof(zeroipn)); 55265417Sjojemann nextipnat = &zeroipn; 55275417Sjojemann t->ipt_data = NULL; 55285417Sjojemann } 55295417Sjojemann break; 55305417Sjojemann case IPFGENITER_NAT : 55315417Sjojemann if (nextnat != NULL) { 55323448Sdh155122 MUTEX_ENTER(&nextnat->nat_lock); 55333448Sdh155122 nextnat->nat_ref++; 55343448Sdh155122 MUTEX_EXIT(&nextnat->nat_lock); 55355417Sjojemann t->ipt_data = nextnat; 55365417Sjojemann } else { 55375417Sjojemann bzero(&zeronat, sizeof(zeronat)); 55385417Sjojemann nextnat = &zeronat; 55395417Sjojemann t->ipt_data = NULL; 55403448Sdh155122 } 55415417Sjojemann break; 55425417Sjojemann default : 55435417Sjojemann break; 55443448Sdh155122 } 55455417Sjojemann 55465417Sjojemann /* 55476518Sjojemann * Now that we have ref, it's save to give up lock. 55485417Sjojemann */ 55495417Sjojemann RWLOCK_EXIT(&ifs->ifs_ipf_nat); 55505417Sjojemann 55516518Sjojemann /* 55526518Sjojemann * Copy out data and clean up references and token as needed. 55536518Sjojemann */ 55545417Sjojemann switch (itp->igi_type) 55555417Sjojemann { 55565417Sjojemann case IPFGENITER_HOSTMAP : 55575417Sjojemann error = COPYOUT(nexthm, dst, sizeof(*nexthm)); 55586518Sjojemann if (error != 0) 55595417Sjojemann error = EFAULT; 55606518Sjojemann if (t->ipt_data == NULL) { 55616518Sjojemann ipf_freetoken(t, ifs); 55626518Sjojemann break; 55635417Sjojemann } else { 55646518Sjojemann if (hm != NULL) { 55656518Sjojemann WRITE_ENTER(&ifs->ifs_ipf_nat); 55666518Sjojemann fr_hostmapdel(&hm); 55676518Sjojemann RWLOCK_EXIT(&ifs->ifs_ipf_nat); 55686518Sjojemann } 55696518Sjojemann if (nexthm->hm_next == NULL) { 55706518Sjojemann ipf_freetoken(t, ifs); 55716518Sjojemann break; 55726518Sjojemann } 55735417Sjojemann dst += sizeof(*nexthm); 55745417Sjojemann hm = nexthm; 55755417Sjojemann nexthm = nexthm->hm_next; 55765417Sjojemann } 55775417Sjojemann break; 55786518Sjojemann 55795417Sjojemann case IPFGENITER_IPNAT : 55805417Sjojemann error = COPYOUT(nextipnat, dst, sizeof(*nextipnat)); 55816518Sjojemann if (error != 0) 55825417Sjojemann error = EFAULT; 55836518Sjojemann if (t->ipt_data == NULL) { 55846518Sjojemann ipf_freetoken(t, ifs); 55856518Sjojemann break; 55865417Sjojemann } else { 55876518Sjojemann if (ipn != NULL) { 55886518Sjojemann WRITE_ENTER(&ifs->ifs_ipf_nat); 55896518Sjojemann fr_ipnatderef(&ipn, ifs); 55906518Sjojemann RWLOCK_EXIT(&ifs->ifs_ipf_nat); 55916518Sjojemann } 55926518Sjojemann if (nextipnat->in_next == NULL) { 55936518Sjojemann ipf_freetoken(t, ifs); 55946518Sjojemann break; 55956518Sjojemann } 55965417Sjojemann dst += sizeof(*nextipnat); 55975417Sjojemann ipn = nextipnat; 55985417Sjojemann nextipnat = nextipnat->in_next; 55995417Sjojemann } 56005417Sjojemann break; 56016518Sjojemann 56025417Sjojemann case IPFGENITER_NAT : 56035417Sjojemann error = COPYOUT(nextnat, dst, sizeof(*nextnat)); 56046518Sjojemann if (error != 0) 56055417Sjojemann error = EFAULT; 56066518Sjojemann if (t->ipt_data == NULL) { 56076518Sjojemann ipf_freetoken(t, ifs); 56086518Sjojemann break; 56095417Sjojemann } else { 56106518Sjojemann if (nat != NULL) 56116518Sjojemann fr_natderef(&nat, ifs); 56126518Sjojemann if (nextnat->nat_next == NULL) { 56136518Sjojemann ipf_freetoken(t, ifs); 56146518Sjojemann break; 56156518Sjojemann } 56165417Sjojemann dst += sizeof(*nextnat); 56175417Sjojemann nat = nextnat; 56185417Sjojemann nextnat = nextnat->nat_next; 56195417Sjojemann } 56205417Sjojemann break; 56215417Sjojemann default : 56225417Sjojemann break; 56235417Sjojemann } 56245417Sjojemann 56255417Sjojemann if ((count == 1) || (error != 0)) 56265417Sjojemann break; 56275417Sjojemann 56285417Sjojemann READ_ENTER(&ifs->ifs_ipf_nat); 56293448Sdh155122 } 56303448Sdh155122 56313448Sdh155122 return error; 56323448Sdh155122 } 56333448Sdh155122 56343448Sdh155122 56353448Sdh155122 /* ------------------------------------------------------------------------ */ 56363448Sdh155122 /* Function: nat_iterator */ 56373448Sdh155122 /* Returns: int - 0 == ok, else error */ 56383448Sdh155122 /* Parameters: token(I) - pointer to ipftoken structure */ 56393448Sdh155122 /* itp(I) - pointer to ipfgeniter_t structure */ 56403448Sdh155122 /* */ 56413448Sdh155122 /* This function acts as a handler for the SIOCGENITER ioctls that use a */ 56423448Sdh155122 /* generic structure to iterate through a list. There are three different */ 56433448Sdh155122 /* linked lists of NAT related information to go through: NAT rules, active */ 56443448Sdh155122 /* NAT mappings and the NAT fragment cache. */ 56453448Sdh155122 /* ------------------------------------------------------------------------ */ 56463448Sdh155122 static int nat_iterator(token, itp, ifs) 56473448Sdh155122 ipftoken_t *token; 56483448Sdh155122 ipfgeniter_t *itp; 56493448Sdh155122 ipf_stack_t *ifs; 56503448Sdh155122 { 56513448Sdh155122 int error; 56523448Sdh155122 56533448Sdh155122 if (itp->igi_data == NULL) 56543448Sdh155122 return EFAULT; 56553448Sdh155122 56563448Sdh155122 token->ipt_subtype = itp->igi_type; 56573448Sdh155122 56583448Sdh155122 switch (itp->igi_type) 56593448Sdh155122 { 56603448Sdh155122 case IPFGENITER_HOSTMAP : 56613448Sdh155122 case IPFGENITER_IPNAT : 56623448Sdh155122 case IPFGENITER_NAT : 56633448Sdh155122 error = nat_getnext(token, itp, ifs); 56643448Sdh155122 break; 56653448Sdh155122 case IPFGENITER_NATFRAG : 56663448Sdh155122 error = fr_nextfrag(token, itp, &ifs->ifs_ipfr_natlist, 56673448Sdh155122 &ifs->ifs_ipfr_nattail, 56683448Sdh155122 &ifs->ifs_ipf_natfrag, ifs); 56693448Sdh155122 break; 56703448Sdh155122 default : 56713448Sdh155122 error = EINVAL; 56723448Sdh155122 break; 56733448Sdh155122 } 56743448Sdh155122 56753448Sdh155122 return error; 56763448Sdh155122 } 56774817San207044 56784817San207044 56794817San207044 /* -------------------------------------------------------------------- */ 56804817San207044 /* Function: nat_earlydrop */ 56814817San207044 /* Returns: number of dropped/removed entries from the queue */ 56824817San207044 /* Parameters: ifq - pointer to queue with entries to be processed */ 56834817San207044 /* maxidle - entry must be idle this long to be dropped */ 56844817San207044 /* ifs - ipf stack instance */ 56854817San207044 /* */ 56864817San207044 /* Function is invoked from nat_extraflush() only. Removes entries */ 56874817San207044 /* form specified timeout queue, based on how long they've sat idle, */ 56884817San207044 /* without waiting for it to happen on its own. */ 56894817San207044 /* -------------------------------------------------------------------- */ 56904817San207044 static int nat_earlydrop(ifq, maxidle, ifs) 56914817San207044 ipftq_t *ifq; 56924817San207044 int maxidle; 56934817San207044 ipf_stack_t *ifs; 56944817San207044 { 56954817San207044 ipftqent_t *tqe, *tqn; 56964817San207044 nat_t *nat; 56974817San207044 unsigned int dropped; 56984817San207044 int droptick; 56994817San207044 57004817San207044 if (ifq == NULL) 57014817San207044 return (0); 57024817San207044 57034817San207044 dropped = 0; 57044817San207044 57054817San207044 /* 57064817San207044 * Determine the tick representing the idle time we're interested 57074817San207044 * in. If an entry exists in the queue, and it was touched before 57084817San207044 * that tick, then it's been idle longer than maxidle ... remove it. 57094817San207044 */ 57104817San207044 droptick = ifs->ifs_fr_ticks - maxidle; 57114817San207044 tqn = ifq->ifq_head; 57124817San207044 while ((tqe = tqn) != NULL && tqe->tqe_touched < droptick) { 57134817San207044 tqn = tqe->tqe_next; 57144817San207044 nat = tqe->tqe_parent; 57154817San207044 nat_delete(nat, ISL_EXPIRE, ifs); 57164817San207044 dropped++; 57174817San207044 } 57184817San207044 return (dropped); 57194817San207044 } 57204817San207044 57214817San207044 57224817San207044 /* --------------------------------------------------------------------- */ 57234817San207044 /* Function: nat_flushclosing */ 57244817San207044 /* Returns: int - number of NAT entries deleted */ 57254817San207044 /* Parameters: stateval(I) - State at which to start removing entries */ 57264817San207044 /* ifs - ipf stack instance */ 57274817San207044 /* */ 57284817San207044 /* Remove nat table entries for TCP connections which are in the process */ 57294817San207044 /* of closing, and are in (or "beyond") state specified by 'stateval'. */ 57304817San207044 /* --------------------------------------------------------------------- */ 57314817San207044 static int nat_flushclosing(stateval, ifs) 57324817San207044 int stateval; 57334817San207044 ipf_stack_t *ifs; 57344817San207044 { 57354817San207044 ipftq_t *ifq, *ifqn; 57364817San207044 ipftqent_t *tqe, *tqn; 57374817San207044 nat_t *nat; 57384817San207044 int dropped; 57394817San207044 57404817San207044 dropped = 0; 57414817San207044 57424817San207044 /* 57434817San207044 * Start by deleting any entries in specific timeout queues. 57444817San207044 */ 57454817San207044 ifqn = &ifs->ifs_nat_tqb[stateval]; 57464817San207044 while ((ifq = ifqn) != NULL) { 57474817San207044 ifqn = ifq->ifq_next; 57484817San207044 dropped += nat_earlydrop(ifq, (int)0, ifs); 57494817San207044 } 57504817San207044 57514817San207044 /* 57524817San207044 * Next, look through user defined queues for closing entries. 57534817San207044 */ 57544817San207044 ifqn = ifs->ifs_nat_utqe; 57554817San207044 while ((ifq = ifqn) != NULL) { 57564817San207044 ifqn = ifq->ifq_next; 57574817San207044 tqn = ifq->ifq_head; 57584817San207044 while ((tqe = tqn) != NULL) { 57594817San207044 tqn = tqe->tqe_next; 57604817San207044 nat = tqe->tqe_parent; 57614817San207044 if (nat->nat_p != IPPROTO_TCP) 57624817San207044 continue; 57634817San207044 if ((nat->nat_tcpstate[0] >= stateval) && 57644817San207044 (nat->nat_tcpstate[1] >= stateval)) { 57654817San207044 nat_delete(nat, NL_EXPIRE, ifs); 57664817San207044 dropped++; 57674817San207044 } 57684817San207044 } 57694817San207044 } 57704817San207044 return (dropped); 57714817San207044 } 57724817San207044 57734817San207044 57744817San207044 /* --------------------------------------------------------------------- */ 57754817San207044 /* Function: nat_extraflush */ 57764817San207044 /* Returns: int - number of NAT entries deleted */ 57774817San207044 /* Parameters: which(I) - how to flush the active NAT table */ 57784817San207044 /* ifs - ipf stack instance */ 57794817San207044 /* Write Locks: ipf_nat */ 57804817San207044 /* */ 57814817San207044 /* Flush nat tables. Three actions currently defined: */ 57824817San207044 /* */ 57834817San207044 /* which == 0 : Flush all nat table entries. */ 57844817San207044 /* */ 57854817San207044 /* which == 1 : Flush entries with TCP connections which have started */ 57864817San207044 /* to close on both ends. */ 57874817San207044 /* */ 57884817San207044 /* which == 2 : First, flush entries which are "almost" closed. If that */ 57894817San207044 /* does not take us below specified threshold in the table, */ 57904817San207044 /* we want to flush entries with TCP connections which have */ 57914817San207044 /* been idle for a long time. Start with connections idle */ 57924817San207044 /* over 12 hours, and then work backwards in half hour */ 57934817San207044 /* increments to at most 30 minutes idle, and finally work */ 57944817San207044 /* back in 30 second increments to at most 30 seconds. */ 57954817San207044 /* --------------------------------------------------------------------- */ 57964817San207044 static int nat_extraflush(which, ifs) 57974817San207044 int which; 57984817San207044 ipf_stack_t *ifs; 57994817San207044 { 58004817San207044 ipftq_t *ifq, *ifqn; 58014817San207044 nat_t *nat, **natp; 58024817San207044 int idletime, removed, idle_idx; 58034817San207044 SPL_INT(s); 58044817San207044 58054817San207044 removed = 0; 58064817San207044 58074817San207044 SPL_NET(s); 58084817San207044 switch (which) 58094817San207044 { 58104817San207044 case 0: 58114817San207044 natp = &ifs->ifs_nat_instances; 58124817San207044 while ((nat = *natp) != NULL) { 58134817San207044 natp = &nat->nat_next; 58144817San207044 nat_delete(nat, ISL_FLUSH, ifs); 58154817San207044 removed++; 58164817San207044 } 58174817San207044 break; 58184817San207044 58194817San207044 case 1: 58204817San207044 removed = nat_flushclosing(IPF_TCPS_CLOSE_WAIT, ifs); 58214817San207044 break; 58224817San207044 58234817San207044 case 2: 58244817San207044 removed = nat_flushclosing(IPF_TCPS_FIN_WAIT_2, ifs); 58254817San207044 58264817San207044 /* 58274817San207044 * Be sure we haven't done this in the last 10 seconds. 58284817San207044 */ 58294817San207044 if (ifs->ifs_fr_ticks - ifs->ifs_nat_last_force_flush < 58304817San207044 IPF_TTLVAL(10)) 58314817San207044 break; 58324817San207044 ifs->ifs_nat_last_force_flush = ifs->ifs_fr_ticks; 58334817San207044 58344817San207044 /* 58354817San207044 * Determine initial threshold for minimum idle time based on 58364817San207044 * how long ipfilter has been running. Ipfilter needs to have 58374817San207044 * been up as long as the smallest interval to continue on. 58384817San207044 * 58394817San207044 * Minimum idle times stored in idletime_tab and indexed by 58404817San207044 * idle_idx. Start at upper end of array and work backwards. 58414817San207044 * 58424817San207044 * Once the index is found, set the initial idle time to the 58434817San207044 * first interval before the current ipfilter run time. 58444817San207044 */ 58454817San207044 if (ifs->ifs_fr_ticks < idletime_tab[0]) 58464817San207044 break; /* switch */ 58474817San207044 idle_idx = (sizeof (idletime_tab) / sizeof (int)) - 1; 58484817San207044 if (ifs->ifs_fr_ticks > idletime_tab[idle_idx]) { 58494817San207044 idletime = idletime_tab[idle_idx]; 58504817San207044 } else { 58514817San207044 while ((idle_idx > 0) && 58524817San207044 (ifs->ifs_fr_ticks < idletime_tab[idle_idx])) 58534817San207044 idle_idx--; 58544817San207044 idletime = (ifs->ifs_fr_ticks / 58554817San207044 idletime_tab[idle_idx]) * 58564817San207044 idletime_tab[idle_idx]; 58574817San207044 } 58584817San207044 58594817San207044 while ((idle_idx >= 0) && 58604817San207044 (NAT_TAB_WATER_LEVEL(ifs) > ifs->ifs_nat_flush_lvl_lo)) { 58614817San207044 /* 58624817San207044 * Start with appropriate timeout queue. 58634817San207044 */ 58644817San207044 removed += nat_earlydrop( 58654817San207044 &ifs->ifs_nat_tqb[IPF_TCPS_ESTABLISHED], 58664817San207044 idletime, ifs); 58674817San207044 58684817San207044 /* 58694817San207044 * Make sure we haven't already deleted enough 58704817San207044 * entries before checking the user defined queues. 58714817San207044 */ 58724817San207044 if (NAT_TAB_WATER_LEVEL(ifs) <= 58734817San207044 ifs->ifs_nat_flush_lvl_lo) 58744817San207044 break; 58754817San207044 58764817San207044 /* 58774817San207044 * Next, look through the user defined queues. 58784817San207044 */ 58794817San207044 ifqn = ifs->ifs_nat_utqe; 58804817San207044 while ((ifq = ifqn) != NULL) { 58814817San207044 ifqn = ifq->ifq_next; 58824817San207044 removed += nat_earlydrop(ifq, idletime, ifs); 58834817San207044 } 58844817San207044 58854817San207044 /* 58864817San207044 * Adjust the granularity of idle time. 58874817San207044 * 58884817San207044 * If we reach an interval boundary, we need to 58894817San207044 * either adjust the idle time accordingly or exit 58904817San207044 * the loop altogether (if this is very last check). 58914817San207044 */ 58924817San207044 idletime -= idletime_tab[idle_idx]; 58934817San207044 if (idletime < idletime_tab[idle_idx]) { 58944817San207044 if (idle_idx != 0) { 58954817San207044 idletime = idletime_tab[idle_idx] - 58964817San207044 idletime_tab[idle_idx - 1]; 58974817San207044 idle_idx--; 58984817San207044 } else { 58994817San207044 break; /* while */ 59004817San207044 } 59014817San207044 } 59024817San207044 } 59034817San207044 break; 59044817San207044 default: 59054817San207044 break; 59064817San207044 } 59074817San207044 59084817San207044 SPL_X(s); 59094817San207044 return (removed); 59104817San207044 } 5911