12393Syz155240 /* 22393Syz155240 * Copyright (C) 1995-2003 by Darren Reed. 32393Syz155240 * 42393Syz155240 * See the IPFILTER.LICENCE file for details on licencing. 52393Syz155240 * 63448Sdh155122 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 72393Syz155240 * Use is subject to license terms. 82393Syz155240 */ 92393Syz155240 104380Sjojemann #pragma ident "%Z%%M% %I% %E% SMI"$ 112393Syz155240 122393Syz155240 #if defined(KERNEL) || defined(_KERNEL) 132393Syz155240 # undef KERNEL 142393Syz155240 # undef _KERNEL 152393Syz155240 # define KERNEL 1 162393Syz155240 # define _KERNEL 1 172393Syz155240 #endif 182393Syz155240 #include <sys/errno.h> 192393Syz155240 #include <sys/types.h> 202393Syz155240 #include <sys/param.h> 212393Syz155240 #include <sys/time.h> 222393Syz155240 #include <sys/file.h> 232393Syz155240 #if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \ 242393Syz155240 defined(_KERNEL) 252393Syz155240 # include "opt_ipfilter_log.h" 262393Syz155240 #endif 272393Syz155240 #if !defined(_KERNEL) 282393Syz155240 # include <stdio.h> 292393Syz155240 # include <string.h> 302393Syz155240 # include <stdlib.h> 312393Syz155240 # define _KERNEL 322393Syz155240 # ifdef __OpenBSD__ 332393Syz155240 struct file; 342393Syz155240 # endif 352393Syz155240 # include <sys/uio.h> 362393Syz155240 # undef _KERNEL 372393Syz155240 #endif 382393Syz155240 #if defined(_KERNEL) && (__FreeBSD_version >= 220000) 392393Syz155240 # include <sys/filio.h> 402393Syz155240 # include <sys/fcntl.h> 412393Syz155240 #else 422393Syz155240 # include <sys/ioctl.h> 432393Syz155240 #endif 442393Syz155240 #if !defined(AIX) 452393Syz155240 # include <sys/fcntl.h> 462393Syz155240 #endif 472393Syz155240 #if !defined(linux) 482393Syz155240 # include <sys/protosw.h> 492393Syz155240 #endif 502393Syz155240 #include <sys/socket.h> 512393Syz155240 #if defined(_KERNEL) 522393Syz155240 # include <sys/systm.h> 532393Syz155240 # if !defined(__SVR4) && !defined(__svr4__) 542393Syz155240 # include <sys/mbuf.h> 552393Syz155240 # endif 562393Syz155240 #endif 572393Syz155240 #if defined(__SVR4) || defined(__svr4__) 582393Syz155240 # include <sys/filio.h> 592393Syz155240 # include <sys/byteorder.h> 602393Syz155240 # ifdef _KERNEL 612393Syz155240 # include <sys/dditypes.h> 622393Syz155240 # endif 632393Syz155240 # include <sys/stream.h> 642393Syz155240 # include <sys/kmem.h> 652393Syz155240 #endif 662393Syz155240 #if __FreeBSD_version >= 300000 672393Syz155240 # include <sys/queue.h> 682393Syz155240 #endif 692393Syz155240 #include <net/if.h> 702393Syz155240 #if __FreeBSD_version >= 300000 712393Syz155240 # include <net/if_var.h> 722393Syz155240 # if defined(_KERNEL) && !defined(IPFILTER_LKM) 732393Syz155240 # include "opt_ipfilter.h" 742393Syz155240 # endif 752393Syz155240 #endif 762393Syz155240 #ifdef sun 772393Syz155240 # include <net/af.h> 782393Syz155240 #endif 792393Syz155240 #include <net/route.h> 802393Syz155240 #include <netinet/in.h> 812393Syz155240 #include <netinet/in_systm.h> 822393Syz155240 #include <netinet/ip.h> 832393Syz155240 842393Syz155240 #ifdef RFC1825 852393Syz155240 # include <vpn/md5.h> 862393Syz155240 # include <vpn/ipsec.h> 872393Syz155240 extern struct ifnet vpnif; 882393Syz155240 #endif 892393Syz155240 902393Syz155240 #if !defined(linux) 912393Syz155240 # include <netinet/ip_var.h> 922393Syz155240 #endif 932393Syz155240 #include <netinet/tcp.h> 942393Syz155240 #include <netinet/udp.h> 952393Syz155240 #include <netinet/ip_icmp.h> 962393Syz155240 #include "netinet/ip_compat.h" 972393Syz155240 #include <netinet/tcpip.h> 982393Syz155240 #include "netinet/ip_fil.h" 992393Syz155240 #include "netinet/ip_nat.h" 1002393Syz155240 #include "netinet/ip_frag.h" 1012393Syz155240 #include "netinet/ip_state.h" 1022393Syz155240 #include "netinet/ip_proxy.h" 1033448Sdh155122 #include "netinet/ipf_stack.h" 1042393Syz155240 #ifdef IPFILTER_SYNC 1052393Syz155240 #include "netinet/ip_sync.h" 1062393Syz155240 #endif 1072393Syz155240 #if (__FreeBSD_version >= 300000) 1082393Syz155240 # include <sys/malloc.h> 1092393Syz155240 #endif 1102393Syz155240 /* END OF INCLUDES */ 1112393Syz155240 1122393Syz155240 #undef SOCKADDR_IN 1132393Syz155240 #define SOCKADDR_IN struct sockaddr_in 1142393Syz155240 1152393Syz155240 #if !defined(lint) 1162393Syz155240 static const char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed"; 1172393Syz155240 static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.195.2.42 2005/08/11 19:51:36 darrenr Exp $"; 1182393Syz155240 #endif 1192393Syz155240 1202393Syz155240 1212393Syz155240 /* ======================================================================== */ 1222393Syz155240 /* How the NAT is organised and works. */ 1232393Syz155240 /* */ 1242393Syz155240 /* Inside (interface y) NAT Outside (interface x) */ 1252393Syz155240 /* -------------------- -+- ------------------------------------- */ 1262393Syz155240 /* Packet going | out, processsed by fr_checknatout() for x */ 1272393Syz155240 /* ------------> | ------------> */ 1282393Syz155240 /* src=10.1.1.1 | src=192.1.1.1 */ 1292393Syz155240 /* | */ 1302393Syz155240 /* | in, processed by fr_checknatin() for x */ 1312393Syz155240 /* <------------ | <------------ */ 1322393Syz155240 /* dst=10.1.1.1 | dst=192.1.1.1 */ 1332393Syz155240 /* -------------------- -+- ------------------------------------- */ 1342393Syz155240 /* fr_checknatout() - changes ip_src and if required, sport */ 1352393Syz155240 /* - creates a new mapping, if required. */ 1362393Syz155240 /* fr_checknatin() - changes ip_dst and if required, dport */ 1372393Syz155240 /* */ 1382393Syz155240 /* In the NAT table, internal source is recorded as "in" and externally */ 1392393Syz155240 /* seen as "out". */ 1402393Syz155240 /* ======================================================================== */ 1412393Syz155240 1422393Syz155240 1433448Sdh155122 static int nat_flushtable __P((ipf_stack_t *)); 1443448Sdh155122 static int nat_clearlist __P((ipf_stack_t *)); 1453448Sdh155122 static void nat_addnat __P((struct ipnat *, ipf_stack_t *)); 1463448Sdh155122 static void nat_addrdr __P((struct ipnat *, ipf_stack_t *)); 1473448Sdh155122 static void nat_delete __P((struct nat *, int, ipf_stack_t *)); 1482393Syz155240 static void nat_delrdr __P((struct ipnat *)); 1492393Syz155240 static void nat_delnat __P((struct ipnat *)); 1503448Sdh155122 static int fr_natgetent __P((caddr_t, ipf_stack_t *)); 1513448Sdh155122 static int fr_natgetsz __P((caddr_t, ipf_stack_t *)); 1523448Sdh155122 static int fr_natputent __P((caddr_t, int, ipf_stack_t *)); 1533448Sdh155122 static void nat_tabmove __P((nat_t *, ipf_stack_t *)); 1542393Syz155240 static int nat_match __P((fr_info_t *, ipnat_t *)); 1552393Syz155240 static INLINE int nat_newmap __P((fr_info_t *, nat_t *, natinfo_t *)); 1562393Syz155240 static INLINE int nat_newrdr __P((fr_info_t *, nat_t *, natinfo_t *)); 1572393Syz155240 static hostmap_t *nat_hostmap __P((ipnat_t *, struct in_addr, 1583448Sdh155122 struct in_addr, struct in_addr, u_32_t, 1593448Sdh155122 ipf_stack_t *)); 1602393Syz155240 static void nat_hostmapdel __P((struct hostmap *)); 1612393Syz155240 static INLINE int nat_icmpquerytype4 __P((int)); 1623448Sdh155122 static int nat_siocaddnat __P((ipnat_t *, ipnat_t **, int, 1633448Sdh155122 ipf_stack_t *)); 1643448Sdh155122 static void nat_siocdelnat __P((ipnat_t *, ipnat_t **, int, 1653448Sdh155122 ipf_stack_t *)); 1663448Sdh155122 static INLINE int nat_icmperrortype4 __P((int)); 1672393Syz155240 static INLINE int nat_finalise __P((fr_info_t *, nat_t *, natinfo_t *, 1682393Syz155240 tcphdr_t *, nat_t **, int)); 1694380Sjojemann static INLINE int nat_resolverule __P((ipnat_t *, ipf_stack_t *)); 1702393Syz155240 static nat_t *fr_natclone __P((fr_info_t *, nat_t *)); 1712958Sdr146992 static void nat_mssclamp __P((tcphdr_t *, u_32_t, u_short *)); 1722393Syz155240 static INLINE int nat_wildok __P((nat_t *, int, int, int, int)); 1733448Sdh155122 static int nat_getnext __P((ipftoken_t *, ipfgeniter_t *, ipf_stack_t *)); 1743448Sdh155122 static int nat_iterator __P((ipftoken_t *, ipfgeniter_t *, ipf_stack_t *)); 1754817San207044 static int nat_extraflush __P((int, ipf_stack_t *)); 1764817San207044 static int nat_earlydrop __P((ipftq_t *, int, ipf_stack_t *)); 1774817San207044 static int nat_flushclosing __P((int, ipf_stack_t *)); 1784817San207044 1794817San207044 1804817San207044 /* 1814817San207044 * Below we declare a list of constants used only in the nat_extraflush() 1824817San207044 * routine. We are placing it here, instead of in nat_extraflush() itself, 1834817San207044 * because we want to make it visible to tools such as mdb, nm etc., so the 1844817San207044 * values can easily be altered during debugging. 1854817San207044 */ 1864817San207044 static const int idletime_tab[] = { 1874817San207044 IPF_TTLVAL(30), /* 30 seconds */ 1884817San207044 IPF_TTLVAL(1800), /* 30 minutes */ 1894817San207044 IPF_TTLVAL(43200), /* 12 hours */ 1904817San207044 IPF_TTLVAL(345600), /* 4 days */ 1914817San207044 }; 1922393Syz155240 1932393Syz155240 1942393Syz155240 /* ------------------------------------------------------------------------ */ 1952393Syz155240 /* Function: fr_natinit */ 1962393Syz155240 /* Returns: int - 0 == success, -1 == failure */ 1972393Syz155240 /* Parameters: Nil */ 1982393Syz155240 /* */ 1992393Syz155240 /* Initialise all of the NAT locks, tables and other structures. */ 2002393Syz155240 /* ------------------------------------------------------------------------ */ 2013448Sdh155122 int fr_natinit(ifs) 2023448Sdh155122 ipf_stack_t *ifs; 2032393Syz155240 { 2042393Syz155240 int i; 2052393Syz155240 2063448Sdh155122 KMALLOCS(ifs->ifs_nat_table[0], nat_t **, 2073448Sdh155122 sizeof(nat_t *) * ifs->ifs_ipf_nattable_sz); 2083448Sdh155122 if (ifs->ifs_nat_table[0] != NULL) 2093448Sdh155122 bzero((char *)ifs->ifs_nat_table[0], 2103448Sdh155122 ifs->ifs_ipf_nattable_sz * sizeof(nat_t *)); 2112393Syz155240 else 2122393Syz155240 return -1; 2132393Syz155240 2143448Sdh155122 KMALLOCS(ifs->ifs_nat_table[1], nat_t **, 2153448Sdh155122 sizeof(nat_t *) * ifs->ifs_ipf_nattable_sz); 2163448Sdh155122 if (ifs->ifs_nat_table[1] != NULL) 2173448Sdh155122 bzero((char *)ifs->ifs_nat_table[1], 2183448Sdh155122 ifs->ifs_ipf_nattable_sz * sizeof(nat_t *)); 2192393Syz155240 else 2202393Syz155240 return -2; 2212393Syz155240 2223448Sdh155122 KMALLOCS(ifs->ifs_nat_rules, ipnat_t **, 2233448Sdh155122 sizeof(ipnat_t *) * ifs->ifs_ipf_natrules_sz); 2243448Sdh155122 if (ifs->ifs_nat_rules != NULL) 2253448Sdh155122 bzero((char *)ifs->ifs_nat_rules, 2263448Sdh155122 ifs->ifs_ipf_natrules_sz * sizeof(ipnat_t *)); 2272393Syz155240 else 2282393Syz155240 return -3; 2292393Syz155240 2303448Sdh155122 KMALLOCS(ifs->ifs_rdr_rules, ipnat_t **, 2313448Sdh155122 sizeof(ipnat_t *) * ifs->ifs_ipf_rdrrules_sz); 2323448Sdh155122 if (ifs->ifs_rdr_rules != NULL) 2333448Sdh155122 bzero((char *)ifs->ifs_rdr_rules, 2343448Sdh155122 ifs->ifs_ipf_rdrrules_sz * sizeof(ipnat_t *)); 2352393Syz155240 else 2362393Syz155240 return -4; 2372393Syz155240 2383448Sdh155122 KMALLOCS(ifs->ifs_maptable, hostmap_t **, 2393448Sdh155122 sizeof(hostmap_t *) * ifs->ifs_ipf_hostmap_sz); 2403448Sdh155122 if (ifs->ifs_maptable != NULL) 2413448Sdh155122 bzero((char *)ifs->ifs_maptable, 2423448Sdh155122 sizeof(hostmap_t *) * ifs->ifs_ipf_hostmap_sz); 2432393Syz155240 else 2442393Syz155240 return -5; 2452393Syz155240 2463448Sdh155122 ifs->ifs_ipf_hm_maplist = NULL; 2473448Sdh155122 2483448Sdh155122 KMALLOCS(ifs->ifs_nat_stats.ns_bucketlen[0], u_long *, 2493448Sdh155122 ifs->ifs_ipf_nattable_sz * sizeof(u_long)); 2503448Sdh155122 if (ifs->ifs_nat_stats.ns_bucketlen[0] == NULL) 2513448Sdh155122 return -1; 2523448Sdh155122 bzero((char *)ifs->ifs_nat_stats.ns_bucketlen[0], 2533448Sdh155122 ifs->ifs_ipf_nattable_sz * sizeof(u_long)); 2543448Sdh155122 2553448Sdh155122 KMALLOCS(ifs->ifs_nat_stats.ns_bucketlen[1], u_long *, 2563448Sdh155122 ifs->ifs_ipf_nattable_sz * sizeof(u_long)); 2573448Sdh155122 if (ifs->ifs_nat_stats.ns_bucketlen[1] == NULL) 2583448Sdh155122 return -1; 2593448Sdh155122 bzero((char *)ifs->ifs_nat_stats.ns_bucketlen[1], 2603448Sdh155122 ifs->ifs_ipf_nattable_sz * sizeof(u_long)); 2613448Sdh155122 2623448Sdh155122 if (ifs->ifs_fr_nat_maxbucket == 0) { 2633448Sdh155122 for (i = ifs->ifs_ipf_nattable_sz; i > 0; i >>= 1) 2643448Sdh155122 ifs->ifs_fr_nat_maxbucket++; 2653448Sdh155122 ifs->ifs_fr_nat_maxbucket *= 2; 2662393Syz155240 } 2672393Syz155240 2683448Sdh155122 fr_sttab_init(ifs->ifs_nat_tqb, ifs); 2692393Syz155240 /* 2702393Syz155240 * Increase this because we may have "keep state" following this too 2712393Syz155240 * and packet storms can occur if this is removed too quickly. 2722393Syz155240 */ 2733448Sdh155122 ifs->ifs_nat_tqb[IPF_TCPS_CLOSED].ifq_ttl = ifs->ifs_fr_tcplastack; 2743448Sdh155122 ifs->ifs_nat_tqb[IPF_TCP_NSTATES - 1].ifq_next = &ifs->ifs_nat_udptq; 2753448Sdh155122 ifs->ifs_nat_udptq.ifq_ttl = ifs->ifs_fr_defnatage; 2763448Sdh155122 ifs->ifs_nat_udptq.ifq_ref = 1; 2773448Sdh155122 ifs->ifs_nat_udptq.ifq_head = NULL; 2783448Sdh155122 ifs->ifs_nat_udptq.ifq_tail = &ifs->ifs_nat_udptq.ifq_head; 2793448Sdh155122 MUTEX_INIT(&ifs->ifs_nat_udptq.ifq_lock, "nat ipftq udp tab"); 2803448Sdh155122 ifs->ifs_nat_udptq.ifq_next = &ifs->ifs_nat_icmptq; 2813448Sdh155122 ifs->ifs_nat_icmptq.ifq_ttl = ifs->ifs_fr_defnaticmpage; 2823448Sdh155122 ifs->ifs_nat_icmptq.ifq_ref = 1; 2833448Sdh155122 ifs->ifs_nat_icmptq.ifq_head = NULL; 2843448Sdh155122 ifs->ifs_nat_icmptq.ifq_tail = &ifs->ifs_nat_icmptq.ifq_head; 2853448Sdh155122 MUTEX_INIT(&ifs->ifs_nat_icmptq.ifq_lock, "nat icmp ipftq tab"); 2863448Sdh155122 ifs->ifs_nat_icmptq.ifq_next = &ifs->ifs_nat_iptq; 2873448Sdh155122 ifs->ifs_nat_iptq.ifq_ttl = ifs->ifs_fr_defnatipage; 2883448Sdh155122 ifs->ifs_nat_iptq.ifq_ref = 1; 2893448Sdh155122 ifs->ifs_nat_iptq.ifq_head = NULL; 2903448Sdh155122 ifs->ifs_nat_iptq.ifq_tail = &ifs->ifs_nat_iptq.ifq_head; 2913448Sdh155122 MUTEX_INIT(&ifs->ifs_nat_iptq.ifq_lock, "nat ip ipftq tab"); 2923448Sdh155122 ifs->ifs_nat_iptq.ifq_next = NULL; 2932393Syz155240 2942393Syz155240 for (i = 0; i < IPF_TCP_NSTATES; i++) { 2953448Sdh155122 if (ifs->ifs_nat_tqb[i].ifq_ttl < ifs->ifs_fr_defnaticmpage) 2963448Sdh155122 ifs->ifs_nat_tqb[i].ifq_ttl = ifs->ifs_fr_defnaticmpage; 2972393Syz155240 #ifdef LARGE_NAT 2983448Sdh155122 else if (ifs->ifs_nat_tqb[i].ifq_ttl > ifs->ifs_fr_defnatage) 2993448Sdh155122 ifs->ifs_nat_tqb[i].ifq_ttl = ifs->ifs_fr_defnatage; 3002393Syz155240 #endif 3012393Syz155240 } 3022393Syz155240 3032393Syz155240 /* 3042393Syz155240 * Increase this because we may have "keep state" following 3052393Syz155240 * this too and packet storms can occur if this is removed 3062393Syz155240 * too quickly. 3072393Syz155240 */ 3083448Sdh155122 ifs->ifs_nat_tqb[IPF_TCPS_CLOSED].ifq_ttl = 3093448Sdh155122 ifs->ifs_nat_tqb[IPF_TCPS_LAST_ACK].ifq_ttl; 3103448Sdh155122 3113448Sdh155122 RWLOCK_INIT(&ifs->ifs_ipf_nat, "ipf IP NAT rwlock"); 3123448Sdh155122 RWLOCK_INIT(&ifs->ifs_ipf_natfrag, "ipf IP NAT-Frag rwlock"); 3133448Sdh155122 MUTEX_INIT(&ifs->ifs_ipf_nat_new, "ipf nat new mutex"); 3143448Sdh155122 MUTEX_INIT(&ifs->ifs_ipf_natio, "ipf nat io mutex"); 3153448Sdh155122 3163448Sdh155122 ifs->ifs_fr_nat_init = 1; 3172393Syz155240 3182393Syz155240 return 0; 3192393Syz155240 } 3202393Syz155240 3212393Syz155240 3222393Syz155240 /* ------------------------------------------------------------------------ */ 3232393Syz155240 /* Function: nat_addrdr */ 3242393Syz155240 /* Returns: Nil */ 3252393Syz155240 /* Parameters: n(I) - pointer to NAT rule to add */ 3262393Syz155240 /* */ 3272393Syz155240 /* Adds a redirect rule to the hash table of redirect rules and the list of */ 3282393Syz155240 /* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ 3292393Syz155240 /* use by redirect rules. */ 3302393Syz155240 /* ------------------------------------------------------------------------ */ 3313448Sdh155122 static void nat_addrdr(n, ifs) 3322393Syz155240 ipnat_t *n; 3333448Sdh155122 ipf_stack_t *ifs; 3342393Syz155240 { 3352393Syz155240 ipnat_t **np; 3362393Syz155240 u_32_t j; 3372393Syz155240 u_int hv; 3382393Syz155240 int k; 3392393Syz155240 3402393Syz155240 k = count4bits(n->in_outmsk); 3412393Syz155240 if ((k >= 0) && (k != 32)) 3423448Sdh155122 ifs->ifs_rdr_masks |= 1 << k; 3432393Syz155240 j = (n->in_outip & n->in_outmsk); 3443448Sdh155122 hv = NAT_HASH_FN(j, 0, ifs->ifs_ipf_rdrrules_sz); 3453448Sdh155122 np = ifs->ifs_rdr_rules + hv; 3462393Syz155240 while (*np != NULL) 3472393Syz155240 np = &(*np)->in_rnext; 3482393Syz155240 n->in_rnext = NULL; 3492393Syz155240 n->in_prnext = np; 3502393Syz155240 n->in_hv = hv; 3512393Syz155240 *np = n; 3522393Syz155240 } 3532393Syz155240 3542393Syz155240 3552393Syz155240 /* ------------------------------------------------------------------------ */ 3562393Syz155240 /* Function: nat_addnat */ 3572393Syz155240 /* Returns: Nil */ 3582393Syz155240 /* Parameters: n(I) - pointer to NAT rule to add */ 3592393Syz155240 /* */ 3602393Syz155240 /* Adds a NAT map rule to the hash table of rules and the list of loaded */ 3612393Syz155240 /* NAT rules. Updates the bitmask indicating which netmasks are in use by */ 3622393Syz155240 /* redirect rules. */ 3632393Syz155240 /* ------------------------------------------------------------------------ */ 3643448Sdh155122 static void nat_addnat(n, ifs) 3652393Syz155240 ipnat_t *n; 3663448Sdh155122 ipf_stack_t *ifs; 3672393Syz155240 { 3682393Syz155240 ipnat_t **np; 3692393Syz155240 u_32_t j; 3702393Syz155240 u_int hv; 3712393Syz155240 int k; 3722393Syz155240 3732393Syz155240 k = count4bits(n->in_inmsk); 3742393Syz155240 if ((k >= 0) && (k != 32)) 3753448Sdh155122 ifs->ifs_nat_masks |= 1 << k; 3762393Syz155240 j = (n->in_inip & n->in_inmsk); 3773448Sdh155122 hv = NAT_HASH_FN(j, 0, ifs->ifs_ipf_natrules_sz); 3783448Sdh155122 np = ifs->ifs_nat_rules + hv; 3792393Syz155240 while (*np != NULL) 3802393Syz155240 np = &(*np)->in_mnext; 3812393Syz155240 n->in_mnext = NULL; 3822393Syz155240 n->in_pmnext = np; 3832393Syz155240 n->in_hv = hv; 3842393Syz155240 *np = n; 3852393Syz155240 } 3862393Syz155240 3872393Syz155240 3882393Syz155240 /* ------------------------------------------------------------------------ */ 3892393Syz155240 /* Function: nat_delrdr */ 3902393Syz155240 /* Returns: Nil */ 3912393Syz155240 /* Parameters: n(I) - pointer to NAT rule to delete */ 3922393Syz155240 /* */ 3932393Syz155240 /* Removes a redirect rule from the hash table of redirect rules. */ 3942393Syz155240 /* ------------------------------------------------------------------------ */ 3952393Syz155240 static void nat_delrdr(n) 3962393Syz155240 ipnat_t *n; 3972393Syz155240 { 3982393Syz155240 if (n->in_rnext) 3992393Syz155240 n->in_rnext->in_prnext = n->in_prnext; 4002393Syz155240 *n->in_prnext = n->in_rnext; 4012393Syz155240 } 4022393Syz155240 4032393Syz155240 4042393Syz155240 /* ------------------------------------------------------------------------ */ 4052393Syz155240 /* Function: nat_delnat */ 4062393Syz155240 /* Returns: Nil */ 4072393Syz155240 /* Parameters: n(I) - pointer to NAT rule to delete */ 4082393Syz155240 /* */ 4092393Syz155240 /* Removes a NAT map rule from the hash table of NAT map rules. */ 4102393Syz155240 /* ------------------------------------------------------------------------ */ 4112393Syz155240 static void nat_delnat(n) 4122393Syz155240 ipnat_t *n; 4132393Syz155240 { 4142393Syz155240 if (n->in_mnext != NULL) 4152393Syz155240 n->in_mnext->in_pmnext = n->in_pmnext; 4162393Syz155240 *n->in_pmnext = n->in_mnext; 4172393Syz155240 } 4182393Syz155240 4192393Syz155240 4202393Syz155240 /* ------------------------------------------------------------------------ */ 4212393Syz155240 /* Function: nat_hostmap */ 4222393Syz155240 /* Returns: struct hostmap* - NULL if no hostmap could be created, */ 4232393Syz155240 /* else a pointer to the hostmapping to use */ 4242393Syz155240 /* Parameters: np(I) - pointer to NAT rule */ 4252393Syz155240 /* real(I) - real IP address */ 4262393Syz155240 /* map(I) - mapped IP address */ 4272393Syz155240 /* port(I) - destination port number */ 4282393Syz155240 /* Write Locks: ipf_nat */ 4292393Syz155240 /* */ 4302393Syz155240 /* Check if an ip address has already been allocated for a given mapping */ 4312393Syz155240 /* that is not doing port based translation. If is not yet allocated, then */ 4322393Syz155240 /* create a new entry if a non-NULL NAT rule pointer has been supplied. */ 4332393Syz155240 /* ------------------------------------------------------------------------ */ 4343448Sdh155122 static struct hostmap *nat_hostmap(np, src, dst, map, port, ifs) 4352393Syz155240 ipnat_t *np; 4362393Syz155240 struct in_addr src; 4372393Syz155240 struct in_addr dst; 4382393Syz155240 struct in_addr map; 4392393Syz155240 u_32_t port; 4403448Sdh155122 ipf_stack_t *ifs; 4412393Syz155240 { 4422393Syz155240 hostmap_t *hm; 4432393Syz155240 u_int hv; 4442393Syz155240 4452393Syz155240 hv = (src.s_addr ^ dst.s_addr); 4462393Syz155240 hv += src.s_addr; 4472393Syz155240 hv += dst.s_addr; 4482393Syz155240 hv %= HOSTMAP_SIZE; 4493448Sdh155122 for (hm = ifs->ifs_maptable[hv]; hm; hm = hm->hm_next) 4502393Syz155240 if ((hm->hm_srcip.s_addr == src.s_addr) && 4512393Syz155240 (hm->hm_dstip.s_addr == dst.s_addr) && 4522393Syz155240 ((np == NULL) || (np == hm->hm_ipnat)) && 4532393Syz155240 ((port == 0) || (port == hm->hm_port))) { 4542393Syz155240 hm->hm_ref++; 4552393Syz155240 return hm; 4562393Syz155240 } 4572393Syz155240 4582393Syz155240 if (np == NULL) 4592393Syz155240 return NULL; 4602393Syz155240 4612393Syz155240 KMALLOC(hm, hostmap_t *); 4622393Syz155240 if (hm) { 4633448Sdh155122 hm->hm_hnext = ifs->ifs_ipf_hm_maplist; 4643448Sdh155122 hm->hm_phnext = &ifs->ifs_ipf_hm_maplist; 4653448Sdh155122 if (ifs->ifs_ipf_hm_maplist != NULL) 4663448Sdh155122 ifs->ifs_ipf_hm_maplist->hm_phnext = &hm->hm_hnext; 4673448Sdh155122 ifs->ifs_ipf_hm_maplist = hm; 4683448Sdh155122 4693448Sdh155122 hm->hm_next = ifs->ifs_maptable[hv]; 4703448Sdh155122 hm->hm_pnext = ifs->ifs_maptable + hv; 4713448Sdh155122 if (ifs->ifs_maptable[hv] != NULL) 4723448Sdh155122 ifs->ifs_maptable[hv]->hm_pnext = &hm->hm_next; 4733448Sdh155122 ifs->ifs_maptable[hv] = hm; 4742393Syz155240 hm->hm_ipnat = np; 4752393Syz155240 hm->hm_srcip = src; 4762393Syz155240 hm->hm_dstip = dst; 4772393Syz155240 hm->hm_mapip = map; 4782393Syz155240 hm->hm_ref = 1; 4792393Syz155240 hm->hm_port = port; 4802393Syz155240 } 4812393Syz155240 return hm; 4822393Syz155240 } 4832393Syz155240 4842393Syz155240 4852393Syz155240 /* ------------------------------------------------------------------------ */ 4862393Syz155240 /* Function: nat_hostmapdel */ 4872393Syz155240 /* Returns: Nil */ 4882393Syz155240 /* Parameters: hm(I) - pointer to hostmap structure */ 4892393Syz155240 /* Write Locks: ipf_nat */ 4902393Syz155240 /* */ 4912393Syz155240 /* Decrement the references to this hostmap structure by one. If this */ 4922393Syz155240 /* reaches zero then remove it and free it. */ 4932393Syz155240 /* ------------------------------------------------------------------------ */ 4942393Syz155240 static void nat_hostmapdel(hm) 4952393Syz155240 struct hostmap *hm; 4962393Syz155240 { 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 5093448Sdh155122 void fr_hostmapderef(hmp) 5103448Sdh155122 struct hostmap **hmp; 5113448Sdh155122 { 5123448Sdh155122 struct hostmap *hm; 5133448Sdh155122 5143448Sdh155122 hm = *hmp; 5153448Sdh155122 *hmp = NULL; 5163448Sdh155122 hm->hm_ref--; 5173448Sdh155122 if (hm->hm_ref == 0) 5183448Sdh155122 nat_hostmapdel(hm); 5193448Sdh155122 } 5203448Sdh155122 5212393Syz155240 5222393Syz155240 /* ------------------------------------------------------------------------ */ 5232393Syz155240 /* Function: fix_outcksum */ 5242393Syz155240 /* Returns: Nil */ 5252958Sdr146992 /* Parameters: sp(I) - location of 16bit checksum to update */ 5262393Syz155240 /* n((I) - amount to adjust checksum by */ 5272393Syz155240 /* */ 5282393Syz155240 /* Adjusts the 16bit checksum by "n" for packets going out. */ 5292393Syz155240 /* ------------------------------------------------------------------------ */ 5302958Sdr146992 void fix_outcksum(sp, n) 5312393Syz155240 u_short *sp; 5322393Syz155240 u_32_t n; 5332393Syz155240 { 5342393Syz155240 u_short sumshort; 5352393Syz155240 u_32_t sum1; 5362393Syz155240 5372393Syz155240 if (n == 0) 5382393Syz155240 return; 5392393Syz155240 5402393Syz155240 sum1 = (~ntohs(*sp)) & 0xffff; 5412393Syz155240 sum1 += (n); 5422393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 5432393Syz155240 /* Again */ 5442393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 5452393Syz155240 sumshort = ~(u_short)sum1; 5462393Syz155240 *(sp) = htons(sumshort); 5472393Syz155240 } 5482393Syz155240 5492393Syz155240 5502393Syz155240 /* ------------------------------------------------------------------------ */ 5512393Syz155240 /* Function: fix_incksum */ 5522393Syz155240 /* Returns: Nil */ 5532958Sdr146992 /* Parameters: sp(I) - location of 16bit checksum to update */ 5542393Syz155240 /* n((I) - amount to adjust checksum by */ 5552393Syz155240 /* */ 5562393Syz155240 /* Adjusts the 16bit checksum by "n" for packets going in. */ 5572393Syz155240 /* ------------------------------------------------------------------------ */ 5582958Sdr146992 void fix_incksum(sp, n) 5592393Syz155240 u_short *sp; 5602393Syz155240 u_32_t n; 5612393Syz155240 { 5622393Syz155240 u_short sumshort; 5632393Syz155240 u_32_t sum1; 5642393Syz155240 5652393Syz155240 if (n == 0) 5662393Syz155240 return; 5672393Syz155240 5682393Syz155240 sum1 = (~ntohs(*sp)) & 0xffff; 5692393Syz155240 sum1 += ~(n) & 0xffff; 5702393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 5712393Syz155240 /* Again */ 5722393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 5732393Syz155240 sumshort = ~(u_short)sum1; 5742393Syz155240 *(sp) = htons(sumshort); 5752393Syz155240 } 5762393Syz155240 5772393Syz155240 5782393Syz155240 /* ------------------------------------------------------------------------ */ 5792393Syz155240 /* Function: fix_datacksum */ 5802393Syz155240 /* Returns: Nil */ 5812393Syz155240 /* Parameters: sp(I) - location of 16bit checksum to update */ 5822393Syz155240 /* n((I) - amount to adjust checksum by */ 5832393Syz155240 /* */ 5842393Syz155240 /* Fix_datacksum is used *only* for the adjustments of checksums in the */ 5852393Syz155240 /* data section of an IP packet. */ 5862393Syz155240 /* */ 5872393Syz155240 /* The only situation in which you need to do this is when NAT'ing an */ 5882393Syz155240 /* ICMP error message. Such a message, contains in its body the IP header */ 5892393Syz155240 /* of the original IP packet, that causes the error. */ 5902393Syz155240 /* */ 5912393Syz155240 /* You can't use fix_incksum or fix_outcksum in that case, because for the */ 5922393Syz155240 /* kernel the data section of the ICMP error is just data, and no special */ 5932393Syz155240 /* processing like hardware cksum or ntohs processing have been done by the */ 5942393Syz155240 /* kernel on the data section. */ 5952393Syz155240 /* ------------------------------------------------------------------------ */ 5962393Syz155240 void fix_datacksum(sp, n) 5972393Syz155240 u_short *sp; 5982393Syz155240 u_32_t n; 5992393Syz155240 { 6002393Syz155240 u_short sumshort; 6012393Syz155240 u_32_t sum1; 6022393Syz155240 6032393Syz155240 if (n == 0) 6042393Syz155240 return; 6052393Syz155240 6062393Syz155240 sum1 = (~ntohs(*sp)) & 0xffff; 6072393Syz155240 sum1 += (n); 6082393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 6092393Syz155240 /* Again */ 6102393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 6112393Syz155240 sumshort = ~(u_short)sum1; 6122393Syz155240 *(sp) = htons(sumshort); 6132393Syz155240 } 6142393Syz155240 6152393Syz155240 6162393Syz155240 /* ------------------------------------------------------------------------ */ 6172393Syz155240 /* Function: fr_nat_ioctl */ 6182393Syz155240 /* Returns: int - 0 == success, != 0 == failure */ 6192393Syz155240 /* Parameters: data(I) - pointer to ioctl data */ 6202393Syz155240 /* cmd(I) - ioctl command integer */ 6212393Syz155240 /* mode(I) - file mode bits used with open */ 6222393Syz155240 /* */ 6232393Syz155240 /* Processes an ioctl call made to operate on the IP Filter NAT device. */ 6242393Syz155240 /* ------------------------------------------------------------------------ */ 6253448Sdh155122 int fr_nat_ioctl(data, cmd, mode, uid, ctx, ifs) 6262393Syz155240 ioctlcmd_t cmd; 6272393Syz155240 caddr_t data; 6283448Sdh155122 int mode, uid; 6293448Sdh155122 void *ctx; 6303448Sdh155122 ipf_stack_t *ifs; 6312393Syz155240 { 6322393Syz155240 ipnat_t *nat, *nt, *n = NULL, **np = NULL; 6332393Syz155240 int error = 0, ret, arg, getlock; 6342393Syz155240 ipnat_t natd; 6352393Syz155240 6362393Syz155240 #if (BSD >= 199306) && defined(_KERNEL) 6372393Syz155240 if ((securelevel >= 2) && (mode & FWRITE)) 6382393Syz155240 return EPERM; 6392393Syz155240 #endif 6402393Syz155240 6412393Syz155240 #if defined(__osf__) && defined(_KERNEL) 6422393Syz155240 getlock = 0; 6432393Syz155240 #else 6442393Syz155240 getlock = (mode & NAT_LOCKHELD) ? 0 : 1; 6452393Syz155240 #endif 6462393Syz155240 6472393Syz155240 nat = NULL; /* XXX gcc -Wuninitialized */ 6482393Syz155240 if (cmd == (ioctlcmd_t)SIOCADNAT) { 6492393Syz155240 KMALLOC(nt, ipnat_t *); 6502393Syz155240 } else { 6512393Syz155240 nt = NULL; 6522393Syz155240 } 6532393Syz155240 6542393Syz155240 if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) { 6552393Syz155240 if (mode & NAT_SYSSPACE) { 6562393Syz155240 bcopy(data, (char *)&natd, sizeof(natd)); 6572393Syz155240 error = 0; 6582393Syz155240 } else { 6592393Syz155240 error = fr_inobj(data, &natd, IPFOBJ_IPNAT); 6602393Syz155240 } 6612393Syz155240 6622393Syz155240 } else if (cmd == (ioctlcmd_t)SIOCIPFFL) { /* SIOCFLNAT & SIOCCNATL */ 6632393Syz155240 BCOPYIN(data, &arg, sizeof(arg)); 6642393Syz155240 } 6652393Syz155240 6662393Syz155240 if (error != 0) 6672393Syz155240 goto done; 6682393Syz155240 6692393Syz155240 /* 6702393Syz155240 * For add/delete, look to see if the NAT entry is already present 6712393Syz155240 */ 6722393Syz155240 if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) { 6732393Syz155240 nat = &natd; 6742393Syz155240 if (nat->in_v == 0) /* For backward compat. */ 6752393Syz155240 nat->in_v = 4; 6762393Syz155240 nat->in_flags &= IPN_USERFLAGS; 6772393Syz155240 if ((nat->in_redir & NAT_MAPBLK) == 0) { 6782393Syz155240 if ((nat->in_flags & IPN_SPLIT) == 0) 6792393Syz155240 nat->in_inip &= nat->in_inmsk; 6802393Syz155240 if ((nat->in_flags & IPN_IPRANGE) == 0) 6812393Syz155240 nat->in_outip &= nat->in_outmsk; 6822393Syz155240 } 6833448Sdh155122 MUTEX_ENTER(&ifs->ifs_ipf_natio); 6843448Sdh155122 for (np = &ifs->ifs_nat_list; ((n = *np) != NULL); 6853448Sdh155122 np = &n->in_next) 6862393Syz155240 if (!bcmp((char *)&nat->in_flags, (char *)&n->in_flags, 6872393Syz155240 IPN_CMPSIZ)) 6882393Syz155240 break; 6892393Syz155240 } 6902393Syz155240 6912393Syz155240 switch (cmd) 6922393Syz155240 { 6933448Sdh155122 case SIOCGENITER : 6943448Sdh155122 { 6953448Sdh155122 ipfgeniter_t iter; 6963448Sdh155122 ipftoken_t *token; 6973448Sdh155122 6983448Sdh155122 error = fr_inobj(data, &iter, IPFOBJ_GENITER); 6993448Sdh155122 if (error != 0) 7003448Sdh155122 break; 7013448Sdh155122 7023448Sdh155122 token = ipf_findtoken(iter.igi_type, uid, ctx, ifs); 7033448Sdh155122 if (token != NULL) 7043448Sdh155122 error = nat_iterator(token, &iter, ifs); 7053448Sdh155122 else 7063448Sdh155122 error = ESRCH; 7073448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_tokens); 7083448Sdh155122 break; 7093448Sdh155122 } 7102393Syz155240 #ifdef IPFILTER_LOG 7112393Syz155240 case SIOCIPFFB : 7122393Syz155240 { 7132393Syz155240 int tmp; 7142393Syz155240 7152393Syz155240 if (!(mode & FWRITE)) 7162393Syz155240 error = EPERM; 7172393Syz155240 else { 7183448Sdh155122 tmp = ipflog_clear(IPL_LOGNAT, ifs); 7192393Syz155240 BCOPYOUT((char *)&tmp, (char *)data, sizeof(tmp)); 7202393Syz155240 } 7212393Syz155240 break; 7222393Syz155240 } 7232393Syz155240 case SIOCSETLG : 7242393Syz155240 if (!(mode & FWRITE)) 7252393Syz155240 error = EPERM; 7262393Syz155240 else { 7273448Sdh155122 BCOPYIN((char *)data, 7283448Sdh155122 (char *)&ifs->ifs_nat_logging, 7293448Sdh155122 sizeof(ifs->ifs_nat_logging)); 7302393Syz155240 } 7312393Syz155240 break; 7322393Syz155240 case SIOCGETLG : 7333448Sdh155122 BCOPYOUT((char *)&ifs->ifs_nat_logging, (char *)data, 7343448Sdh155122 sizeof(ifs->ifs_nat_logging)); 7352393Syz155240 break; 7362393Syz155240 case FIONREAD : 7373448Sdh155122 arg = ifs->ifs_iplused[IPL_LOGNAT]; 7382393Syz155240 BCOPYOUT(&arg, data, sizeof(arg)); 7392393Syz155240 break; 7402393Syz155240 #endif 7412393Syz155240 case SIOCADNAT : 7422393Syz155240 if (!(mode & FWRITE)) { 7432393Syz155240 error = EPERM; 7442393Syz155240 } else if (n != NULL) { 7452393Syz155240 error = EEXIST; 7462393Syz155240 } else if (nt == NULL) { 7472393Syz155240 error = ENOMEM; 7482393Syz155240 } 7492393Syz155240 if (error != 0) { 7503448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_natio); 7512393Syz155240 break; 7522393Syz155240 } 7532393Syz155240 bcopy((char *)nat, (char *)nt, sizeof(*n)); 7543448Sdh155122 error = nat_siocaddnat(nt, np, getlock, ifs); 7553448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_natio); 7562393Syz155240 if (error == 0) 7572393Syz155240 nt = NULL; 7582393Syz155240 break; 7592393Syz155240 case SIOCRMNAT : 7602393Syz155240 if (!(mode & FWRITE)) { 7612393Syz155240 error = EPERM; 7622393Syz155240 n = NULL; 7632393Syz155240 } else if (n == NULL) { 7642393Syz155240 error = ESRCH; 7652393Syz155240 } 7662393Syz155240 7672393Syz155240 if (error != 0) { 7683448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_natio); 7692393Syz155240 break; 7702393Syz155240 } 7713448Sdh155122 nat_siocdelnat(n, np, getlock, ifs); 7723448Sdh155122 7733448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_natio); 7742393Syz155240 n = NULL; 7752393Syz155240 break; 7762393Syz155240 case SIOCGNATS : 7773448Sdh155122 ifs->ifs_nat_stats.ns_table[0] = ifs->ifs_nat_table[0]; 7783448Sdh155122 ifs->ifs_nat_stats.ns_table[1] = ifs->ifs_nat_table[1]; 7793448Sdh155122 ifs->ifs_nat_stats.ns_list = ifs->ifs_nat_list; 7803448Sdh155122 ifs->ifs_nat_stats.ns_maptable = ifs->ifs_maptable; 7813448Sdh155122 ifs->ifs_nat_stats.ns_maplist = ifs->ifs_ipf_hm_maplist; 7823448Sdh155122 ifs->ifs_nat_stats.ns_nattab_max = ifs->ifs_ipf_nattable_max; 7833448Sdh155122 ifs->ifs_nat_stats.ns_nattab_sz = ifs->ifs_ipf_nattable_sz; 7843448Sdh155122 ifs->ifs_nat_stats.ns_rultab_sz = ifs->ifs_ipf_natrules_sz; 7853448Sdh155122 ifs->ifs_nat_stats.ns_rdrtab_sz = ifs->ifs_ipf_rdrrules_sz; 7863448Sdh155122 ifs->ifs_nat_stats.ns_hostmap_sz = ifs->ifs_ipf_hostmap_sz; 7873448Sdh155122 ifs->ifs_nat_stats.ns_instances = ifs->ifs_nat_instances; 7883448Sdh155122 ifs->ifs_nat_stats.ns_apslist = ifs->ifs_ap_sess_list; 7893448Sdh155122 error = fr_outobj(data, &ifs->ifs_nat_stats, IPFOBJ_NATSTAT); 7902393Syz155240 break; 7912393Syz155240 case SIOCGNATL : 7922393Syz155240 { 7932393Syz155240 natlookup_t nl; 7942393Syz155240 7952393Syz155240 if (getlock) { 7963448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 7972393Syz155240 } 7982393Syz155240 error = fr_inobj(data, &nl, IPFOBJ_NATLOOKUP); 7992393Syz155240 if (error == 0) { 8003448Sdh155122 if (nat_lookupredir(&nl, ifs) != NULL) { 8012393Syz155240 error = fr_outobj(data, &nl, IPFOBJ_NATLOOKUP); 8022393Syz155240 } else { 8032393Syz155240 error = ESRCH; 8042393Syz155240 } 8052393Syz155240 } 8062393Syz155240 if (getlock) { 8073448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 8082393Syz155240 } 8092393Syz155240 break; 8102393Syz155240 } 8112393Syz155240 case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */ 8122393Syz155240 if (!(mode & FWRITE)) { 8132393Syz155240 error = EPERM; 8142393Syz155240 break; 8152393Syz155240 } 8162393Syz155240 if (getlock) { 8173448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 8182393Syz155240 } 8192393Syz155240 error = 0; 8202393Syz155240 if (arg == 0) 8213448Sdh155122 ret = nat_flushtable(ifs); 8222393Syz155240 else if (arg == 1) 8233448Sdh155122 ret = nat_clearlist(ifs); 8244817San207044 else if (arg >= 2 && arg <= 4) 8254817San207044 ret = nat_extraflush(arg - 2, ifs); 8262393Syz155240 else 8272393Syz155240 error = EINVAL; 8282393Syz155240 if (getlock) { 8293448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 8302393Syz155240 } 8312393Syz155240 if (error == 0) { 8322393Syz155240 BCOPYOUT(&ret, data, sizeof(ret)); 8332393Syz155240 } 8342393Syz155240 break; 8352393Syz155240 case SIOCPROXY : 8363448Sdh155122 error = appr_ioctl(data, cmd, mode, ifs); 8372393Syz155240 break; 8382393Syz155240 case SIOCSTLCK : 8392393Syz155240 if (!(mode & FWRITE)) { 8402393Syz155240 error = EPERM; 8412393Syz155240 } else { 8423448Sdh155122 fr_lock(data, &ifs->ifs_fr_nat_lock); 8432393Syz155240 } 8442393Syz155240 break; 8452393Syz155240 case SIOCSTPUT : 8462761Sjojemann if ((mode & FWRITE) != 0) { 8473448Sdh155122 error = fr_natputent(data, getlock, ifs); 8482393Syz155240 } else { 8492393Syz155240 error = EACCES; 8502393Syz155240 } 8512393Syz155240 break; 8522393Syz155240 case SIOCSTGSZ : 8533448Sdh155122 if (ifs->ifs_fr_nat_lock) { 8542393Syz155240 if (getlock) { 8553448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 8562393Syz155240 } 8573448Sdh155122 error = fr_natgetsz(data, ifs); 8582393Syz155240 if (getlock) { 8593448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 8602393Syz155240 } 8612393Syz155240 } else 8622393Syz155240 error = EACCES; 8632393Syz155240 break; 8642393Syz155240 case SIOCSTGET : 8653448Sdh155122 if (ifs->ifs_fr_nat_lock) { 8662393Syz155240 if (getlock) { 8673448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 8682393Syz155240 } 8693448Sdh155122 error = fr_natgetent(data, ifs); 8702393Syz155240 if (getlock) { 8713448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 8722393Syz155240 } 8732393Syz155240 } else 8742393Syz155240 error = EACCES; 8752393Syz155240 break; 8763448Sdh155122 case SIOCIPFDELTOK : 8773448Sdh155122 (void) BCOPYIN((caddr_t)data, (caddr_t)&arg, sizeof(arg)); 8783448Sdh155122 error = ipf_deltoken(arg, uid, ctx, ifs); 8793448Sdh155122 break; 8802393Syz155240 default : 8812393Syz155240 error = EINVAL; 8822393Syz155240 break; 8832393Syz155240 } 8842393Syz155240 done: 8852393Syz155240 if (nt) 8862393Syz155240 KFREE(nt); 8872393Syz155240 return error; 8882393Syz155240 } 8892393Syz155240 8902393Syz155240 8912393Syz155240 /* ------------------------------------------------------------------------ */ 8922393Syz155240 /* Function: nat_siocaddnat */ 8932393Syz155240 /* Returns: int - 0 == success, != 0 == failure */ 8942393Syz155240 /* Parameters: n(I) - pointer to new NAT rule */ 8952393Syz155240 /* np(I) - pointer to where to insert new NAT rule */ 8962393Syz155240 /* getlock(I) - flag indicating if lock on ipf_nat is held */ 8972393Syz155240 /* Mutex Locks: ipf_natio */ 8982393Syz155240 /* */ 8992393Syz155240 /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 9002393Syz155240 /* from information passed to the kernel, then add it to the appropriate */ 9012393Syz155240 /* NAT rule table(s). */ 9022393Syz155240 /* ------------------------------------------------------------------------ */ 9033448Sdh155122 static int nat_siocaddnat(n, np, getlock, ifs) 9042393Syz155240 ipnat_t *n, **np; 9052393Syz155240 int getlock; 9063448Sdh155122 ipf_stack_t *ifs; 9072393Syz155240 { 9082393Syz155240 int error = 0, i, j; 9092393Syz155240 9104380Sjojemann if (nat_resolverule(n, ifs) != 0) 9114380Sjojemann return ENOENT; 9122393Syz155240 9132393Syz155240 if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) 9142393Syz155240 return EINVAL; 9152393Syz155240 9162393Syz155240 n->in_use = 0; 9172393Syz155240 if (n->in_redir & NAT_MAPBLK) 9182393Syz155240 n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk); 9192393Syz155240 else if (n->in_flags & IPN_AUTOPORTMAP) 9202393Syz155240 n->in_space = USABLE_PORTS * ~ntohl(n->in_inmsk); 9212393Syz155240 else if (n->in_flags & IPN_IPRANGE) 9222393Syz155240 n->in_space = ntohl(n->in_outmsk) - ntohl(n->in_outip); 9232393Syz155240 else if (n->in_flags & IPN_SPLIT) 9242393Syz155240 n->in_space = 2; 9252393Syz155240 else if (n->in_outmsk != 0) 9262393Syz155240 n->in_space = ~ntohl(n->in_outmsk); 9272393Syz155240 else 9282393Syz155240 n->in_space = 1; 9292393Syz155240 9302393Syz155240 /* 9312393Syz155240 * Calculate the number of valid IP addresses in the output 9322393Syz155240 * mapping range. In all cases, the range is inclusive of 9332393Syz155240 * the start and ending IP addresses. 9342393Syz155240 * If to a CIDR address, lose 2: broadcast + network address 9352393Syz155240 * (so subtract 1) 9362393Syz155240 * If to a range, add one. 9372393Syz155240 * If to a single IP address, set to 1. 9382393Syz155240 */ 9392393Syz155240 if (n->in_space) { 9402393Syz155240 if ((n->in_flags & IPN_IPRANGE) != 0) 9412393Syz155240 n->in_space += 1; 9422393Syz155240 else 9432393Syz155240 n->in_space -= 1; 9442393Syz155240 } else 9452393Syz155240 n->in_space = 1; 9462393Syz155240 9472393Syz155240 if ((n->in_outmsk != 0xffffffff) && (n->in_outmsk != 0) && 9482393Syz155240 ((n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0)) 9492393Syz155240 n->in_nip = ntohl(n->in_outip) + 1; 9502393Syz155240 else if ((n->in_flags & IPN_SPLIT) && 9512393Syz155240 (n->in_redir & NAT_REDIRECT)) 9522393Syz155240 n->in_nip = ntohl(n->in_inip); 9532393Syz155240 else 9542393Syz155240 n->in_nip = ntohl(n->in_outip); 9552393Syz155240 if (n->in_redir & NAT_MAP) { 9562393Syz155240 n->in_pnext = ntohs(n->in_pmin); 9572393Syz155240 /* 9582393Syz155240 * Multiply by the number of ports made available. 9592393Syz155240 */ 9602393Syz155240 if (ntohs(n->in_pmax) >= ntohs(n->in_pmin)) { 9612393Syz155240 n->in_space *= (ntohs(n->in_pmax) - 9622393Syz155240 ntohs(n->in_pmin) + 1); 9632393Syz155240 /* 9642393Syz155240 * Because two different sources can map to 9652393Syz155240 * different destinations but use the same 9662393Syz155240 * local IP#/port #. 9672393Syz155240 * If the result is smaller than in_space, then 9682393Syz155240 * we may have wrapped around 32bits. 9692393Syz155240 */ 9702393Syz155240 i = n->in_inmsk; 9712393Syz155240 if ((i != 0) && (i != 0xffffffff)) { 9722393Syz155240 j = n->in_space * (~ntohl(i) + 1); 9732393Syz155240 if (j >= n->in_space) 9742393Syz155240 n->in_space = j; 9752393Syz155240 else 9762393Syz155240 n->in_space = 0xffffffff; 9772393Syz155240 } 9782393Syz155240 } 9792393Syz155240 /* 9802393Syz155240 * If no protocol is specified, multiple by 256 to allow for 9812393Syz155240 * at least one IP:IP mapping per protocol. 9822393Syz155240 */ 9832393Syz155240 if ((n->in_flags & IPN_TCPUDPICMP) == 0) { 9842393Syz155240 j = n->in_space * 256; 9852393Syz155240 if (j >= n->in_space) 9862393Syz155240 n->in_space = j; 9872393Syz155240 else 9882393Syz155240 n->in_space = 0xffffffff; 9892393Syz155240 } 9902393Syz155240 } 9912393Syz155240 9922393Syz155240 /* Otherwise, these fields are preset */ 9932393Syz155240 9942393Syz155240 if (getlock) { 9953448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 9962393Syz155240 } 9972393Syz155240 n->in_next = NULL; 9982393Syz155240 *np = n; 9992393Syz155240 10002393Syz155240 if (n->in_age[0] != 0) 10013448Sdh155122 n->in_tqehead[0] = fr_addtimeoutqueue(&ifs->ifs_nat_utqe, 10023448Sdh155122 n->in_age[0], ifs); 10032393Syz155240 10042393Syz155240 if (n->in_age[1] != 0) 10053448Sdh155122 n->in_tqehead[1] = fr_addtimeoutqueue(&ifs->ifs_nat_utqe, 10063448Sdh155122 n->in_age[1], ifs); 10072393Syz155240 10082393Syz155240 if (n->in_redir & NAT_REDIRECT) { 10092393Syz155240 n->in_flags &= ~IPN_NOTDST; 10103448Sdh155122 nat_addrdr(n, ifs); 10112393Syz155240 } 10122393Syz155240 if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { 10132393Syz155240 n->in_flags &= ~IPN_NOTSRC; 10143448Sdh155122 nat_addnat(n, ifs); 10152393Syz155240 } 10162393Syz155240 n = NULL; 10173448Sdh155122 ifs->ifs_nat_stats.ns_rules++; 10182393Syz155240 if (getlock) { 10193448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); /* WRITE */ 10202393Syz155240 } 10212393Syz155240 10222393Syz155240 return error; 10232393Syz155240 } 10242393Syz155240 10252393Syz155240 10262393Syz155240 /* ------------------------------------------------------------------------ */ 10272393Syz155240 /* Function: nat_resolvrule */ 10284380Sjojemann /* Returns: int - 0 == success, -1 == failure */ 10292393Syz155240 /* Parameters: n(I) - pointer to NAT rule */ 10302393Syz155240 /* */ 10314380Sjojemann /* Resolve some of the details inside the NAT rule. Includes resolving */ 10324380Sjojemann /* any specified interfaces and proxy labels, and determines whether or not */ 10334380Sjojemann /* all proxy labels are correctly specified. */ 10344380Sjojemann /* */ 10354380Sjojemann /* Called by nat_siocaddnat() (SIOCADNAT) and fr_natputent (SIOCSTPUT). */ 10362393Syz155240 /* ------------------------------------------------------------------------ */ 10374380Sjojemann static int nat_resolverule(n, ifs) 10382393Syz155240 ipnat_t *n; 10393448Sdh155122 ipf_stack_t *ifs; 10402393Syz155240 { 10412393Syz155240 n->in_ifnames[0][LIFNAMSIZ - 1] = '\0'; 10423448Sdh155122 n->in_ifps[0] = fr_resolvenic(n->in_ifnames[0], 4, ifs); 10432393Syz155240 10442393Syz155240 n->in_ifnames[1][LIFNAMSIZ - 1] = '\0'; 10452393Syz155240 if (n->in_ifnames[1][0] == '\0') { 10462393Syz155240 (void) strncpy(n->in_ifnames[1], n->in_ifnames[0], LIFNAMSIZ); 10472393Syz155240 n->in_ifps[1] = n->in_ifps[0]; 10482393Syz155240 } else { 10493894Sjojemann n->in_ifps[1] = fr_resolvenic(n->in_ifnames[1], 4, ifs); 10502393Syz155240 } 10512393Syz155240 10522393Syz155240 if (n->in_plabel[0] != '\0') { 10533448Sdh155122 n->in_apr = appr_lookup(n->in_p, n->in_plabel, ifs); 10544380Sjojemann if (n->in_apr == NULL) 10554380Sjojemann return -1; 10562393Syz155240 } 10574380Sjojemann return 0; 10582393Syz155240 } 10592393Syz155240 10602393Syz155240 10612393Syz155240 /* ------------------------------------------------------------------------ */ 10622393Syz155240 /* Function: nat_siocdelnat */ 10632393Syz155240 /* Returns: int - 0 == success, != 0 == failure */ 10642393Syz155240 /* Parameters: n(I) - pointer to new NAT rule */ 10652393Syz155240 /* np(I) - pointer to where to insert new NAT rule */ 10662393Syz155240 /* getlock(I) - flag indicating if lock on ipf_nat is held */ 10672393Syz155240 /* Mutex Locks: ipf_natio */ 10682393Syz155240 /* */ 10692393Syz155240 /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 10702393Syz155240 /* from information passed to the kernel, then add it to the appropriate */ 10712393Syz155240 /* NAT rule table(s). */ 10722393Syz155240 /* ------------------------------------------------------------------------ */ 10733448Sdh155122 static void nat_siocdelnat(n, np, getlock, ifs) 10742393Syz155240 ipnat_t *n, **np; 10752393Syz155240 int getlock; 10763448Sdh155122 ipf_stack_t *ifs; 10772393Syz155240 { 10782393Syz155240 if (getlock) { 10793448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 10802393Syz155240 } 10812393Syz155240 if (n->in_redir & NAT_REDIRECT) 10822393Syz155240 nat_delrdr(n); 10832393Syz155240 if (n->in_redir & (NAT_MAPBLK|NAT_MAP)) 10842393Syz155240 nat_delnat(n); 10853448Sdh155122 if (ifs->ifs_nat_list == NULL) { 10863448Sdh155122 ifs->ifs_nat_masks = 0; 10873448Sdh155122 ifs->ifs_rdr_masks = 0; 10882393Syz155240 } 10892393Syz155240 10902393Syz155240 if (n->in_tqehead[0] != NULL) { 10912393Syz155240 if (fr_deletetimeoutqueue(n->in_tqehead[0]) == 0) { 10924817San207044 fr_freetimeoutqueue(n->in_tqehead[0], ifs); 10932393Syz155240 } 10942393Syz155240 } 10952393Syz155240 10962393Syz155240 if (n->in_tqehead[1] != NULL) { 10972393Syz155240 if (fr_deletetimeoutqueue(n->in_tqehead[1]) == 0) { 10983448Sdh155122 fr_freetimeoutqueue(n->in_tqehead[1], ifs); 10992393Syz155240 } 11002393Syz155240 } 11012393Syz155240 11022393Syz155240 *np = n->in_next; 11032393Syz155240 11042393Syz155240 if (n->in_use == 0) { 11052393Syz155240 if (n->in_apr) 11062393Syz155240 appr_free(n->in_apr); 11072393Syz155240 KFREE(n); 11083448Sdh155122 ifs->ifs_nat_stats.ns_rules--; 11092393Syz155240 } else { 11102393Syz155240 n->in_flags |= IPN_DELETE; 11112393Syz155240 n->in_next = NULL; 11122393Syz155240 } 11132393Syz155240 if (getlock) { 11143448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); /* READ/WRITE */ 11152393Syz155240 } 11162393Syz155240 } 11172393Syz155240 11182393Syz155240 11192393Syz155240 /* ------------------------------------------------------------------------ */ 11202393Syz155240 /* Function: fr_natgetsz */ 11212393Syz155240 /* Returns: int - 0 == success, != 0 is the error value. */ 11222393Syz155240 /* Parameters: data(I) - pointer to natget structure with kernel pointer */ 11232393Syz155240 /* get the size of. */ 11242393Syz155240 /* */ 11252393Syz155240 /* Handle SIOCSTGSZ. */ 11262393Syz155240 /* Return the size of the nat list entry to be copied back to user space. */ 11272393Syz155240 /* The size of the entry is stored in the ng_sz field and the enture natget */ 11282393Syz155240 /* structure is copied back to the user. */ 11292393Syz155240 /* ------------------------------------------------------------------------ */ 11303448Sdh155122 static int fr_natgetsz(data, ifs) 11312393Syz155240 caddr_t data; 11323448Sdh155122 ipf_stack_t *ifs; 11332393Syz155240 { 11342393Syz155240 ap_session_t *aps; 11352393Syz155240 nat_t *nat, *n; 11362393Syz155240 natget_t ng; 11372393Syz155240 11382393Syz155240 BCOPYIN(data, &ng, sizeof(ng)); 11392393Syz155240 11402393Syz155240 nat = ng.ng_ptr; 11412393Syz155240 if (!nat) { 11423448Sdh155122 nat = ifs->ifs_nat_instances; 11432393Syz155240 ng.ng_sz = 0; 11442393Syz155240 /* 11452393Syz155240 * Empty list so the size returned is 0. Simple. 11462393Syz155240 */ 11472393Syz155240 if (nat == NULL) { 11482393Syz155240 BCOPYOUT(&ng, data, sizeof(ng)); 11492393Syz155240 return 0; 11502393Syz155240 } 11512393Syz155240 } else { 11522393Syz155240 /* 11532393Syz155240 * Make sure the pointer we're copying from exists in the 11542393Syz155240 * current list of entries. Security precaution to prevent 11552393Syz155240 * copying of random kernel data. 11562393Syz155240 */ 11573448Sdh155122 for (n = ifs->ifs_nat_instances; n; n = n->nat_next) 11582393Syz155240 if (n == nat) 11592393Syz155240 break; 11602393Syz155240 if (!n) 11612393Syz155240 return ESRCH; 11622393Syz155240 } 11632393Syz155240 11642393Syz155240 /* 11652393Syz155240 * Incluse any space required for proxy data structures. 11662393Syz155240 */ 11672393Syz155240 ng.ng_sz = sizeof(nat_save_t); 11682393Syz155240 aps = nat->nat_aps; 11692393Syz155240 if (aps != NULL) { 11702393Syz155240 ng.ng_sz += sizeof(ap_session_t) - 4; 11712393Syz155240 if (aps->aps_data != 0) 11722393Syz155240 ng.ng_sz += aps->aps_psiz; 11732393Syz155240 } 11742393Syz155240 11752393Syz155240 BCOPYOUT(&ng, data, sizeof(ng)); 11762393Syz155240 return 0; 11772393Syz155240 } 11782393Syz155240 11792393Syz155240 11802393Syz155240 /* ------------------------------------------------------------------------ */ 11812393Syz155240 /* Function: fr_natgetent */ 11822393Syz155240 /* Returns: int - 0 == success, != 0 is the error value. */ 11832393Syz155240 /* Parameters: data(I) - pointer to natget structure with kernel pointer */ 11842393Syz155240 /* to NAT structure to copy out. */ 11852393Syz155240 /* */ 11862393Syz155240 /* Handle SIOCSTGET. */ 11872393Syz155240 /* Copies out NAT entry to user space. Any additional data held for a */ 11882393Syz155240 /* proxy is also copied, as to is the NAT rule which was responsible for it */ 11892393Syz155240 /* ------------------------------------------------------------------------ */ 11903448Sdh155122 static int fr_natgetent(data, ifs) 11912393Syz155240 caddr_t data; 11923448Sdh155122 ipf_stack_t *ifs; 11932393Syz155240 { 11942393Syz155240 int error, outsize; 11952393Syz155240 ap_session_t *aps; 11962393Syz155240 nat_save_t *ipn, ipns; 11972393Syz155240 nat_t *n, *nat; 11982393Syz155240 11992393Syz155240 error = fr_inobj(data, &ipns, IPFOBJ_NATSAVE); 12002393Syz155240 if (error != 0) 12012393Syz155240 return error; 12022393Syz155240 12032393Syz155240 if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) 12042393Syz155240 return EINVAL; 12052393Syz155240 12062393Syz155240 KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize); 12072393Syz155240 if (ipn == NULL) 12082393Syz155240 return ENOMEM; 12092393Syz155240 12102393Syz155240 ipn->ipn_dsize = ipns.ipn_dsize; 12112393Syz155240 nat = ipns.ipn_next; 12122393Syz155240 if (nat == NULL) { 12133448Sdh155122 nat = ifs->ifs_nat_instances; 12142393Syz155240 if (nat == NULL) { 12153448Sdh155122 if (ifs->ifs_nat_instances == NULL) 12162393Syz155240 error = ENOENT; 12172393Syz155240 goto finished; 12182393Syz155240 } 12192393Syz155240 } else { 12202393Syz155240 /* 12212393Syz155240 * Make sure the pointer we're copying from exists in the 12222393Syz155240 * current list of entries. Security precaution to prevent 12232393Syz155240 * copying of random kernel data. 12242393Syz155240 */ 12253448Sdh155122 for (n = ifs->ifs_nat_instances; n; n = n->nat_next) 12262393Syz155240 if (n == nat) 12272393Syz155240 break; 12282393Syz155240 if (n == NULL) { 12292393Syz155240 error = ESRCH; 12302393Syz155240 goto finished; 12312393Syz155240 } 12322393Syz155240 } 12332393Syz155240 ipn->ipn_next = nat->nat_next; 12342393Syz155240 12352393Syz155240 /* 12362393Syz155240 * Copy the NAT structure. 12372393Syz155240 */ 12382393Syz155240 bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat)); 12392393Syz155240 12402393Syz155240 /* 12412393Syz155240 * If we have a pointer to the NAT rule it belongs to, save that too. 12422393Syz155240 */ 12432393Syz155240 if (nat->nat_ptr != NULL) 12442393Syz155240 bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat, 12452393Syz155240 sizeof(ipn->ipn_ipnat)); 12462393Syz155240 12472393Syz155240 /* 12482393Syz155240 * If we also know the NAT entry has an associated filter rule, 12492393Syz155240 * save that too. 12502393Syz155240 */ 12512393Syz155240 if (nat->nat_fr != NULL) 12522393Syz155240 bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr, 12532393Syz155240 sizeof(ipn->ipn_fr)); 12542393Syz155240 12552393Syz155240 /* 12562393Syz155240 * Last but not least, if there is an application proxy session set 12572393Syz155240 * up for this NAT entry, then copy that out too, including any 12582393Syz155240 * private data saved along side it by the proxy. 12592393Syz155240 */ 12602393Syz155240 aps = nat->nat_aps; 12612393Syz155240 outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data); 12622393Syz155240 if (aps != NULL) { 12632393Syz155240 char *s; 12642393Syz155240 12652393Syz155240 if (outsize < sizeof(*aps)) { 12662393Syz155240 error = ENOBUFS; 12672393Syz155240 goto finished; 12682393Syz155240 } 12692393Syz155240 12702393Syz155240 s = ipn->ipn_data; 12712393Syz155240 bcopy((char *)aps, s, sizeof(*aps)); 12722393Syz155240 s += sizeof(*aps); 12732393Syz155240 outsize -= sizeof(*aps); 12742393Syz155240 if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz)) 12752393Syz155240 bcopy(aps->aps_data, s, aps->aps_psiz); 12762393Syz155240 else 12772393Syz155240 error = ENOBUFS; 12782393Syz155240 } 12792393Syz155240 if (error == 0) { 12802393Syz155240 error = fr_outobjsz(data, ipn, IPFOBJ_NATSAVE, ipns.ipn_dsize); 12812393Syz155240 } 12822393Syz155240 12832393Syz155240 finished: 12842393Syz155240 if (ipn != NULL) { 12852393Syz155240 KFREES(ipn, ipns.ipn_dsize); 12862393Syz155240 } 12872393Syz155240 return error; 12882393Syz155240 } 12892393Syz155240 12902393Syz155240 12912393Syz155240 /* ------------------------------------------------------------------------ */ 12922393Syz155240 /* Function: fr_natputent */ 12932393Syz155240 /* Returns: int - 0 == success, != 0 is the error value. */ 12942393Syz155240 /* Parameters: data(I) - pointer to natget structure with NAT */ 12952393Syz155240 /* structure information to load into the kernel */ 12962393Syz155240 /* getlock(I) - flag indicating whether or not a write lock */ 12972393Syz155240 /* on ipf_nat is already held. */ 12982393Syz155240 /* */ 12992393Syz155240 /* Handle SIOCSTPUT. */ 13002393Syz155240 /* Loads a NAT table entry from user space, including a NAT rule, proxy and */ 13012393Syz155240 /* firewall rule data structures, if pointers to them indicate so. */ 13022393Syz155240 /* ------------------------------------------------------------------------ */ 13033448Sdh155122 static int fr_natputent(data, getlock, ifs) 13042393Syz155240 caddr_t data; 13052393Syz155240 int getlock; 13063448Sdh155122 ipf_stack_t *ifs; 13072393Syz155240 { 13082393Syz155240 nat_save_t ipn, *ipnn; 13092393Syz155240 ap_session_t *aps; 13102393Syz155240 nat_t *n, *nat; 13112393Syz155240 frentry_t *fr; 13122393Syz155240 fr_info_t fin; 13132393Syz155240 ipnat_t *in; 13142393Syz155240 int error; 13152393Syz155240 13162393Syz155240 error = fr_inobj(data, &ipn, IPFOBJ_NATSAVE); 13172393Syz155240 if (error != 0) 13182393Syz155240 return error; 13192393Syz155240 13202393Syz155240 /* 13214817San207044 * Trigger automatic call to nat_extraflush() if the 13224817San207044 * table has reached capcity specified by hi watermark. 13234817San207044 */ 13244817San207044 if (NAT_TAB_WATER_LEVEL(ifs) > ifs->ifs_nat_flush_lvl_hi) 13254817San207044 ifs->ifs_nat_doflush = 1; 13264817San207044 13274817San207044 /* 13282393Syz155240 * Initialise early because of code at junkput label. 13292393Syz155240 */ 13302393Syz155240 in = NULL; 13312393Syz155240 aps = NULL; 13322393Syz155240 nat = NULL; 13332393Syz155240 ipnn = NULL; 13342393Syz155240 13352393Syz155240 /* 13362393Syz155240 * New entry, copy in the rest of the NAT entry if it's size is more 13372393Syz155240 * than just the nat_t structure. 13382393Syz155240 */ 13392393Syz155240 fr = NULL; 13402393Syz155240 if (ipn.ipn_dsize > sizeof(ipn)) { 13412393Syz155240 if (ipn.ipn_dsize > 81920) { 13422393Syz155240 error = ENOMEM; 13432393Syz155240 goto junkput; 13442393Syz155240 } 13452393Syz155240 13462393Syz155240 KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize); 13472393Syz155240 if (ipnn == NULL) 13482393Syz155240 return ENOMEM; 13492393Syz155240 13502393Syz155240 error = fr_inobjsz(data, ipnn, IPFOBJ_NATSAVE, ipn.ipn_dsize); 13512393Syz155240 if (error != 0) { 13522393Syz155240 error = EFAULT; 13532393Syz155240 goto junkput; 13542393Syz155240 } 13552393Syz155240 } else 13562393Syz155240 ipnn = &ipn; 13572393Syz155240 13582393Syz155240 KMALLOC(nat, nat_t *); 13592393Syz155240 if (nat == NULL) { 13602393Syz155240 error = ENOMEM; 13612393Syz155240 goto junkput; 13622393Syz155240 } 13632393Syz155240 13642393Syz155240 bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat)); 13652393Syz155240 /* 13662393Syz155240 * Initialize all these so that nat_delete() doesn't cause a crash. 13672393Syz155240 */ 13682393Syz155240 bzero((char *)nat, offsetof(struct nat, nat_tqe)); 13692393Syz155240 nat->nat_tqe.tqe_pnext = NULL; 13702393Syz155240 nat->nat_tqe.tqe_next = NULL; 13712393Syz155240 nat->nat_tqe.tqe_ifq = NULL; 13722393Syz155240 nat->nat_tqe.tqe_parent = nat; 13732393Syz155240 13742393Syz155240 /* 13752393Syz155240 * Restore the rule associated with this nat session 13762393Syz155240 */ 13772393Syz155240 in = ipnn->ipn_nat.nat_ptr; 13782393Syz155240 if (in != NULL) { 13792393Syz155240 KMALLOC(in, ipnat_t *); 13802393Syz155240 nat->nat_ptr = in; 13812393Syz155240 if (in == NULL) { 13822393Syz155240 error = ENOMEM; 13832393Syz155240 goto junkput; 13842393Syz155240 } 13852393Syz155240 bzero((char *)in, offsetof(struct ipnat, in_next6)); 13862393Syz155240 bcopy((char *)&ipnn->ipn_ipnat, (char *)in, sizeof(*in)); 13872393Syz155240 in->in_use = 1; 13882393Syz155240 in->in_flags |= IPN_DELETE; 13892393Syz155240 13903448Sdh155122 ATOMIC_INC(ifs->ifs_nat_stats.ns_rules); 13913448Sdh155122 13924380Sjojemann if (nat_resolverule(in, ifs) != 0) { 13934380Sjojemann error = ESRCH; 13944380Sjojemann goto junkput; 13954380Sjojemann } 13962393Syz155240 } 13972393Syz155240 13982393Syz155240 /* 13992393Syz155240 * Check that the NAT entry doesn't already exist in the kernel. 14002393Syz155240 */ 14012393Syz155240 bzero((char *)&fin, sizeof(fin)); 14022393Syz155240 fin.fin_p = nat->nat_p; 14034414Sjojemann fin.fin_ifs = ifs; 14042393Syz155240 if (nat->nat_dir == NAT_OUTBOUND) { 14052393Syz155240 fin.fin_data[0] = ntohs(nat->nat_oport); 14062393Syz155240 fin.fin_data[1] = ntohs(nat->nat_outport); 14072508Syz155240 fin.fin_ifp = nat->nat_ifps[0]; 14082393Syz155240 if (getlock) { 14093448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 14102393Syz155240 } 14112393Syz155240 n = nat_inlookup(&fin, nat->nat_flags, fin.fin_p, 14122393Syz155240 nat->nat_oip, nat->nat_outip); 14132393Syz155240 if (getlock) { 14143448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 14152393Syz155240 } 14162393Syz155240 if (n != NULL) { 14172393Syz155240 error = EEXIST; 14182393Syz155240 goto junkput; 14192393Syz155240 } 14202393Syz155240 } else if (nat->nat_dir == NAT_INBOUND) { 14212393Syz155240 fin.fin_data[0] = ntohs(nat->nat_inport); 14222393Syz155240 fin.fin_data[1] = ntohs(nat->nat_oport); 14232508Syz155240 fin.fin_ifp = nat->nat_ifps[1]; 14242393Syz155240 if (getlock) { 14253448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 14262393Syz155240 } 14272393Syz155240 n = nat_outlookup(&fin, nat->nat_flags, fin.fin_p, 14282508Syz155240 nat->nat_inip, nat->nat_oip); 14292393Syz155240 if (getlock) { 14303448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 14312393Syz155240 } 14322393Syz155240 if (n != NULL) { 14332393Syz155240 error = EEXIST; 14342393Syz155240 goto junkput; 14352393Syz155240 } 14362393Syz155240 } else { 14372393Syz155240 error = EINVAL; 14382393Syz155240 goto junkput; 14392393Syz155240 } 14402393Syz155240 14412393Syz155240 /* 14422393Syz155240 * Restore ap_session_t structure. Include the private data allocated 14432393Syz155240 * if it was there. 14442393Syz155240 */ 14452393Syz155240 aps = nat->nat_aps; 14462393Syz155240 if (aps != NULL) { 14472393Syz155240 KMALLOC(aps, ap_session_t *); 14482393Syz155240 nat->nat_aps = aps; 14492393Syz155240 if (aps == NULL) { 14502393Syz155240 error = ENOMEM; 14512393Syz155240 goto junkput; 14522393Syz155240 } 14532393Syz155240 bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps)); 14542393Syz155240 if (in != NULL) 14552393Syz155240 aps->aps_apr = in->in_apr; 14562393Syz155240 else 14572393Syz155240 aps->aps_apr = NULL; 14582393Syz155240 if (aps->aps_psiz != 0) { 14592393Syz155240 if (aps->aps_psiz > 81920) { 14602393Syz155240 error = ENOMEM; 14612393Syz155240 goto junkput; 14622393Syz155240 } 14632393Syz155240 KMALLOCS(aps->aps_data, void *, aps->aps_psiz); 14642393Syz155240 if (aps->aps_data == NULL) { 14652393Syz155240 error = ENOMEM; 14662393Syz155240 goto junkput; 14672393Syz155240 } 14682393Syz155240 bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data, 14692393Syz155240 aps->aps_psiz); 14702393Syz155240 } else { 14712393Syz155240 aps->aps_psiz = 0; 14722393Syz155240 aps->aps_data = NULL; 14732393Syz155240 } 14742393Syz155240 } 14752393Syz155240 14762393Syz155240 /* 14772393Syz155240 * If there was a filtering rule associated with this entry then 14782393Syz155240 * build up a new one. 14792393Syz155240 */ 14802393Syz155240 fr = nat->nat_fr; 14812393Syz155240 if (fr != NULL) { 14822393Syz155240 if ((nat->nat_flags & SI_NEWFR) != 0) { 14832393Syz155240 KMALLOC(fr, frentry_t *); 14842393Syz155240 nat->nat_fr = fr; 14852393Syz155240 if (fr == NULL) { 14862393Syz155240 error = ENOMEM; 14872393Syz155240 goto junkput; 14882393Syz155240 } 14892393Syz155240 ipnn->ipn_nat.nat_fr = fr; 14902393Syz155240 (void) fr_outobj(data, ipnn, IPFOBJ_NATSAVE); 14912393Syz155240 bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr)); 14924380Sjojemann 14934380Sjojemann fr->fr_ref = 1; 14944380Sjojemann fr->fr_dsize = 0; 14954380Sjojemann fr->fr_data = NULL; 14964380Sjojemann fr->fr_type = FR_T_NONE; 14974380Sjojemann 14982393Syz155240 MUTEX_NUKE(&fr->fr_lock); 14992393Syz155240 MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock"); 15002393Syz155240 } else { 15014380Sjojemann if (getlock) { 15024380Sjojemann READ_ENTER(&ifs->ifs_ipf_nat); 15034380Sjojemann } 15043448Sdh155122 for (n = ifs->ifs_nat_instances; n; n = n->nat_next) 15052393Syz155240 if (n->nat_fr == fr) 15062393Syz155240 break; 15072393Syz155240 15082393Syz155240 if (n != NULL) { 15092393Syz155240 MUTEX_ENTER(&fr->fr_lock); 15102393Syz155240 fr->fr_ref++; 15112393Syz155240 MUTEX_EXIT(&fr->fr_lock); 15122393Syz155240 } 15134380Sjojemann if (getlock) { 15144380Sjojemann RWLOCK_EXIT(&ifs->ifs_ipf_nat); 15154380Sjojemann } 15162393Syz155240 if (!n) { 15172393Syz155240 error = ESRCH; 15182393Syz155240 goto junkput; 15192393Syz155240 } 15202393Syz155240 } 15212393Syz155240 } 15222393Syz155240 15232393Syz155240 if (ipnn != &ipn) { 15242393Syz155240 KFREES(ipnn, ipn.ipn_dsize); 15252393Syz155240 ipnn = NULL; 15262393Syz155240 } 15272393Syz155240 15282393Syz155240 if (getlock) { 15293448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 15302393Syz155240 } 15313448Sdh155122 error = nat_insert(nat, nat->nat_rev, ifs); 15322393Syz155240 if ((error == 0) && (aps != NULL)) { 15333448Sdh155122 aps->aps_next = ifs->ifs_ap_sess_list; 15343448Sdh155122 ifs->ifs_ap_sess_list = aps; 15352393Syz155240 } 15362393Syz155240 if (getlock) { 15373448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 15382393Syz155240 } 15392393Syz155240 15402393Syz155240 if (error == 0) 15412393Syz155240 return 0; 15422393Syz155240 15432393Syz155240 error = ENOMEM; 15442393Syz155240 15452393Syz155240 junkput: 15462393Syz155240 if (fr != NULL) 15473448Sdh155122 (void) fr_derefrule(&fr, ifs); 15482393Syz155240 15492393Syz155240 if ((ipnn != NULL) && (ipnn != &ipn)) { 15502393Syz155240 KFREES(ipnn, ipn.ipn_dsize); 15512393Syz155240 } 15522393Syz155240 if (nat != NULL) { 15532393Syz155240 if (aps != NULL) { 15542393Syz155240 if (aps->aps_data != NULL) { 15552393Syz155240 KFREES(aps->aps_data, aps->aps_psiz); 15562393Syz155240 } 15572393Syz155240 KFREE(aps); 15582393Syz155240 } 15592393Syz155240 if (in != NULL) { 15602393Syz155240 if (in->in_apr) 15612393Syz155240 appr_free(in->in_apr); 15622393Syz155240 KFREE(in); 15632393Syz155240 } 15642393Syz155240 KFREE(nat); 15652393Syz155240 } 15662393Syz155240 return error; 15672393Syz155240 } 15682393Syz155240 15692393Syz155240 15702393Syz155240 /* ------------------------------------------------------------------------ */ 15712393Syz155240 /* Function: nat_delete */ 15722393Syz155240 /* Returns: Nil */ 15732393Syz155240 /* Parameters: natd(I) - pointer to NAT structure to delete */ 15742393Syz155240 /* logtype(I) - type of LOG record to create before deleting */ 15752393Syz155240 /* Write Lock: ipf_nat */ 15762393Syz155240 /* */ 15772393Syz155240 /* Delete a nat entry from the various lists and table. If NAT logging is */ 15782393Syz155240 /* enabled then generate a NAT log record for this event. */ 15792393Syz155240 /* ------------------------------------------------------------------------ */ 15803448Sdh155122 static void nat_delete(nat, logtype, ifs) 15812393Syz155240 struct nat *nat; 15822393Syz155240 int logtype; 15833448Sdh155122 ipf_stack_t *ifs; 15842393Syz155240 { 15852393Syz155240 struct ipnat *ipn; 15862393Syz155240 15873448Sdh155122 if (logtype != 0 && ifs->ifs_nat_logging != 0) 15883448Sdh155122 nat_log(nat, logtype, ifs); 15893448Sdh155122 15902393Syz155240 /* 15912393Syz155240 * Take it as a general indication that all the pointers are set if 15922393Syz155240 * nat_pnext is set. 15932393Syz155240 */ 15942393Syz155240 if (nat->nat_pnext != NULL) { 15953448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--; 15963448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--; 15972393Syz155240 15982393Syz155240 *nat->nat_pnext = nat->nat_next; 15992393Syz155240 if (nat->nat_next != NULL) { 16002393Syz155240 nat->nat_next->nat_pnext = nat->nat_pnext; 16012393Syz155240 nat->nat_next = NULL; 16022393Syz155240 } 16032393Syz155240 nat->nat_pnext = NULL; 16042393Syz155240 16052393Syz155240 *nat->nat_phnext[0] = nat->nat_hnext[0]; 16062393Syz155240 if (nat->nat_hnext[0] != NULL) { 16072393Syz155240 nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 16082393Syz155240 nat->nat_hnext[0] = NULL; 16092393Syz155240 } 16102393Syz155240 nat->nat_phnext[0] = NULL; 16112393Syz155240 16122393Syz155240 *nat->nat_phnext[1] = nat->nat_hnext[1]; 16132393Syz155240 if (nat->nat_hnext[1] != NULL) { 16142393Syz155240 nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 16152393Syz155240 nat->nat_hnext[1] = NULL; 16162393Syz155240 } 16172393Syz155240 nat->nat_phnext[1] = NULL; 16182393Syz155240 16192393Syz155240 if ((nat->nat_flags & SI_WILDP) != 0) 16203448Sdh155122 ifs->ifs_nat_stats.ns_wilds--; 16212393Syz155240 } 16222393Syz155240 16232393Syz155240 if (nat->nat_me != NULL) { 16242393Syz155240 *nat->nat_me = NULL; 16252393Syz155240 nat->nat_me = NULL; 16262393Syz155240 } 16272393Syz155240 16282393Syz155240 fr_deletequeueentry(&nat->nat_tqe); 16292393Syz155240 1630*5055Sdr146992 MUTEX_ENTER(&nat->nat_lock); 1631*5055Sdr146992 if (nat->nat_ref > 1) { 1632*5055Sdr146992 nat->nat_ref--; 1633*5055Sdr146992 MUTEX_EXIT(&nat->nat_lock); 16342393Syz155240 return; 16352393Syz155240 } 1636*5055Sdr146992 MUTEX_EXIT(&nat->nat_lock); 1637*5055Sdr146992 1638*5055Sdr146992 /* 1639*5055Sdr146992 * At this point, nat_ref is 1, doing "--" would make it 0.. 1640*5055Sdr146992 */ 1641*5055Sdr146992 nat->nat_ref = 0; 16422393Syz155240 16432393Syz155240 #ifdef IPFILTER_SYNC 16442393Syz155240 if (nat->nat_sync) 16452393Syz155240 ipfsync_del(nat->nat_sync); 16462393Syz155240 #endif 16472393Syz155240 16482393Syz155240 if (nat->nat_fr != NULL) 16493448Sdh155122 (void)fr_derefrule(&nat->nat_fr, ifs); 16502393Syz155240 16512393Syz155240 if (nat->nat_hm != NULL) 16522393Syz155240 nat_hostmapdel(nat->nat_hm); 16532393Syz155240 16542393Syz155240 /* 16552393Syz155240 * If there is an active reference from the nat entry to its parent 16562393Syz155240 * rule, decrement the rule's reference count and free it too if no 16572393Syz155240 * longer being used. 16582393Syz155240 */ 16592393Syz155240 ipn = nat->nat_ptr; 16602393Syz155240 if (ipn != NULL) { 16612393Syz155240 ipn->in_space++; 16622393Syz155240 ipn->in_use--; 16632393Syz155240 if (ipn->in_use == 0 && (ipn->in_flags & IPN_DELETE)) { 16642393Syz155240 if (ipn->in_apr) 16652393Syz155240 appr_free(ipn->in_apr); 16662393Syz155240 KFREE(ipn); 16673448Sdh155122 ifs->ifs_nat_stats.ns_rules--; 16682393Syz155240 } 16692393Syz155240 } 16702393Syz155240 16712393Syz155240 MUTEX_DESTROY(&nat->nat_lock); 16722393Syz155240 16733448Sdh155122 aps_free(nat->nat_aps, ifs); 16743448Sdh155122 ifs->ifs_nat_stats.ns_inuse--; 16752393Syz155240 16762393Syz155240 /* 16772393Syz155240 * If there's a fragment table entry too for this nat entry, then 16782393Syz155240 * dereference that as well. This is after nat_lock is released 16792393Syz155240 * because of Tru64. 16802393Syz155240 */ 16813448Sdh155122 fr_forgetnat((void *)nat, ifs); 16822393Syz155240 16832393Syz155240 KFREE(nat); 16842393Syz155240 } 16852393Syz155240 16862393Syz155240 16872393Syz155240 /* ------------------------------------------------------------------------ */ 16882393Syz155240 /* Function: nat_flushtable */ 16892393Syz155240 /* Returns: int - number of NAT rules deleted */ 16902393Syz155240 /* Parameters: Nil */ 16912393Syz155240 /* */ 16922393Syz155240 /* Deletes all currently active NAT sessions. In deleting each NAT entry a */ 16932393Syz155240 /* log record should be emitted in nat_delete() if NAT logging is enabled. */ 16942393Syz155240 /* ------------------------------------------------------------------------ */ 16952393Syz155240 /* 16962393Syz155240 * nat_flushtable - clear the NAT table of all mapping entries. 16972393Syz155240 */ 16983448Sdh155122 static int nat_flushtable(ifs) 16993448Sdh155122 ipf_stack_t *ifs; 17002393Syz155240 { 17012393Syz155240 nat_t *nat; 17022393Syz155240 int j = 0; 17032393Syz155240 17042393Syz155240 /* 17052393Syz155240 * ALL NAT mappings deleted, so lets just make the deletions 17062393Syz155240 * quicker. 17072393Syz155240 */ 17083448Sdh155122 if (ifs->ifs_nat_table[0] != NULL) 17093448Sdh155122 bzero((char *)ifs->ifs_nat_table[0], 17103448Sdh155122 sizeof(ifs->ifs_nat_table[0]) * ifs->ifs_ipf_nattable_sz); 17113448Sdh155122 if (ifs->ifs_nat_table[1] != NULL) 17123448Sdh155122 bzero((char *)ifs->ifs_nat_table[1], 17133448Sdh155122 sizeof(ifs->ifs_nat_table[1]) * ifs->ifs_ipf_nattable_sz); 17143448Sdh155122 17153448Sdh155122 while ((nat = ifs->ifs_nat_instances) != NULL) { 17163448Sdh155122 nat_delete(nat, NL_FLUSH, ifs); 17172393Syz155240 j++; 17182393Syz155240 } 17192393Syz155240 17203448Sdh155122 ifs->ifs_nat_stats.ns_inuse = 0; 17212393Syz155240 return j; 17222393Syz155240 } 17232393Syz155240 17242393Syz155240 17252393Syz155240 /* ------------------------------------------------------------------------ */ 17262393Syz155240 /* Function: nat_clearlist */ 17272393Syz155240 /* Returns: int - number of NAT/RDR rules deleted */ 17282393Syz155240 /* Parameters: Nil */ 17292393Syz155240 /* */ 17302393Syz155240 /* Delete all rules in the current list of rules. There is nothing elegant */ 17312393Syz155240 /* about this cleanup: simply free all entries on the list of rules and */ 17322393Syz155240 /* clear out the tables used for hashed NAT rule lookups. */ 17332393Syz155240 /* ------------------------------------------------------------------------ */ 17343448Sdh155122 static int nat_clearlist(ifs) 17353448Sdh155122 ipf_stack_t *ifs; 17362393Syz155240 { 17373448Sdh155122 ipnat_t *n, **np = &ifs->ifs_nat_list; 17382393Syz155240 int i = 0; 17392393Syz155240 17403448Sdh155122 if (ifs->ifs_nat_rules != NULL) 17413448Sdh155122 bzero((char *)ifs->ifs_nat_rules, 17423448Sdh155122 sizeof(*ifs->ifs_nat_rules) * ifs->ifs_ipf_natrules_sz); 17433448Sdh155122 if (ifs->ifs_rdr_rules != NULL) 17443448Sdh155122 bzero((char *)ifs->ifs_rdr_rules, 17453448Sdh155122 sizeof(*ifs->ifs_rdr_rules) * ifs->ifs_ipf_rdrrules_sz); 17462393Syz155240 17472393Syz155240 while ((n = *np) != NULL) { 17482393Syz155240 *np = n->in_next; 17492393Syz155240 if (n->in_use == 0) { 17502393Syz155240 if (n->in_apr != NULL) 17512393Syz155240 appr_free(n->in_apr); 17522393Syz155240 KFREE(n); 17533448Sdh155122 ifs->ifs_nat_stats.ns_rules--; 17542393Syz155240 } else { 17552393Syz155240 n->in_flags |= IPN_DELETE; 17562393Syz155240 n->in_next = NULL; 17572393Syz155240 } 17582393Syz155240 i++; 17592393Syz155240 } 17603448Sdh155122 ifs->ifs_nat_masks = 0; 17613448Sdh155122 ifs->ifs_rdr_masks = 0; 17622393Syz155240 return i; 17632393Syz155240 } 17642393Syz155240 17652393Syz155240 17662393Syz155240 /* ------------------------------------------------------------------------ */ 17672393Syz155240 /* Function: nat_newmap */ 17682393Syz155240 /* Returns: int - -1 == error, 0 == success */ 17692393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 17702393Syz155240 /* nat(I) - pointer to NAT entry */ 17712393Syz155240 /* ni(I) - pointer to structure with misc. information needed */ 17722393Syz155240 /* to create new NAT entry. */ 17732393Syz155240 /* */ 17742393Syz155240 /* Given an empty NAT structure, populate it with new information about a */ 17752393Syz155240 /* new NAT session, as defined by the matching NAT rule. */ 17762393Syz155240 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 17772393Syz155240 /* to the new IP address for the translation. */ 17782393Syz155240 /* ------------------------------------------------------------------------ */ 17792393Syz155240 static INLINE int nat_newmap(fin, nat, ni) 17802393Syz155240 fr_info_t *fin; 17812393Syz155240 nat_t *nat; 17822393Syz155240 natinfo_t *ni; 17832393Syz155240 { 17842393Syz155240 u_short st_port, dport, sport, port, sp, dp; 17852393Syz155240 struct in_addr in, inb; 17862393Syz155240 hostmap_t *hm; 17872393Syz155240 u_32_t flags; 17882393Syz155240 u_32_t st_ip; 17892393Syz155240 ipnat_t *np; 17902393Syz155240 nat_t *natl; 17912393Syz155240 int l; 17923448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 17932393Syz155240 17942393Syz155240 /* 17952393Syz155240 * If it's an outbound packet which doesn't match any existing 17962393Syz155240 * record, then create a new port 17972393Syz155240 */ 17982393Syz155240 l = 0; 17992393Syz155240 hm = NULL; 18002393Syz155240 np = ni->nai_np; 18012393Syz155240 st_ip = np->in_nip; 18022393Syz155240 st_port = np->in_pnext; 18032393Syz155240 flags = ni->nai_flags; 18042393Syz155240 sport = ni->nai_sport; 18052393Syz155240 dport = ni->nai_dport; 18062393Syz155240 18072393Syz155240 /* 18082393Syz155240 * Do a loop until we either run out of entries to try or we find 18092393Syz155240 * a NAT mapping that isn't currently being used. This is done 18102393Syz155240 * because the change to the source is not (usually) being fixed. 18112393Syz155240 */ 18122393Syz155240 do { 18132393Syz155240 port = 0; 18142393Syz155240 in.s_addr = htonl(np->in_nip); 18152393Syz155240 if (l == 0) { 18162393Syz155240 /* 18172393Syz155240 * Check to see if there is an existing NAT 18182393Syz155240 * setup for this IP address pair. 18192393Syz155240 */ 18202393Syz155240 hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, 18213448Sdh155122 in, 0, ifs); 18222393Syz155240 if (hm != NULL) 18232393Syz155240 in.s_addr = hm->hm_mapip.s_addr; 18242393Syz155240 } else if ((l == 1) && (hm != NULL)) { 18252393Syz155240 nat_hostmapdel(hm); 18262393Syz155240 hm = NULL; 18272393Syz155240 } 18282393Syz155240 in.s_addr = ntohl(in.s_addr); 18292393Syz155240 18302393Syz155240 nat->nat_hm = hm; 18312393Syz155240 18322393Syz155240 if ((np->in_outmsk == 0xffffffff) && (np->in_pnext == 0)) { 18332393Syz155240 if (l > 0) 18342393Syz155240 return -1; 18352393Syz155240 } 18362393Syz155240 18372393Syz155240 if (np->in_redir == NAT_BIMAP && 18382393Syz155240 np->in_inmsk == np->in_outmsk) { 18392393Syz155240 /* 18402393Syz155240 * map the address block in a 1:1 fashion 18412393Syz155240 */ 18422393Syz155240 in.s_addr = np->in_outip; 18432393Syz155240 in.s_addr |= fin->fin_saddr & ~np->in_inmsk; 18442393Syz155240 in.s_addr = ntohl(in.s_addr); 18452393Syz155240 18462393Syz155240 } else if (np->in_redir & NAT_MAPBLK) { 18472393Syz155240 if ((l >= np->in_ppip) || ((l > 0) && 18482393Syz155240 !(flags & IPN_TCPUDP))) 18492393Syz155240 return -1; 18502393Syz155240 /* 18512393Syz155240 * map-block - Calculate destination address. 18522393Syz155240 */ 18532393Syz155240 in.s_addr = ntohl(fin->fin_saddr); 18542393Syz155240 in.s_addr &= ntohl(~np->in_inmsk); 18552393Syz155240 inb.s_addr = in.s_addr; 18562393Syz155240 in.s_addr /= np->in_ippip; 18572393Syz155240 in.s_addr &= ntohl(~np->in_outmsk); 18582393Syz155240 in.s_addr += ntohl(np->in_outip); 18592393Syz155240 /* 18602393Syz155240 * Calculate destination port. 18612393Syz155240 */ 18622393Syz155240 if ((flags & IPN_TCPUDP) && 18632393Syz155240 (np->in_ppip != 0)) { 18642393Syz155240 port = ntohs(sport) + l; 18652393Syz155240 port %= np->in_ppip; 18662393Syz155240 port += np->in_ppip * 18672393Syz155240 (inb.s_addr % np->in_ippip); 18682393Syz155240 port += MAPBLK_MINPORT; 18692393Syz155240 port = htons(port); 18702393Syz155240 } 18712393Syz155240 18722393Syz155240 } else if ((np->in_outip == 0) && 18732393Syz155240 (np->in_outmsk == 0xffffffff)) { 18742393Syz155240 /* 18752393Syz155240 * 0/32 - use the interface's IP address. 18762393Syz155240 */ 18772393Syz155240 if ((l > 0) || 18782393Syz155240 fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp, 18793448Sdh155122 &in, NULL, fin->fin_ifs) == -1) 18802393Syz155240 return -1; 18812393Syz155240 in.s_addr = ntohl(in.s_addr); 18822393Syz155240 18832393Syz155240 } else if ((np->in_outip == 0) && (np->in_outmsk == 0)) { 18842393Syz155240 /* 18852393Syz155240 * 0/0 - use the original source address/port. 18862393Syz155240 */ 18872393Syz155240 if (l > 0) 18882393Syz155240 return -1; 18892393Syz155240 in.s_addr = ntohl(fin->fin_saddr); 18902393Syz155240 18912393Syz155240 } else if ((np->in_outmsk != 0xffffffff) && 18922393Syz155240 (np->in_pnext == 0) && ((l > 0) || (hm == NULL))) 18932393Syz155240 np->in_nip++; 18942393Syz155240 18952393Syz155240 natl = NULL; 18962393Syz155240 18972393Syz155240 if ((flags & IPN_TCPUDP) && 18982393Syz155240 ((np->in_redir & NAT_MAPBLK) == 0) && 18992393Syz155240 (np->in_flags & IPN_AUTOPORTMAP)) { 19002393Syz155240 /* 19012393Syz155240 * "ports auto" (without map-block) 19022393Syz155240 */ 19032393Syz155240 if ((l > 0) && (l % np->in_ppip == 0)) { 19042393Syz155240 if (l > np->in_space) { 19052393Syz155240 return -1; 19062393Syz155240 } else if ((l > np->in_ppip) && 19072393Syz155240 np->in_outmsk != 0xffffffff) 19082393Syz155240 np->in_nip++; 19092393Syz155240 } 19102393Syz155240 if (np->in_ppip != 0) { 19112393Syz155240 port = ntohs(sport); 19122393Syz155240 port += (l % np->in_ppip); 19132393Syz155240 port %= np->in_ppip; 19142393Syz155240 port += np->in_ppip * 19152393Syz155240 (ntohl(fin->fin_saddr) % 19162393Syz155240 np->in_ippip); 19172393Syz155240 port += MAPBLK_MINPORT; 19182393Syz155240 port = htons(port); 19192393Syz155240 } 19202393Syz155240 19212393Syz155240 } else if (((np->in_redir & NAT_MAPBLK) == 0) && 19222393Syz155240 (flags & IPN_TCPUDPICMP) && (np->in_pnext != 0)) { 19232393Syz155240 /* 19242393Syz155240 * Standard port translation. Select next port. 19252393Syz155240 */ 19262393Syz155240 port = htons(np->in_pnext++); 19272393Syz155240 19282393Syz155240 if (np->in_pnext > ntohs(np->in_pmax)) { 19292393Syz155240 np->in_pnext = ntohs(np->in_pmin); 19302393Syz155240 if (np->in_outmsk != 0xffffffff) 19312393Syz155240 np->in_nip++; 19322393Syz155240 } 19332393Syz155240 } 19342393Syz155240 19352393Syz155240 if (np->in_flags & IPN_IPRANGE) { 19362393Syz155240 if (np->in_nip > ntohl(np->in_outmsk)) 19372393Syz155240 np->in_nip = ntohl(np->in_outip); 19382393Syz155240 } else { 19392393Syz155240 if ((np->in_outmsk != 0xffffffff) && 19402393Syz155240 ((np->in_nip + 1) & ntohl(np->in_outmsk)) > 19412393Syz155240 ntohl(np->in_outip)) 19422393Syz155240 np->in_nip = ntohl(np->in_outip) + 1; 19432393Syz155240 } 19442393Syz155240 19452393Syz155240 if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) 19462393Syz155240 port = sport; 19472393Syz155240 19482393Syz155240 /* 19492393Syz155240 * Here we do a lookup of the connection as seen from 19502393Syz155240 * the outside. If an IP# pair already exists, try 19512393Syz155240 * again. So if you have A->B becomes C->B, you can 19522393Syz155240 * also have D->E become C->E but not D->B causing 19532393Syz155240 * another C->B. Also take protocol and ports into 19542393Syz155240 * account when determining whether a pre-existing 19552393Syz155240 * NAT setup will cause an external conflict where 19562393Syz155240 * this is appropriate. 19572393Syz155240 */ 19582393Syz155240 inb.s_addr = htonl(in.s_addr); 19592393Syz155240 sp = fin->fin_data[0]; 19602393Syz155240 dp = fin->fin_data[1]; 19612393Syz155240 fin->fin_data[0] = fin->fin_data[1]; 19622393Syz155240 fin->fin_data[1] = htons(port); 19632393Syz155240 natl = nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 19642393Syz155240 (u_int)fin->fin_p, fin->fin_dst, inb); 19652393Syz155240 fin->fin_data[0] = sp; 19662393Syz155240 fin->fin_data[1] = dp; 19672393Syz155240 19682393Syz155240 /* 19692393Syz155240 * Has the search wrapped around and come back to the 19702393Syz155240 * start ? 19712393Syz155240 */ 19722393Syz155240 if ((natl != NULL) && 19732393Syz155240 (np->in_pnext != 0) && (st_port == np->in_pnext) && 19742393Syz155240 (np->in_nip != 0) && (st_ip == np->in_nip)) 19752393Syz155240 return -1; 19762393Syz155240 l++; 19772393Syz155240 } while (natl != NULL); 19782393Syz155240 19792393Syz155240 if (np->in_space > 0) 19802393Syz155240 np->in_space--; 19812393Syz155240 19822393Syz155240 /* Setup the NAT table */ 19832393Syz155240 nat->nat_inip = fin->fin_src; 19842393Syz155240 nat->nat_outip.s_addr = htonl(in.s_addr); 19852393Syz155240 nat->nat_oip = fin->fin_dst; 19862393Syz155240 if (nat->nat_hm == NULL) 19872393Syz155240 nat->nat_hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, 19883448Sdh155122 nat->nat_outip, 0, ifs); 19892393Syz155240 19902393Syz155240 /* 19912393Syz155240 * The ICMP checksum does not have a pseudo header containing 19922393Syz155240 * the IP addresses 19932393Syz155240 */ 19942393Syz155240 ni->nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr)); 19952393Syz155240 ni->nai_sum2 = LONG_SUM(in.s_addr); 19962393Syz155240 if ((flags & IPN_TCPUDP)) { 19972393Syz155240 ni->nai_sum1 += ntohs(sport); 19982393Syz155240 ni->nai_sum2 += ntohs(port); 19992393Syz155240 } 20002393Syz155240 20012393Syz155240 if (flags & IPN_TCPUDP) { 20022393Syz155240 nat->nat_inport = sport; 20032393Syz155240 nat->nat_outport = port; /* sport */ 20042393Syz155240 nat->nat_oport = dport; 20052393Syz155240 ((tcphdr_t *)fin->fin_dp)->th_sport = port; 20062393Syz155240 } else if (flags & IPN_ICMPQUERY) { 20072393Syz155240 ((icmphdr_t *)fin->fin_dp)->icmp_id = port; 20082393Syz155240 nat->nat_inport = port; 20092393Syz155240 nat->nat_outport = port; 20102393Syz155240 } 20112393Syz155240 20122393Syz155240 ni->nai_ip.s_addr = in.s_addr; 20132393Syz155240 ni->nai_port = port; 20142393Syz155240 ni->nai_nport = dport; 20152393Syz155240 return 0; 20162393Syz155240 } 20172393Syz155240 20182393Syz155240 20192393Syz155240 /* ------------------------------------------------------------------------ */ 20202393Syz155240 /* Function: nat_newrdr */ 20212393Syz155240 /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ 20222393Syz155240 /* allow rule to be moved if IPN_ROUNDR is set. */ 20232393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 20242393Syz155240 /* nat(I) - pointer to NAT entry */ 20252393Syz155240 /* ni(I) - pointer to structure with misc. information needed */ 20262393Syz155240 /* to create new NAT entry. */ 20272393Syz155240 /* */ 20282393Syz155240 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 20292393Syz155240 /* to the new IP address for the translation. */ 20302393Syz155240 /* ------------------------------------------------------------------------ */ 20312393Syz155240 static INLINE int nat_newrdr(fin, nat, ni) 20322393Syz155240 fr_info_t *fin; 20332393Syz155240 nat_t *nat; 20342393Syz155240 natinfo_t *ni; 20352393Syz155240 { 20362393Syz155240 u_short nport, dport, sport; 20372393Syz155240 struct in_addr in; 20382393Syz155240 hostmap_t *hm; 20392393Syz155240 u_32_t flags; 20402393Syz155240 ipnat_t *np; 20412393Syz155240 int move; 20423448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 20432393Syz155240 20442393Syz155240 move = 1; 20452393Syz155240 hm = NULL; 20462393Syz155240 in.s_addr = 0; 20472393Syz155240 np = ni->nai_np; 20482393Syz155240 flags = ni->nai_flags; 20492393Syz155240 sport = ni->nai_sport; 20502393Syz155240 dport = ni->nai_dport; 20512393Syz155240 20522393Syz155240 /* 20532393Syz155240 * If the matching rule has IPN_STICKY set, then we want to have the 20542393Syz155240 * same rule kick in as before. Why would this happen? If you have 20552393Syz155240 * a collection of rdr rules with "round-robin sticky", the current 20562393Syz155240 * packet might match a different one to the previous connection but 20572393Syz155240 * we want the same destination to be used. 20582393Syz155240 */ 20592393Syz155240 if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == 20602393Syz155240 (IPN_ROUNDR|IPN_STICKY)) { 20612393Syz155240 hm = nat_hostmap(NULL, fin->fin_src, fin->fin_dst, in, 20623448Sdh155122 (u_32_t)dport, ifs); 20632393Syz155240 if (hm != NULL) { 20642393Syz155240 in.s_addr = ntohl(hm->hm_mapip.s_addr); 20652393Syz155240 np = hm->hm_ipnat; 20662393Syz155240 ni->nai_np = np; 20672393Syz155240 move = 0; 20682393Syz155240 } 20692393Syz155240 } 20702393Syz155240 20712393Syz155240 /* 20722393Syz155240 * Otherwise, it's an inbound packet. Most likely, we don't 20732393Syz155240 * want to rewrite source ports and source addresses. Instead, 20742393Syz155240 * we want to rewrite to a fixed internal address and fixed 20752393Syz155240 * internal port. 20762393Syz155240 */ 20772393Syz155240 if (np->in_flags & IPN_SPLIT) { 20782393Syz155240 in.s_addr = np->in_nip; 20792393Syz155240 20802393Syz155240 if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { 20812393Syz155240 hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, 20823448Sdh155122 in, (u_32_t)dport, ifs); 20832393Syz155240 if (hm != NULL) { 20842393Syz155240 in.s_addr = hm->hm_mapip.s_addr; 20852393Syz155240 move = 0; 20862393Syz155240 } 20872393Syz155240 } 20882393Syz155240 20892393Syz155240 if (hm == NULL || hm->hm_ref == 1) { 20902393Syz155240 if (np->in_inip == htonl(in.s_addr)) { 20912393Syz155240 np->in_nip = ntohl(np->in_inmsk); 20922393Syz155240 move = 0; 20932393Syz155240 } else { 20942393Syz155240 np->in_nip = ntohl(np->in_inip); 20952393Syz155240 } 20962393Syz155240 } 20972393Syz155240 20982393Syz155240 } else if ((np->in_inip == 0) && (np->in_inmsk == 0xffffffff)) { 20992393Syz155240 /* 21002393Syz155240 * 0/32 - use the interface's IP address. 21012393Syz155240 */ 21023448Sdh155122 if (fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp, &in, NULL, 21033448Sdh155122 fin->fin_ifs) == -1) 21042393Syz155240 return -1; 21052393Syz155240 in.s_addr = ntohl(in.s_addr); 21062393Syz155240 21072393Syz155240 } else if ((np->in_inip == 0) && (np->in_inmsk== 0)) { 21082393Syz155240 /* 21092393Syz155240 * 0/0 - use the original destination address/port. 21102393Syz155240 */ 21112393Syz155240 in.s_addr = ntohl(fin->fin_daddr); 21122393Syz155240 21132393Syz155240 } else if (np->in_redir == NAT_BIMAP && 21142393Syz155240 np->in_inmsk == np->in_outmsk) { 21152393Syz155240 /* 21162393Syz155240 * map the address block in a 1:1 fashion 21172393Syz155240 */ 21182393Syz155240 in.s_addr = np->in_inip; 21192393Syz155240 in.s_addr |= fin->fin_daddr & ~np->in_inmsk; 21202393Syz155240 in.s_addr = ntohl(in.s_addr); 21212393Syz155240 } else { 21222393Syz155240 in.s_addr = ntohl(np->in_inip); 21232393Syz155240 } 21242393Syz155240 21252393Syz155240 if ((np->in_pnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) 21262393Syz155240 nport = dport; 21272393Syz155240 else { 21282393Syz155240 /* 21292393Syz155240 * Whilst not optimized for the case where 21302393Syz155240 * pmin == pmax, the gain is not significant. 21312393Syz155240 */ 21322393Syz155240 if (((np->in_flags & IPN_FIXEDDPORT) == 0) && 21332393Syz155240 (np->in_pmin != np->in_pmax)) { 21342393Syz155240 nport = ntohs(dport) - ntohs(np->in_pmin) + 21352393Syz155240 ntohs(np->in_pnext); 21362393Syz155240 nport = htons(nport); 21372393Syz155240 } else 21382393Syz155240 nport = np->in_pnext; 21392393Syz155240 } 21402393Syz155240 21412393Syz155240 /* 21422393Syz155240 * When the redirect-to address is set to 0.0.0.0, just 21432393Syz155240 * assume a blank `forwarding' of the packet. We don't 21442393Syz155240 * setup any translation for this either. 21452393Syz155240 */ 21462393Syz155240 if (in.s_addr == 0) { 21472393Syz155240 if (nport == dport) 21482393Syz155240 return -1; 21492393Syz155240 in.s_addr = ntohl(fin->fin_daddr); 21502393Syz155240 } 21512393Syz155240 21522393Syz155240 nat->nat_inip.s_addr = htonl(in.s_addr); 21532393Syz155240 nat->nat_outip = fin->fin_dst; 21542393Syz155240 nat->nat_oip = fin->fin_src; 21552393Syz155240 21562393Syz155240 ni->nai_sum1 = LONG_SUM(ntohl(fin->fin_daddr)) + ntohs(dport); 21572393Syz155240 ni->nai_sum2 = LONG_SUM(in.s_addr) + ntohs(nport); 21582393Syz155240 21592393Syz155240 ni->nai_ip.s_addr = in.s_addr; 21602393Syz155240 ni->nai_nport = nport; 21612393Syz155240 ni->nai_port = sport; 21622393Syz155240 21632393Syz155240 if (flags & IPN_TCPUDP) { 21642393Syz155240 nat->nat_inport = nport; 21652393Syz155240 nat->nat_outport = dport; 21662393Syz155240 nat->nat_oport = sport; 21672393Syz155240 ((tcphdr_t *)fin->fin_dp)->th_dport = nport; 21682393Syz155240 } else if (flags & IPN_ICMPQUERY) { 21692393Syz155240 ((icmphdr_t *)fin->fin_dp)->icmp_id = nport; 21702393Syz155240 nat->nat_inport = nport; 21712393Syz155240 nat->nat_outport = nport; 21722393Syz155240 } 21732393Syz155240 21742393Syz155240 return move; 21752393Syz155240 } 21762393Syz155240 21772393Syz155240 /* ------------------------------------------------------------------------ */ 21782393Syz155240 /* Function: nat_new */ 21792393Syz155240 /* Returns: nat_t* - NULL == failure to create new NAT structure, */ 21802393Syz155240 /* else pointer to new NAT structure */ 21812393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 21822393Syz155240 /* np(I) - pointer to NAT rule */ 21832393Syz155240 /* natsave(I) - pointer to where to store NAT struct pointer */ 21842393Syz155240 /* flags(I) - flags describing the current packet */ 21852393Syz155240 /* direction(I) - direction of packet (in/out) */ 21862393Syz155240 /* Write Lock: ipf_nat */ 21872393Syz155240 /* */ 21882393Syz155240 /* Attempts to create a new NAT entry. Does not actually change the packet */ 21892393Syz155240 /* in any way. */ 21902393Syz155240 /* */ 21912393Syz155240 /* This fucntion is in three main parts: (1) deal with creating a new NAT */ 21922393Syz155240 /* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */ 21932393Syz155240 /* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */ 21942393Syz155240 /* and (3) building that structure and putting it into the NAT table(s). */ 21952393Syz155240 /* ------------------------------------------------------------------------ */ 21962393Syz155240 nat_t *nat_new(fin, np, natsave, flags, direction) 21972393Syz155240 fr_info_t *fin; 21982393Syz155240 ipnat_t *np; 21992393Syz155240 nat_t **natsave; 22002393Syz155240 u_int flags; 22012393Syz155240 int direction; 22022393Syz155240 { 22032393Syz155240 u_short port = 0, sport = 0, dport = 0, nport = 0; 22042393Syz155240 tcphdr_t *tcp = NULL; 22052393Syz155240 hostmap_t *hm = NULL; 22062393Syz155240 struct in_addr in; 22072393Syz155240 nat_t *nat, *natl; 22082393Syz155240 u_int nflags; 22092393Syz155240 natinfo_t ni; 22102393Syz155240 u_32_t sumd; 22112393Syz155240 int move; 22123448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 22133448Sdh155122 22144817San207044 /* 22154817San207044 * Trigger automatic call to nat_extraflush() if the 22164817San207044 * table has reached capcity specified by hi watermark. 22174817San207044 */ 22184817San207044 if (NAT_TAB_WATER_LEVEL(ifs) > ifs->ifs_nat_flush_lvl_hi) 22194817San207044 ifs->ifs_nat_doflush = 1; 22204817San207044 22213448Sdh155122 if (ifs->ifs_nat_stats.ns_inuse >= ifs->ifs_ipf_nattable_max) { 22223448Sdh155122 ifs->ifs_nat_stats.ns_memfail++; 22232393Syz155240 return NULL; 22242393Syz155240 } 22252393Syz155240 22262393Syz155240 move = 1; 22272393Syz155240 nflags = np->in_flags & flags; 22282393Syz155240 nflags &= NAT_FROMRULE; 22292393Syz155240 22302393Syz155240 ni.nai_np = np; 22312393Syz155240 ni.nai_nflags = nflags; 22322393Syz155240 ni.nai_flags = flags; 22332393Syz155240 22342393Syz155240 /* Give me a new nat */ 22352393Syz155240 KMALLOC(nat, nat_t *); 22362393Syz155240 if (nat == NULL) { 22373448Sdh155122 ifs->ifs_nat_stats.ns_memfail++; 22382393Syz155240 /* 22392393Syz155240 * Try to automatically tune the max # of entries in the 22402393Syz155240 * table allowed to be less than what will cause kmem_alloc() 22412393Syz155240 * to fail and try to eliminate panics due to out of memory 22422393Syz155240 * conditions arising. 22432393Syz155240 */ 22443448Sdh155122 if (ifs->ifs_ipf_nattable_max > ifs->ifs_ipf_nattable_sz) { 22453448Sdh155122 ifs->ifs_ipf_nattable_max = ifs->ifs_nat_stats.ns_inuse - 100; 22462393Syz155240 printf("ipf_nattable_max reduced to %d\n", 22473448Sdh155122 ifs->ifs_ipf_nattable_max); 22482393Syz155240 } 22492393Syz155240 return NULL; 22502393Syz155240 } 22512393Syz155240 22522393Syz155240 if (flags & IPN_TCPUDP) { 22532393Syz155240 tcp = fin->fin_dp; 22542393Syz155240 ni.nai_sport = htons(fin->fin_sport); 22552393Syz155240 ni.nai_dport = htons(fin->fin_dport); 22562393Syz155240 } else if (flags & IPN_ICMPQUERY) { 22572393Syz155240 /* 22582393Syz155240 * In the ICMP query NAT code, we translate the ICMP id fields 22592393Syz155240 * to make them unique. This is indepedent of the ICMP type 22602393Syz155240 * (e.g. in the unlikely event that a host sends an echo and 22612393Syz155240 * an tstamp request with the same id, both packets will have 22622393Syz155240 * their ip address/id field changed in the same way). 22632393Syz155240 */ 22642393Syz155240 /* The icmp_id field is used by the sender to identify the 22652393Syz155240 * process making the icmp request. (the receiver justs 22662393Syz155240 * copies it back in its response). So, it closely matches 22672393Syz155240 * the concept of source port. We overlay sport, so we can 22682393Syz155240 * maximally reuse the existing code. 22692393Syz155240 */ 22702393Syz155240 ni.nai_sport = ((icmphdr_t *)fin->fin_dp)->icmp_id; 22712393Syz155240 ni.nai_dport = ni.nai_sport; 22722393Syz155240 } 22732393Syz155240 22742393Syz155240 bzero((char *)nat, sizeof(*nat)); 22752393Syz155240 nat->nat_flags = flags; 22763448Sdh155122 nat->nat_redir = np->in_redir; 22772393Syz155240 22782393Syz155240 if ((flags & NAT_SLAVE) == 0) { 22793448Sdh155122 MUTEX_ENTER(&ifs->ifs_ipf_nat_new); 22802393Syz155240 } 22812393Syz155240 22822393Syz155240 /* 22832393Syz155240 * Search the current table for a match. 22842393Syz155240 */ 22852393Syz155240 if (direction == NAT_OUTBOUND) { 22862393Syz155240 /* 22872393Syz155240 * We can now arrange to call this for the same connection 22882393Syz155240 * because ipf_nat_new doesn't protect the code path into 22892393Syz155240 * this function. 22902393Syz155240 */ 22912393Syz155240 natl = nat_outlookup(fin, nflags, (u_int)fin->fin_p, 22922393Syz155240 fin->fin_src, fin->fin_dst); 22932393Syz155240 if (natl != NULL) { 22944380Sjojemann KFREE(nat); 22952393Syz155240 nat = natl; 22962393Syz155240 goto done; 22972393Syz155240 } 22982393Syz155240 22992393Syz155240 move = nat_newmap(fin, nat, &ni); 23002393Syz155240 if (move == -1) 23012393Syz155240 goto badnat; 23022393Syz155240 23032393Syz155240 np = ni.nai_np; 23042393Syz155240 in = ni.nai_ip; 23052393Syz155240 } else { 23062393Syz155240 /* 23072393Syz155240 * NAT_INBOUND is used only for redirects rules 23082393Syz155240 */ 23092393Syz155240 natl = nat_inlookup(fin, nflags, (u_int)fin->fin_p, 23102393Syz155240 fin->fin_src, fin->fin_dst); 23112393Syz155240 if (natl != NULL) { 23124380Sjojemann KFREE(nat); 23132393Syz155240 nat = natl; 23142393Syz155240 goto done; 23152393Syz155240 } 23162393Syz155240 23172393Syz155240 move = nat_newrdr(fin, nat, &ni); 23182393Syz155240 if (move == -1) 23192393Syz155240 goto badnat; 23202393Syz155240 23212393Syz155240 np = ni.nai_np; 23222393Syz155240 in = ni.nai_ip; 23232393Syz155240 } 23242393Syz155240 port = ni.nai_port; 23252393Syz155240 nport = ni.nai_nport; 23262393Syz155240 23272393Syz155240 if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { 23282393Syz155240 if (np->in_redir == NAT_REDIRECT) { 23292393Syz155240 nat_delrdr(np); 23303448Sdh155122 nat_addrdr(np, ifs); 23312393Syz155240 } else if (np->in_redir == NAT_MAP) { 23322393Syz155240 nat_delnat(np); 23333448Sdh155122 nat_addnat(np, ifs); 23342393Syz155240 } 23352393Syz155240 } 23362393Syz155240 23372393Syz155240 if (flags & IPN_TCPUDP) { 23382393Syz155240 sport = ni.nai_sport; 23392393Syz155240 dport = ni.nai_dport; 23402393Syz155240 } else if (flags & IPN_ICMPQUERY) { 23412393Syz155240 sport = ni.nai_sport; 23422393Syz155240 dport = 0; 23432393Syz155240 } 23442393Syz155240 23452958Sdr146992 /* 23462958Sdr146992 * nat_sumd[0] stores adjustment value including both IP address and 23472958Sdr146992 * port number changes. nat_sumd[1] stores adjustment value only for 23482958Sdr146992 * IP address changes, to be used for pseudo header adjustment, in 23492958Sdr146992 * case hardware partial checksum offload is offered. 23502958Sdr146992 */ 23512393Syz155240 CALC_SUMD(ni.nai_sum1, ni.nai_sum2, sumd); 23522393Syz155240 nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 23532958Sdr146992 #if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) 23542958Sdr146992 if (flags & IPN_TCPUDP) { 23552958Sdr146992 ni.nai_sum1 = LONG_SUM(in.s_addr); 23562393Syz155240 if (direction == NAT_OUTBOUND) 23572958Sdr146992 ni.nai_sum2 = LONG_SUM(ntohl(fin->fin_saddr)); 23582393Syz155240 else 23592958Sdr146992 ni.nai_sum2 = LONG_SUM(ntohl(fin->fin_daddr)); 23602958Sdr146992 23612958Sdr146992 CALC_SUMD(ni.nai_sum1, ni.nai_sum2, sumd); 23622958Sdr146992 nat->nat_sumd[1] = (sumd & 0xffff) + (sumd >> 16); 23632393Syz155240 } else 23642393Syz155240 #endif 23652393Syz155240 nat->nat_sumd[1] = nat->nat_sumd[0]; 23662393Syz155240 23672393Syz155240 if ((flags & IPN_TCPUDPICMP) && ((sport != port) || (dport != nport))) { 23682393Syz155240 if (direction == NAT_OUTBOUND) 23692393Syz155240 ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr)); 23702393Syz155240 else 23712393Syz155240 ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_daddr)); 23722393Syz155240 23732393Syz155240 ni.nai_sum2 = LONG_SUM(in.s_addr); 23742393Syz155240 23752393Syz155240 CALC_SUMD(ni.nai_sum1, ni.nai_sum2, sumd); 23762393Syz155240 nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); 23772393Syz155240 } else { 23782393Syz155240 nat->nat_ipsumd = nat->nat_sumd[0]; 23792393Syz155240 if (!(flags & IPN_TCPUDPICMP)) { 23802393Syz155240 nat->nat_sumd[0] = 0; 23812393Syz155240 nat->nat_sumd[1] = 0; 23822393Syz155240 } 23832393Syz155240 } 23842393Syz155240 23852393Syz155240 if (nat_finalise(fin, nat, &ni, tcp, natsave, direction) == -1) { 23862393Syz155240 goto badnat; 23872393Syz155240 } 23882393Syz155240 if (flags & SI_WILDP) 23893448Sdh155122 ifs->ifs_nat_stats.ns_wilds++; 23902393Syz155240 goto done; 23912393Syz155240 badnat: 23923448Sdh155122 ifs->ifs_nat_stats.ns_badnat++; 23932393Syz155240 if ((hm = nat->nat_hm) != NULL) 23942393Syz155240 nat_hostmapdel(hm); 23952393Syz155240 KFREE(nat); 23962393Syz155240 nat = NULL; 23972393Syz155240 done: 23982393Syz155240 if ((flags & NAT_SLAVE) == 0) { 23993448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_nat_new); 24002393Syz155240 } 24012393Syz155240 return nat; 24022393Syz155240 } 24032393Syz155240 24042393Syz155240 24052393Syz155240 /* ------------------------------------------------------------------------ */ 24062393Syz155240 /* Function: nat_finalise */ 24072393Syz155240 /* Returns: int - 0 == sucess, -1 == failure */ 24082393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 24092393Syz155240 /* nat(I) - pointer to NAT entry */ 24102393Syz155240 /* ni(I) - pointer to structure with misc. information needed */ 24112393Syz155240 /* to create new NAT entry. */ 24122393Syz155240 /* Write Lock: ipf_nat */ 24132393Syz155240 /* */ 24142393Syz155240 /* This is the tail end of constructing a new NAT entry and is the same */ 24152393Syz155240 /* for both IPv4 and IPv6. */ 24162393Syz155240 /* ------------------------------------------------------------------------ */ 24172393Syz155240 /*ARGSUSED*/ 24182393Syz155240 static INLINE int nat_finalise(fin, nat, ni, tcp, natsave, direction) 24192393Syz155240 fr_info_t *fin; 24202393Syz155240 nat_t *nat; 24212393Syz155240 natinfo_t *ni; 24222393Syz155240 tcphdr_t *tcp; 24232393Syz155240 nat_t **natsave; 24242393Syz155240 int direction; 24252393Syz155240 { 24262393Syz155240 frentry_t *fr; 24272393Syz155240 ipnat_t *np; 24283448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 24292393Syz155240 24302393Syz155240 np = ni->nai_np; 24312393Syz155240 24322958Sdr146992 COPYIFNAME(fin->fin_ifp, nat->nat_ifnames[0], fin->fin_v); 24332958Sdr146992 24342393Syz155240 #ifdef IPFILTER_SYNC 24352393Syz155240 if ((nat->nat_flags & SI_CLONE) == 0) 24362393Syz155240 nat->nat_sync = ipfsync_new(SMC_NAT, fin, nat); 24372393Syz155240 #endif 24382393Syz155240 24392393Syz155240 nat->nat_me = natsave; 24402393Syz155240 nat->nat_dir = direction; 24412508Syz155240 nat->nat_ifps[0] = np->in_ifps[0]; 24422508Syz155240 nat->nat_ifps[1] = np->in_ifps[1]; 24432393Syz155240 nat->nat_ptr = np; 24442393Syz155240 nat->nat_p = fin->fin_p; 24452393Syz155240 nat->nat_mssclamp = np->in_mssclamp; 24462393Syz155240 fr = fin->fin_fr; 24472393Syz155240 nat->nat_fr = fr; 24482393Syz155240 24492393Syz155240 if ((np->in_apr != NULL) && ((ni->nai_flags & NAT_SLAVE) == 0)) 24502393Syz155240 if (appr_new(fin, nat) == -1) 24512393Syz155240 return -1; 24522393Syz155240 24533448Sdh155122 if (nat_insert(nat, fin->fin_rev, ifs) == 0) { 24543448Sdh155122 if (ifs->ifs_nat_logging) 24553448Sdh155122 nat_log(nat, (u_int)np->in_redir, ifs); 24562393Syz155240 np->in_use++; 24572393Syz155240 if (fr != NULL) { 24582393Syz155240 MUTEX_ENTER(&fr->fr_lock); 24592393Syz155240 fr->fr_ref++; 24602393Syz155240 MUTEX_EXIT(&fr->fr_lock); 24612393Syz155240 } 24622393Syz155240 return 0; 24632393Syz155240 } 24642393Syz155240 24652393Syz155240 /* 24662393Syz155240 * nat_insert failed, so cleanup time... 24672393Syz155240 */ 24682393Syz155240 return -1; 24692393Syz155240 } 24702393Syz155240 24712393Syz155240 24722393Syz155240 /* ------------------------------------------------------------------------ */ 24732393Syz155240 /* Function: nat_insert */ 24742393Syz155240 /* Returns: int - 0 == sucess, -1 == failure */ 24752393Syz155240 /* Parameters: nat(I) - pointer to NAT structure */ 24762393Syz155240 /* rev(I) - flag indicating forward/reverse direction of packet */ 24772393Syz155240 /* Write Lock: ipf_nat */ 24782393Syz155240 /* */ 24792393Syz155240 /* Insert a NAT entry into the hash tables for searching and add it to the */ 24802393Syz155240 /* list of active NAT entries. Adjust global counters when complete. */ 24812393Syz155240 /* ------------------------------------------------------------------------ */ 24823448Sdh155122 int nat_insert(nat, rev, ifs) 24832393Syz155240 nat_t *nat; 24842393Syz155240 int rev; 24853448Sdh155122 ipf_stack_t *ifs; 24862393Syz155240 { 24872393Syz155240 u_int hv1, hv2; 24882393Syz155240 nat_t **natp; 24892393Syz155240 24902393Syz155240 /* 24912393Syz155240 * Try and return an error as early as possible, so calculate the hash 24922393Syz155240 * entry numbers first and then proceed. 24932393Syz155240 */ 24942393Syz155240 if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { 24952393Syz155240 hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 24962393Syz155240 0xffffffff); 24972393Syz155240 hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1 + nat->nat_oport, 24983448Sdh155122 ifs->ifs_ipf_nattable_sz); 24992393Syz155240 hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, 25002393Syz155240 0xffffffff); 25012393Syz155240 hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2 + nat->nat_oport, 25023448Sdh155122 ifs->ifs_ipf_nattable_sz); 25032393Syz155240 } else { 25042393Syz155240 hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, 0, 0xffffffff); 25053448Sdh155122 hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1, 25063448Sdh155122 ifs->ifs_ipf_nattable_sz); 25072393Syz155240 hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, 0, 0xffffffff); 25083448Sdh155122 hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2, 25093448Sdh155122 ifs->ifs_ipf_nattable_sz); 25102393Syz155240 } 25112393Syz155240 25123448Sdh155122 if (ifs->ifs_nat_stats.ns_bucketlen[0][hv1] >= ifs->ifs_fr_nat_maxbucket || 25133448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[1][hv2] >= ifs->ifs_fr_nat_maxbucket) { 25142393Syz155240 return -1; 25152393Syz155240 } 25162393Syz155240 25172393Syz155240 nat->nat_hv[0] = hv1; 25182393Syz155240 nat->nat_hv[1] = hv2; 25192393Syz155240 25202393Syz155240 MUTEX_INIT(&nat->nat_lock, "nat entry lock"); 25212393Syz155240 25222393Syz155240 nat->nat_rev = rev; 25232393Syz155240 nat->nat_ref = 1; 25242393Syz155240 nat->nat_bytes[0] = 0; 25252393Syz155240 nat->nat_pkts[0] = 0; 25262393Syz155240 nat->nat_bytes[1] = 0; 25272393Syz155240 nat->nat_pkts[1] = 0; 25282393Syz155240 25292393Syz155240 nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; 25303448Sdh155122 nat->nat_ifps[0] = fr_resolvenic(nat->nat_ifnames[0], 4, ifs); 25312393Syz155240 25322393Syz155240 if (nat->nat_ifnames[1][0] !='\0') { 25332393Syz155240 nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 25343448Sdh155122 nat->nat_ifps[1] = fr_resolvenic(nat->nat_ifnames[1], 4, ifs); 25352393Syz155240 } else { 25362393Syz155240 (void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0], 25372393Syz155240 LIFNAMSIZ); 25382393Syz155240 nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 25392393Syz155240 nat->nat_ifps[1] = nat->nat_ifps[0]; 25402393Syz155240 } 25412393Syz155240 25423448Sdh155122 nat->nat_next = ifs->ifs_nat_instances; 25433448Sdh155122 nat->nat_pnext = &ifs->ifs_nat_instances; 25443448Sdh155122 if (ifs->ifs_nat_instances) 25453448Sdh155122 ifs->ifs_nat_instances->nat_pnext = &nat->nat_next; 25463448Sdh155122 ifs->ifs_nat_instances = nat; 25473448Sdh155122 25483448Sdh155122 natp = &ifs->ifs_nat_table[0][hv1]; 25492393Syz155240 if (*natp) 25502393Syz155240 (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 25512393Syz155240 nat->nat_phnext[0] = natp; 25522393Syz155240 nat->nat_hnext[0] = *natp; 25532393Syz155240 *natp = nat; 25543448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[0][hv1]++; 25553448Sdh155122 25563448Sdh155122 natp = &ifs->ifs_nat_table[1][hv2]; 25572393Syz155240 if (*natp) 25582393Syz155240 (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 25592393Syz155240 nat->nat_phnext[1] = natp; 25602393Syz155240 nat->nat_hnext[1] = *natp; 25612393Syz155240 *natp = nat; 25623448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[1][hv2]++; 25633448Sdh155122 25643448Sdh155122 fr_setnatqueue(nat, rev, ifs); 25653448Sdh155122 25663448Sdh155122 ifs->ifs_nat_stats.ns_added++; 25673448Sdh155122 ifs->ifs_nat_stats.ns_inuse++; 25682393Syz155240 return 0; 25692393Syz155240 } 25702393Syz155240 25712393Syz155240 25722393Syz155240 /* ------------------------------------------------------------------------ */ 25732393Syz155240 /* Function: nat_icmperrorlookup */ 25742393Syz155240 /* Returns: nat_t* - point to matching NAT structure */ 25752393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 25762393Syz155240 /* dir(I) - direction of packet (in/out) */ 25772393Syz155240 /* */ 25782393Syz155240 /* Check if the ICMP error message is related to an existing TCP, UDP or */ 25792393Syz155240 /* ICMP query nat entry. It is assumed that the packet is already of the */ 25802393Syz155240 /* the required length. */ 25812393Syz155240 /* ------------------------------------------------------------------------ */ 25822393Syz155240 nat_t *nat_icmperrorlookup(fin, dir) 25832393Syz155240 fr_info_t *fin; 25842393Syz155240 int dir; 25852393Syz155240 { 25862393Syz155240 int flags = 0, minlen; 25872393Syz155240 icmphdr_t *orgicmp; 25882393Syz155240 tcphdr_t *tcp = NULL; 25892393Syz155240 u_short data[2]; 25902393Syz155240 nat_t *nat; 25912393Syz155240 ip_t *oip; 25922393Syz155240 u_int p; 25932393Syz155240 25942393Syz155240 /* 25952393Syz155240 * Does it at least have the return (basic) IP header ? 25962393Syz155240 * Only a basic IP header (no options) should be with an ICMP error 25972393Syz155240 * header. Also, if it's not an error type, then return. 25982393Syz155240 */ 25992393Syz155240 if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) 26002393Syz155240 return NULL; 26012393Syz155240 26022393Syz155240 /* 26032393Syz155240 * Check packet size 26042393Syz155240 */ 26052393Syz155240 oip = (ip_t *)((char *)fin->fin_dp + 8); 26062393Syz155240 minlen = IP_HL(oip) << 2; 26072393Syz155240 if ((minlen < sizeof(ip_t)) || 26082393Syz155240 (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) 26092393Syz155240 return NULL; 26102393Syz155240 /* 26112393Syz155240 * Is the buffer big enough for all of it ? It's the size of the IP 26122393Syz155240 * header claimed in the encapsulated part which is of concern. It 26132393Syz155240 * may be too big to be in this buffer but not so big that it's 26142393Syz155240 * outside the ICMP packet, leading to TCP deref's causing problems. 26152393Syz155240 * This is possible because we don't know how big oip_hl is when we 26162393Syz155240 * do the pullup early in fr_check() and thus can't gaurantee it is 26172393Syz155240 * all here now. 26182393Syz155240 */ 26192393Syz155240 #ifdef _KERNEL 26202393Syz155240 { 26212393Syz155240 mb_t *m; 26222393Syz155240 26232393Syz155240 m = fin->fin_m; 26242393Syz155240 # if defined(MENTAT) 26252393Syz155240 if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr) 26262393Syz155240 return NULL; 26272393Syz155240 # else 26282393Syz155240 if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > 26292393Syz155240 (char *)fin->fin_ip + M_LEN(m)) 26302393Syz155240 return NULL; 26312393Syz155240 # endif 26322393Syz155240 } 26332393Syz155240 #endif 26342393Syz155240 26352393Syz155240 if (fin->fin_daddr != oip->ip_src.s_addr) 26362393Syz155240 return NULL; 26372393Syz155240 26382393Syz155240 p = oip->ip_p; 26392393Syz155240 if (p == IPPROTO_TCP) 26402393Syz155240 flags = IPN_TCP; 26412393Syz155240 else if (p == IPPROTO_UDP) 26422393Syz155240 flags = IPN_UDP; 26432393Syz155240 else if (p == IPPROTO_ICMP) { 26442393Syz155240 orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 26452393Syz155240 26462393Syz155240 /* see if this is related to an ICMP query */ 26472393Syz155240 if (nat_icmpquerytype4(orgicmp->icmp_type)) { 26482393Syz155240 data[0] = fin->fin_data[0]; 26492393Syz155240 data[1] = fin->fin_data[1]; 26502393Syz155240 fin->fin_data[0] = 0; 26512393Syz155240 fin->fin_data[1] = orgicmp->icmp_id; 26522393Syz155240 26532393Syz155240 flags = IPN_ICMPERR|IPN_ICMPQUERY; 26542393Syz155240 /* 26552393Syz155240 * NOTE : dir refers to the direction of the original 26562393Syz155240 * ip packet. By definition the icmp error 26572393Syz155240 * message flows in the opposite direction. 26582393Syz155240 */ 26592393Syz155240 if (dir == NAT_INBOUND) 26602393Syz155240 nat = nat_inlookup(fin, flags, p, oip->ip_dst, 26612393Syz155240 oip->ip_src); 26622393Syz155240 else 26632393Syz155240 nat = nat_outlookup(fin, flags, p, oip->ip_dst, 26642393Syz155240 oip->ip_src); 26652393Syz155240 fin->fin_data[0] = data[0]; 26662393Syz155240 fin->fin_data[1] = data[1]; 26672393Syz155240 return nat; 26682393Syz155240 } 26692393Syz155240 } 26702393Syz155240 26712393Syz155240 if (flags & IPN_TCPUDP) { 26722393Syz155240 minlen += 8; /* + 64bits of data to get ports */ 26732393Syz155240 if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) 26742393Syz155240 return NULL; 26752393Syz155240 26762393Syz155240 data[0] = fin->fin_data[0]; 26772393Syz155240 data[1] = fin->fin_data[1]; 26782393Syz155240 tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 26792393Syz155240 fin->fin_data[0] = ntohs(tcp->th_dport); 26802393Syz155240 fin->fin_data[1] = ntohs(tcp->th_sport); 26812393Syz155240 26822393Syz155240 if (dir == NAT_INBOUND) { 26832393Syz155240 nat = nat_inlookup(fin, flags, p, oip->ip_dst, 26842393Syz155240 oip->ip_src); 26852393Syz155240 } else { 26862393Syz155240 nat = nat_outlookup(fin, flags, p, oip->ip_dst, 26872393Syz155240 oip->ip_src); 26882393Syz155240 } 26892393Syz155240 fin->fin_data[0] = data[0]; 26902393Syz155240 fin->fin_data[1] = data[1]; 26912393Syz155240 return nat; 26922393Syz155240 } 26932393Syz155240 if (dir == NAT_INBOUND) 26942393Syz155240 return nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src); 26952393Syz155240 else 26962393Syz155240 return nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src); 26972393Syz155240 } 26982393Syz155240 26992393Syz155240 27002393Syz155240 /* ------------------------------------------------------------------------ */ 27012393Syz155240 /* Function: nat_icmperror */ 27022393Syz155240 /* Returns: nat_t* - point to matching NAT structure */ 27032393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 27042393Syz155240 /* nflags(I) - NAT flags for this packet */ 27052393Syz155240 /* dir(I) - direction of packet (in/out) */ 27062393Syz155240 /* */ 27072393Syz155240 /* Fix up an ICMP packet which is an error message for an existing NAT */ 27082393Syz155240 /* session. This will correct both packet header data and checksums. */ 27092393Syz155240 /* */ 27102393Syz155240 /* This should *ONLY* be used for incoming ICMP error packets to make sure */ 27112393Syz155240 /* a NAT'd ICMP packet gets correctly recognised. */ 27122393Syz155240 /* ------------------------------------------------------------------------ */ 27132393Syz155240 nat_t *nat_icmperror(fin, nflags, dir) 27142393Syz155240 fr_info_t *fin; 27152393Syz155240 u_int *nflags; 27162393Syz155240 int dir; 27172393Syz155240 { 27182693Sjojemann u_32_t sum1, sum2, sumd, psum1, psum2, psumd, sumd2; 27192393Syz155240 struct in_addr in; 27202693Sjojemann icmphdr_t *icmp, *orgicmp; 27212693Sjojemann int dlen; 27222693Sjojemann udphdr_t *udp; 27232393Syz155240 tcphdr_t *tcp; 27242393Syz155240 nat_t *nat; 27252393Syz155240 ip_t *oip; 27262393Syz155240 if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) 27272393Syz155240 return NULL; 27282693Sjojemann 27292393Syz155240 /* 27302693Sjojemann * nat_icmperrorlookup() looks up nat entry associated with the 27312693Sjojemann * offending IP packet and returns pointer to the entry, or NULL 27322693Sjojemann * if packet wasn't natted or for `defective' packets. 27332393Syz155240 */ 27342693Sjojemann 27352393Syz155240 if ((fin->fin_v != 4) || !(nat = nat_icmperrorlookup(fin, dir))) 27362393Syz155240 return NULL; 27372393Syz155240 27382393Syz155240 sumd2 = 0; 27392393Syz155240 *nflags = IPN_ICMPERR; 27402393Syz155240 icmp = fin->fin_dp; 27412393Syz155240 oip = (ip_t *)&icmp->icmp_ip; 27422693Sjojemann udp = (udphdr_t *)((((char *)oip) + (IP_HL(oip) << 2))); 27432693Sjojemann tcp = (tcphdr_t *)udp; 27442693Sjojemann dlen = fin->fin_plen - ((char *)udp - (char *)fin->fin_ip); 27452393Syz155240 27462393Syz155240 /* 27472393Syz155240 * Need to adjust ICMP header to include the real IP#'s and 27482693Sjojemann * port #'s. There are three steps required. 27492693Sjojemann * 27502393Syz155240 * Step 1 27512693Sjojemann * Fix the IP addresses in the offending IP packet and update 27522693Sjojemann * ip header checksum to compensate for the change. 27532393Syz155240 * 27542693Sjojemann * No update needed here for icmp_cksum because the ICMP checksum 27552693Sjojemann * is calculated over the complete ICMP packet, which includes the 27562693Sjojemann * changed oip IP addresses and oip->ip_sum. These two changes 27572693Sjojemann * cancel each other out (if the delta for the IP address is x, 27582693Sjojemann * then the delta for ip_sum is minus x). 27592393Syz155240 */ 27602393Syz155240 27612393Syz155240 if (oip->ip_dst.s_addr == nat->nat_oip.s_addr) { 27622393Syz155240 sum1 = LONG_SUM(ntohl(oip->ip_src.s_addr)); 27632393Syz155240 in = nat->nat_inip; 27642393Syz155240 oip->ip_src = in; 27652393Syz155240 } else { 27662393Syz155240 sum1 = LONG_SUM(ntohl(oip->ip_dst.s_addr)); 27672393Syz155240 in = nat->nat_outip; 27682393Syz155240 oip->ip_dst = in; 27692393Syz155240 } 27702393Syz155240 27712393Syz155240 sum2 = LONG_SUM(ntohl(in.s_addr)); 27722393Syz155240 CALC_SUMD(sum1, sum2, sumd); 27732393Syz155240 fix_datacksum(&oip->ip_sum, sumd); 27742393Syz155240 27752393Syz155240 /* 27762693Sjojemann * Step 2 27772693Sjojemann * Perform other adjustments based on protocol of offending packet. 27782393Syz155240 */ 27792693Sjojemann 27802693Sjojemann switch (oip->ip_p) { 27812693Sjojemann case IPPROTO_TCP : 27822693Sjojemann case IPPROTO_UDP : 27832393Syz155240 27842393Syz155240 /* 27852693Sjojemann * For offending TCP/UDP IP packets, translate the ports 27862693Sjojemann * based on the NAT specification. 27872693Sjojemann * 27882693Sjojemann * Advance notice : Now it becomes complicated :-) 27892693Sjojemann * 27902693Sjojemann * Since the port and IP addresse fields are both part 27912693Sjojemann * of the TCP/UDP checksum of the offending IP packet, 27922693Sjojemann * we need to adjust that checksum as well. 27932693Sjojemann * 27942693Sjojemann * To further complicate things, the TCP/UDP checksum 27952693Sjojemann * may not be present. We must check to see if the 27962693Sjojemann * length of the data portion is big enough to hold 27972693Sjojemann * the checksum. In the UDP case, a test to determine 27982693Sjojemann * if the checksum is even set is also required. 27992693Sjojemann * 28002693Sjojemann * Any changes to an IP address, port or checksum within 28012693Sjojemann * the ICMP packet requires a change to icmp_cksum. 28022693Sjojemann * 28032693Sjojemann * Be extremely careful here ... The change is dependent 28042693Sjojemann * upon whether or not the TCP/UPD checksum is present. 28052693Sjojemann * 28062693Sjojemann * If TCP/UPD checksum is present, the icmp_cksum must 28072693Sjojemann * compensate for checksum modification resulting from 28082693Sjojemann * IP address change only. Port change and resulting 28092693Sjojemann * data checksum adjustments cancel each other out. 28102693Sjojemann * 28112693Sjojemann * If TCP/UDP checksum is not present, icmp_cksum must 28122693Sjojemann * compensate for port change only. The IP address 28132693Sjojemann * change does not modify anything else in this case. 28142693Sjojemann */ 28152693Sjojemann 28162693Sjojemann psum1 = 0; 28172693Sjojemann psum2 = 0; 28182693Sjojemann psumd = 0; 28192693Sjojemann 28202693Sjojemann if ((tcp->th_dport == nat->nat_oport) && 28212693Sjojemann (tcp->th_sport != nat->nat_inport)) { 28222393Syz155240 28232393Syz155240 /* 28242693Sjojemann * Translate the source port. 28252693Sjojemann */ 28262693Sjojemann 28272693Sjojemann psum1 = ntohs(tcp->th_sport); 28282693Sjojemann psum2 = ntohs(nat->nat_inport); 28292693Sjojemann tcp->th_sport = nat->nat_inport; 28302693Sjojemann 28312693Sjojemann } else if ((tcp->th_sport == nat->nat_oport) && 28322693Sjojemann (tcp->th_dport != nat->nat_outport)) { 28332693Sjojemann 28342693Sjojemann /* 28352693Sjojemann * Translate the destination port. 28362393Syz155240 */ 28372693Sjojemann 28382693Sjojemann psum1 = ntohs(tcp->th_dport); 28392693Sjojemann psum2 = ntohs(nat->nat_outport); 28402693Sjojemann tcp->th_dport = nat->nat_outport; 28412693Sjojemann } 28422693Sjojemann 28432693Sjojemann if ((oip->ip_p == IPPROTO_TCP) && (dlen >= 18)) { 28442693Sjojemann 28452693Sjojemann /* 28462693Sjojemann * TCP checksum present. 28472693Sjojemann * 28482693Sjojemann * Adjust data checksum and icmp checksum to 28492693Sjojemann * compensate for any IP address change. 28502693Sjojemann */ 28512693Sjojemann 28522693Sjojemann sum1 = ntohs(tcp->th_sum); 28532693Sjojemann fix_datacksum(&tcp->th_sum, sumd); 28542693Sjojemann sum2 = ntohs(tcp->th_sum); 28552693Sjojemann sumd2 = sumd << 1; 28562393Syz155240 CALC_SUMD(sum1, sum2, sumd); 28572393Syz155240 sumd2 += sumd; 28582693Sjojemann 28592693Sjojemann /* 28602693Sjojemann * Also make data checksum adjustment to 28612693Sjojemann * compensate for any port change. 28622693Sjojemann */ 28632693Sjojemann 28642693Sjojemann if (psum1 != psum2) { 28652693Sjojemann CALC_SUMD(psum1, psum2, psumd); 28662693Sjojemann fix_datacksum(&tcp->th_sum, psumd); 28672393Syz155240 } 28682693Sjojemann 28692693Sjojemann } else if ((oip->ip_p == IPPROTO_UDP) && 28702693Sjojemann (dlen >= 8) && (udp->uh_sum != 0)) { 28712693Sjojemann 28722693Sjojemann /* 28732693Sjojemann * The UDP checksum is present and set. 28742693Sjojemann * 28752693Sjojemann * Adjust data checksum and icmp checksum to 28762693Sjojemann * compensate for any IP address change. 28772693Sjojemann */ 28782693Sjojemann 28792693Sjojemann sum1 = ntohs(udp->uh_sum); 28802693Sjojemann fix_datacksum(&udp->uh_sum, sumd); 28812693Sjojemann sum2 = ntohs(udp->uh_sum); 28822693Sjojemann sumd2 = sumd << 1; 28832693Sjojemann CALC_SUMD(sum1, sum2, sumd); 28842393Syz155240 sumd2 += sumd; 28852393Syz155240 28862393Syz155240 /* 28872693Sjojemann * Also make data checksum adjustment to 28882693Sjojemann * compensate for any port change. 28892393Syz155240 */ 28902693Sjojemann 28912693Sjojemann if (psum1 != psum2) { 28922693Sjojemann CALC_SUMD(psum1, psum2, psumd); 28932693Sjojemann fix_datacksum(&udp->uh_sum, psumd); 28942693Sjojemann } 28952693Sjojemann 28962693Sjojemann } else { 28972693Sjojemann 28982693Sjojemann /* 28992693Sjojemann * Data checksum was not present. 29002693Sjojemann * 29012693Sjojemann * Compensate for any port change. 29022693Sjojemann */ 29032693Sjojemann 29042693Sjojemann CALC_SUMD(psum2, psum1, psumd); 29052693Sjojemann sumd2 += psumd; 29062393Syz155240 } 29072693Sjojemann break; 29082693Sjojemann 29092693Sjojemann case IPPROTO_ICMP : 29102693Sjojemann 29112693Sjojemann orgicmp = (icmphdr_t *)udp; 29122693Sjojemann 29132693Sjojemann if ((nat->nat_dir == NAT_OUTBOUND) && 29142693Sjojemann (orgicmp->icmp_id != nat->nat_inport) && 29152693Sjojemann (dlen >= 8)) { 29162393Syz155240 29172393Syz155240 /* 29182393Syz155240 * Fix ICMP checksum (of the offening ICMP 29192393Syz155240 * query packet) to compensate the change 29202393Syz155240 * in the ICMP id of the offending ICMP 29212393Syz155240 * packet. 29222393Syz155240 * 29232393Syz155240 * Since you modify orgicmp->icmp_id with 29242393Syz155240 * a delta (say x) and you compensate that 29252393Syz155240 * in origicmp->icmp_cksum with a delta 29262393Syz155240 * minus x, you don't have to adjust the 29272393Syz155240 * overall icmp->icmp_cksum 29282393Syz155240 */ 29292693Sjojemann 29302393Syz155240 sum1 = ntohs(orgicmp->icmp_id); 29312393Syz155240 sum2 = ntohs(nat->nat_inport); 29322393Syz155240 CALC_SUMD(sum1, sum2, sumd); 29332393Syz155240 orgicmp->icmp_id = nat->nat_inport; 29342393Syz155240 fix_datacksum(&orgicmp->icmp_cksum, sumd); 29352693Sjojemann 29362693Sjojemann } /* nat_dir can't be NAT_INBOUND for icmp queries */ 29372693Sjojemann 29382693Sjojemann break; 29392693Sjojemann 29402693Sjojemann default : 29412693Sjojemann 29422693Sjojemann break; 29432693Sjojemann 29442693Sjojemann } /* switch (oip->ip_p) */ 29452693Sjojemann 29462693Sjojemann /* 29472693Sjojemann * Step 3 29482693Sjojemann * Make the adjustments to icmp checksum. 29492693Sjojemann */ 29502693Sjojemann 29512693Sjojemann if (sumd2 != 0) { 29522693Sjojemann sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 29532693Sjojemann sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 29542958Sdr146992 fix_incksum(&icmp->icmp_cksum, sumd2); 29552393Syz155240 } 29562393Syz155240 return nat; 29572393Syz155240 } 29582393Syz155240 29592393Syz155240 29602393Syz155240 /* 29612393Syz155240 * NB: these lookups don't lock access to the list, it assumed that it has 29622393Syz155240 * already been done! 29632393Syz155240 */ 29642393Syz155240 29652393Syz155240 /* ------------------------------------------------------------------------ */ 29662393Syz155240 /* Function: nat_inlookup */ 29672393Syz155240 /* Returns: nat_t* - NULL == no match, */ 29682393Syz155240 /* else pointer to matching NAT entry */ 29692393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 29702393Syz155240 /* flags(I) - NAT flags for this packet */ 29712393Syz155240 /* p(I) - protocol for this packet */ 29722393Syz155240 /* src(I) - source IP address */ 29732393Syz155240 /* mapdst(I) - destination IP address */ 29742393Syz155240 /* */ 29752393Syz155240 /* Lookup a nat entry based on the mapped destination ip address/port and */ 29762393Syz155240 /* real source address/port. We use this lookup when receiving a packet, */ 29772393Syz155240 /* we're looking for a table entry, based on the destination address. */ 29782393Syz155240 /* */ 29792393Syz155240 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 29802393Syz155240 /* */ 29812393Syz155240 /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */ 29822393Syz155240 /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 29832393Syz155240 /* */ 29842393Syz155240 /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 29852393Syz155240 /* the packet is of said protocol */ 29862393Syz155240 /* ------------------------------------------------------------------------ */ 29872393Syz155240 nat_t *nat_inlookup(fin, flags, p, src, mapdst) 29882393Syz155240 fr_info_t *fin; 29892393Syz155240 u_int flags, p; 29902393Syz155240 struct in_addr src , mapdst; 29912393Syz155240 { 29922393Syz155240 u_short sport, dport; 29932393Syz155240 ipnat_t *ipn; 29942393Syz155240 u_int sflags; 29952393Syz155240 nat_t *nat; 29962393Syz155240 int nflags; 29972393Syz155240 u_32_t dst; 29982393Syz155240 void *ifp; 29992393Syz155240 u_int hv; 30003448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 30012393Syz155240 30022393Syz155240 if (fin != NULL) 30032393Syz155240 ifp = fin->fin_ifp; 30042393Syz155240 else 30052393Syz155240 ifp = NULL; 30062393Syz155240 sport = 0; 30072393Syz155240 dport = 0; 30082393Syz155240 dst = mapdst.s_addr; 30092393Syz155240 sflags = flags & NAT_TCPUDPICMP; 30102393Syz155240 30112393Syz155240 switch (p) 30122393Syz155240 { 30132393Syz155240 case IPPROTO_TCP : 30142393Syz155240 case IPPROTO_UDP : 30152393Syz155240 sport = htons(fin->fin_data[0]); 30162393Syz155240 dport = htons(fin->fin_data[1]); 30172393Syz155240 break; 30182393Syz155240 case IPPROTO_ICMP : 30192393Syz155240 if (flags & IPN_ICMPERR) 30202393Syz155240 sport = fin->fin_data[1]; 30212393Syz155240 else 30222393Syz155240 dport = fin->fin_data[1]; 30232393Syz155240 break; 30242393Syz155240 default : 30252393Syz155240 break; 30262393Syz155240 } 30272393Syz155240 30282393Syz155240 30292393Syz155240 if ((flags & SI_WILDP) != 0) 30302393Syz155240 goto find_in_wild_ports; 30312393Syz155240 30322393Syz155240 hv = NAT_HASH_FN(dst, dport, 0xffffffff); 30333448Sdh155122 hv = NAT_HASH_FN(src.s_addr, hv + sport, ifs->ifs_ipf_nattable_sz); 30343448Sdh155122 nat = ifs->ifs_nat_table[1][hv]; 30352393Syz155240 for (; nat; nat = nat->nat_hnext[1]) { 30362508Syz155240 if (nat->nat_ifps[0] != NULL) { 30372508Syz155240 if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 30382508Syz155240 continue; 30392508Syz155240 } else if (ifp != NULL) 30402508Syz155240 nat->nat_ifps[0] = ifp; 30412508Syz155240 30422393Syz155240 nflags = nat->nat_flags; 30432393Syz155240 30442393Syz155240 if (nat->nat_oip.s_addr == src.s_addr && 30452393Syz155240 nat->nat_outip.s_addr == dst && 30462393Syz155240 (((p == 0) && 30472393Syz155240 (sflags == (nat->nat_flags & IPN_TCPUDPICMP))) 30482393Syz155240 || (p == nat->nat_p))) { 30492393Syz155240 switch (p) 30502393Syz155240 { 30512393Syz155240 #if 0 30522393Syz155240 case IPPROTO_GRE : 30532393Syz155240 if (nat->nat_call[1] != fin->fin_data[0]) 30542393Syz155240 continue; 30552393Syz155240 break; 30562393Syz155240 #endif 30572393Syz155240 case IPPROTO_ICMP : 30582393Syz155240 if ((flags & IPN_ICMPERR) != 0) { 30592393Syz155240 if (nat->nat_outport != sport) 30602393Syz155240 continue; 30612393Syz155240 } else { 30622393Syz155240 if (nat->nat_outport != dport) 30632393Syz155240 continue; 30642393Syz155240 } 30652393Syz155240 break; 30662393Syz155240 case IPPROTO_TCP : 30672393Syz155240 case IPPROTO_UDP : 30682393Syz155240 if (nat->nat_oport != sport) 30692393Syz155240 continue; 30702393Syz155240 if (nat->nat_outport != dport) 30712393Syz155240 continue; 30722393Syz155240 break; 30732393Syz155240 default : 30742393Syz155240 break; 30752393Syz155240 } 30762393Syz155240 30772393Syz155240 ipn = nat->nat_ptr; 30782393Syz155240 if ((ipn != NULL) && (nat->nat_aps != NULL)) 30792393Syz155240 if (appr_match(fin, nat) != 0) 30802393Syz155240 continue; 30812393Syz155240 return nat; 30822393Syz155240 } 30832393Syz155240 } 30842393Syz155240 30852393Syz155240 /* 30862393Syz155240 * So if we didn't find it but there are wildcard members in the hash 30872393Syz155240 * table, go back and look for them. We do this search and update here 30882393Syz155240 * because it is modifying the NAT table and we want to do this only 30892393Syz155240 * for the first packet that matches. The exception, of course, is 30902393Syz155240 * for "dummy" (FI_IGNORE) lookups. 30912393Syz155240 */ 30922393Syz155240 find_in_wild_ports: 30932393Syz155240 if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) 30942393Syz155240 return NULL; 30953448Sdh155122 if (ifs->ifs_nat_stats.ns_wilds == 0) 30962393Syz155240 return NULL; 30972393Syz155240 30983448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 30992393Syz155240 31002393Syz155240 hv = NAT_HASH_FN(dst, 0, 0xffffffff); 31013448Sdh155122 hv = NAT_HASH_FN(src.s_addr, hv, ifs->ifs_ipf_nattable_sz); 31023448Sdh155122 31033448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 31043448Sdh155122 31053448Sdh155122 nat = ifs->ifs_nat_table[1][hv]; 31062393Syz155240 for (; nat; nat = nat->nat_hnext[1]) { 31072508Syz155240 if (nat->nat_ifps[0] != NULL) { 31082508Syz155240 if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 31092508Syz155240 continue; 31102508Syz155240 } else if (ifp != NULL) 31112508Syz155240 nat->nat_ifps[0] = ifp; 31122393Syz155240 31132393Syz155240 if (nat->nat_p != fin->fin_p) 31142393Syz155240 continue; 31152393Syz155240 if (nat->nat_oip.s_addr != src.s_addr || 31162393Syz155240 nat->nat_outip.s_addr != dst) 31172393Syz155240 continue; 31182393Syz155240 31192393Syz155240 nflags = nat->nat_flags; 31202393Syz155240 if (!(nflags & (NAT_TCPUDP|SI_WILDP))) 31212393Syz155240 continue; 31222393Syz155240 31232393Syz155240 if (nat_wildok(nat, (int)sport, (int)dport, nflags, 31242393Syz155240 NAT_INBOUND) == 1) { 31252393Syz155240 if ((fin->fin_flx & FI_IGNORE) != 0) 31262393Syz155240 break; 31272393Syz155240 if ((nflags & SI_CLONE) != 0) { 31282393Syz155240 nat = fr_natclone(fin, nat); 31292393Syz155240 if (nat == NULL) 31302393Syz155240 break; 31312393Syz155240 } else { 31323448Sdh155122 MUTEX_ENTER(&ifs->ifs_ipf_nat_new); 31333448Sdh155122 ifs->ifs_nat_stats.ns_wilds--; 31343448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_nat_new); 31352393Syz155240 } 31362393Syz155240 nat->nat_oport = sport; 31372393Syz155240 nat->nat_outport = dport; 31382393Syz155240 nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 31393448Sdh155122 nat_tabmove(nat, ifs); 31402393Syz155240 break; 31412393Syz155240 } 31422393Syz155240 } 31432393Syz155240 31443448Sdh155122 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); 31452393Syz155240 31462393Syz155240 return nat; 31472393Syz155240 } 31482393Syz155240 31492393Syz155240 31502393Syz155240 /* ------------------------------------------------------------------------ */ 31512393Syz155240 /* Function: nat_tabmove */ 31522393Syz155240 /* Returns: Nil */ 31532393Syz155240 /* Parameters: nat(I) - pointer to NAT structure */ 31542393Syz155240 /* Write Lock: ipf_nat */ 31552393Syz155240 /* */ 31562393Syz155240 /* This function is only called for TCP/UDP NAT table entries where the */ 31572393Syz155240 /* original was placed in the table without hashing on the ports and we now */ 31582393Syz155240 /* want to include hashing on port numbers. */ 31592393Syz155240 /* ------------------------------------------------------------------------ */ 31603448Sdh155122 static void nat_tabmove(nat, ifs) 31612393Syz155240 nat_t *nat; 31623448Sdh155122 ipf_stack_t *ifs; 31632393Syz155240 { 31642393Syz155240 nat_t **natp; 31652393Syz155240 u_int hv; 31662393Syz155240 31672393Syz155240 if (nat->nat_flags & SI_CLONE) 31682393Syz155240 return; 31692393Syz155240 31702393Syz155240 /* 31712393Syz155240 * Remove the NAT entry from the old location 31722393Syz155240 */ 31732393Syz155240 if (nat->nat_hnext[0]) 31742393Syz155240 nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 31752393Syz155240 *nat->nat_phnext[0] = nat->nat_hnext[0]; 31763448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--; 31772393Syz155240 31782393Syz155240 if (nat->nat_hnext[1]) 31792393Syz155240 nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 31802393Syz155240 *nat->nat_phnext[1] = nat->nat_hnext[1]; 31813448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--; 31822393Syz155240 31832393Syz155240 /* 31842393Syz155240 * Add into the NAT table in the new position 31852393Syz155240 */ 31862393Syz155240 hv = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 0xffffffff); 31872393Syz155240 hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport, 31883448Sdh155122 ifs->ifs_ipf_nattable_sz); 31892393Syz155240 nat->nat_hv[0] = hv; 31903448Sdh155122 natp = &ifs->ifs_nat_table[0][hv]; 31912393Syz155240 if (*natp) 31922393Syz155240 (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 31932393Syz155240 nat->nat_phnext[0] = natp; 31942393Syz155240 nat->nat_hnext[0] = *natp; 31952393Syz155240 *natp = nat; 31963448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[0][hv]++; 31972393Syz155240 31982393Syz155240 hv = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, 0xffffffff); 31992393Syz155240 hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport, 32003448Sdh155122 ifs->ifs_ipf_nattable_sz); 32012393Syz155240 nat->nat_hv[1] = hv; 32023448Sdh155122 natp = &ifs->ifs_nat_table[1][hv]; 32032393Syz155240 if (*natp) 32042393Syz155240 (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 32052393Syz155240 nat->nat_phnext[1] = natp; 32062393Syz155240 nat->nat_hnext[1] = *natp; 32072393Syz155240 *natp = nat; 32083448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[1][hv]++; 32092393Syz155240 } 32102393Syz155240 32112393Syz155240 32122393Syz155240 /* ------------------------------------------------------------------------ */ 32132393Syz155240 /* Function: nat_outlookup */ 32142393Syz155240 /* Returns: nat_t* - NULL == no match, */ 32152393Syz155240 /* else pointer to matching NAT entry */ 32162393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 32172393Syz155240 /* flags(I) - NAT flags for this packet */ 32182393Syz155240 /* p(I) - protocol for this packet */ 32192393Syz155240 /* src(I) - source IP address */ 32202393Syz155240 /* dst(I) - destination IP address */ 32212393Syz155240 /* rw(I) - 1 == write lock on ipf_nat held, 0 == read lock. */ 32222393Syz155240 /* */ 32232393Syz155240 /* Lookup a nat entry based on the source 'real' ip address/port and */ 32242393Syz155240 /* destination address/port. We use this lookup when sending a packet out, */ 32252393Syz155240 /* we're looking for a table entry, based on the source address. */ 32262393Syz155240 /* */ 32272393Syz155240 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 32282393Syz155240 /* */ 32292393Syz155240 /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */ 32302393Syz155240 /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 32312393Syz155240 /* */ 32322393Syz155240 /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 32332393Syz155240 /* the packet is of said protocol */ 32342393Syz155240 /* ------------------------------------------------------------------------ */ 32352393Syz155240 nat_t *nat_outlookup(fin, flags, p, src, dst) 32362393Syz155240 fr_info_t *fin; 32372393Syz155240 u_int flags, p; 32382393Syz155240 struct in_addr src , dst; 32392393Syz155240 { 32402393Syz155240 u_short sport, dport; 32412393Syz155240 u_int sflags; 32422393Syz155240 ipnat_t *ipn; 32432393Syz155240 u_32_t srcip; 32442393Syz155240 nat_t *nat; 32452393Syz155240 int nflags; 32462393Syz155240 void *ifp; 32472393Syz155240 u_int hv; 32483448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 32492508Syz155240 32503894Sjojemann ifp = fin->fin_ifp; 32512508Syz155240 32522393Syz155240 srcip = src.s_addr; 32532393Syz155240 sflags = flags & IPN_TCPUDPICMP; 32542393Syz155240 sport = 0; 32552393Syz155240 dport = 0; 32562393Syz155240 32572393Syz155240 switch (p) 32582393Syz155240 { 32592393Syz155240 case IPPROTO_TCP : 32602393Syz155240 case IPPROTO_UDP : 32612393Syz155240 sport = htons(fin->fin_data[0]); 32622393Syz155240 dport = htons(fin->fin_data[1]); 32632393Syz155240 break; 32642393Syz155240 case IPPROTO_ICMP : 32652393Syz155240 if (flags & IPN_ICMPERR) 32662393Syz155240 sport = fin->fin_data[1]; 32672393Syz155240 else 32682393Syz155240 dport = fin->fin_data[1]; 32692393Syz155240 break; 32702393Syz155240 default : 32712393Syz155240 break; 32722393Syz155240 } 32732393Syz155240 32742393Syz155240 if ((flags & SI_WILDP) != 0) 32752393Syz155240 goto find_out_wild_ports; 32762393Syz155240 32772393Syz155240 hv = NAT_HASH_FN(srcip, sport, 0xffffffff); 32783448Sdh155122 hv = NAT_HASH_FN(dst.s_addr, hv + dport, ifs->ifs_ipf_nattable_sz); 32793448Sdh155122 nat = ifs->ifs_nat_table[0][hv]; 32802393Syz155240 for (; nat; nat = nat->nat_hnext[0]) { 32812508Syz155240 if (nat->nat_ifps[1] != NULL) { 32822508Syz155240 if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 32832508Syz155240 continue; 32842508Syz155240 } else if (ifp != NULL) 32852508Syz155240 nat->nat_ifps[1] = ifp; 32862508Syz155240 32872393Syz155240 nflags = nat->nat_flags; 32882508Syz155240 32892393Syz155240 if (nat->nat_inip.s_addr == srcip && 32902393Syz155240 nat->nat_oip.s_addr == dst.s_addr && 32912393Syz155240 (((p == 0) && (sflags == (nflags & NAT_TCPUDPICMP))) 32922393Syz155240 || (p == nat->nat_p))) { 32932393Syz155240 switch (p) 32942393Syz155240 { 32952393Syz155240 #if 0 32962393Syz155240 case IPPROTO_GRE : 32972393Syz155240 if (nat->nat_call[1] != fin->fin_data[0]) 32982393Syz155240 continue; 32992393Syz155240 break; 33002393Syz155240 #endif 33012393Syz155240 case IPPROTO_TCP : 33022393Syz155240 case IPPROTO_UDP : 33032393Syz155240 if (nat->nat_oport != dport) 33042393Syz155240 continue; 33052393Syz155240 if (nat->nat_inport != sport) 33062393Syz155240 continue; 33072393Syz155240 break; 33082393Syz155240 default : 33092393Syz155240 break; 33102393Syz155240 } 33112393Syz155240 33122393Syz155240 ipn = nat->nat_ptr; 33132393Syz155240 if ((ipn != NULL) && (nat->nat_aps != NULL)) 33142393Syz155240 if (appr_match(fin, nat) != 0) 33152393Syz155240 continue; 33162393Syz155240 return nat; 33172393Syz155240 } 33182393Syz155240 } 33192393Syz155240 33202393Syz155240 /* 33212393Syz155240 * So if we didn't find it but there are wildcard members in the hash 33222393Syz155240 * table, go back and look for them. We do this search and update here 33232393Syz155240 * because it is modifying the NAT table and we want to do this only 33242393Syz155240 * for the first packet that matches. The exception, of course, is 33252393Syz155240 * for "dummy" (FI_IGNORE) lookups. 33262393Syz155240 */ 33272393Syz155240 find_out_wild_ports: 33282393Syz155240 if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) 33292393Syz155240 return NULL; 33303448Sdh155122 if (ifs->ifs_nat_stats.ns_wilds == 0) 33312393Syz155240 return NULL; 33322393Syz155240 33333448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 33342393Syz155240 33352393Syz155240 hv = NAT_HASH_FN(srcip, 0, 0xffffffff); 33363448Sdh155122 hv = NAT_HASH_FN(dst.s_addr, hv, ifs->ifs_ipf_nattable_sz); 33373448Sdh155122 33383448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 33393448Sdh155122 33403448Sdh155122 nat = ifs->ifs_nat_table[0][hv]; 33412393Syz155240 for (; nat; nat = nat->nat_hnext[0]) { 33422508Syz155240 if (nat->nat_ifps[1] != NULL) { 33432508Syz155240 if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 33443448Sdh155122 continue; 33452508Syz155240 } else if (ifp != NULL) 33462508Syz155240 nat->nat_ifps[1] = ifp; 33472393Syz155240 33482393Syz155240 if (nat->nat_p != fin->fin_p) 33492393Syz155240 continue; 33502393Syz155240 if ((nat->nat_inip.s_addr != srcip) || 33512393Syz155240 (nat->nat_oip.s_addr != dst.s_addr)) 33522393Syz155240 continue; 33532393Syz155240 33542393Syz155240 nflags = nat->nat_flags; 33552393Syz155240 if (!(nflags & (NAT_TCPUDP|SI_WILDP))) 33562393Syz155240 continue; 33572393Syz155240 33582393Syz155240 if (nat_wildok(nat, (int)sport, (int)dport, nflags, 33592393Syz155240 NAT_OUTBOUND) == 1) { 33602393Syz155240 if ((fin->fin_flx & FI_IGNORE) != 0) 33612393Syz155240 break; 33622393Syz155240 if ((nflags & SI_CLONE) != 0) { 33632393Syz155240 nat = fr_natclone(fin, nat); 33642393Syz155240 if (nat == NULL) 33652393Syz155240 break; 33662393Syz155240 } else { 33673448Sdh155122 MUTEX_ENTER(&ifs->ifs_ipf_nat_new); 33683448Sdh155122 ifs->ifs_nat_stats.ns_wilds--; 33693448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_nat_new); 33702393Syz155240 } 33712393Syz155240 nat->nat_inport = sport; 33722393Syz155240 nat->nat_oport = dport; 33732393Syz155240 if (nat->nat_outport == 0) 33742393Syz155240 nat->nat_outport = sport; 33752393Syz155240 nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 33763448Sdh155122 nat_tabmove(nat, ifs); 33772393Syz155240 break; 33782393Syz155240 } 33792393Syz155240 } 33802393Syz155240 33813448Sdh155122 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); 33822393Syz155240 33832393Syz155240 return nat; 33842393Syz155240 } 33852393Syz155240 33862393Syz155240 33872393Syz155240 /* ------------------------------------------------------------------------ */ 33882393Syz155240 /* Function: nat_lookupredir */ 33892393Syz155240 /* Returns: nat_t* - NULL == no match, */ 33902393Syz155240 /* else pointer to matching NAT entry */ 33912393Syz155240 /* Parameters: np(I) - pointer to description of packet to find NAT table */ 33922393Syz155240 /* entry for. */ 33932393Syz155240 /* */ 33942393Syz155240 /* Lookup the NAT tables to search for a matching redirect */ 33952393Syz155240 /* ------------------------------------------------------------------------ */ 33963448Sdh155122 nat_t *nat_lookupredir(np, ifs) 33972393Syz155240 natlookup_t *np; 33983448Sdh155122 ipf_stack_t *ifs; 33992393Syz155240 { 34002393Syz155240 fr_info_t fi; 34012393Syz155240 nat_t *nat; 34022393Syz155240 34032393Syz155240 bzero((char *)&fi, sizeof(fi)); 34042393Syz155240 if (np->nl_flags & IPN_IN) { 34052393Syz155240 fi.fin_data[0] = ntohs(np->nl_realport); 34062393Syz155240 fi.fin_data[1] = ntohs(np->nl_outport); 34072393Syz155240 } else { 34082393Syz155240 fi.fin_data[0] = ntohs(np->nl_inport); 34092393Syz155240 fi.fin_data[1] = ntohs(np->nl_outport); 34102393Syz155240 } 34112393Syz155240 if (np->nl_flags & IPN_TCP) 34122393Syz155240 fi.fin_p = IPPROTO_TCP; 34132393Syz155240 else if (np->nl_flags & IPN_UDP) 34142393Syz155240 fi.fin_p = IPPROTO_UDP; 34152393Syz155240 else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) 34162393Syz155240 fi.fin_p = IPPROTO_ICMP; 34172393Syz155240 34183448Sdh155122 fi.fin_ifs = ifs; 34192393Syz155240 /* 34202393Syz155240 * We can do two sorts of lookups: 34212393Syz155240 * - IPN_IN: we have the `real' and `out' address, look for `in'. 34222393Syz155240 * - default: we have the `in' and `out' address, look for `real'. 34232393Syz155240 */ 34242393Syz155240 if (np->nl_flags & IPN_IN) { 34252393Syz155240 if ((nat = nat_inlookup(&fi, np->nl_flags, fi.fin_p, 34262393Syz155240 np->nl_realip, np->nl_outip))) { 34272393Syz155240 np->nl_inip = nat->nat_inip; 34282393Syz155240 np->nl_inport = nat->nat_inport; 34292393Syz155240 } 34302393Syz155240 } else { 34312393Syz155240 /* 34322393Syz155240 * If nl_inip is non null, this is a lookup based on the real 34332393Syz155240 * ip address. Else, we use the fake. 34342393Syz155240 */ 34352393Syz155240 if ((nat = nat_outlookup(&fi, np->nl_flags, fi.fin_p, 34362393Syz155240 np->nl_inip, np->nl_outip))) { 34372393Syz155240 34382393Syz155240 if ((np->nl_flags & IPN_FINDFORWARD) != 0) { 34392393Syz155240 fr_info_t fin; 34402393Syz155240 bzero((char *)&fin, sizeof(fin)); 34412393Syz155240 fin.fin_p = nat->nat_p; 34422393Syz155240 fin.fin_data[0] = ntohs(nat->nat_outport); 34432393Syz155240 fin.fin_data[1] = ntohs(nat->nat_oport); 34443448Sdh155122 fin.fin_ifs = ifs; 34452393Syz155240 if (nat_inlookup(&fin, np->nl_flags, fin.fin_p, 34462393Syz155240 nat->nat_outip, 34472393Syz155240 nat->nat_oip) != NULL) { 34482393Syz155240 np->nl_flags &= ~IPN_FINDFORWARD; 34492393Syz155240 } 34502393Syz155240 } 34512393Syz155240 34522393Syz155240 np->nl_realip = nat->nat_outip; 34532393Syz155240 np->nl_realport = nat->nat_outport; 34542393Syz155240 } 34552393Syz155240 } 34562393Syz155240 34572393Syz155240 return nat; 34582393Syz155240 } 34592393Syz155240 34602393Syz155240 34612393Syz155240 /* ------------------------------------------------------------------------ */ 34622393Syz155240 /* Function: nat_match */ 34632393Syz155240 /* Returns: int - 0 == no match, 1 == match */ 34642393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 34652393Syz155240 /* np(I) - pointer to NAT rule */ 34662393Syz155240 /* */ 34672393Syz155240 /* Pull the matching of a packet against a NAT rule out of that complex */ 34682393Syz155240 /* loop inside fr_checknatin() and lay it out properly in its own function. */ 34692393Syz155240 /* ------------------------------------------------------------------------ */ 34702393Syz155240 static int nat_match(fin, np) 34712393Syz155240 fr_info_t *fin; 34722393Syz155240 ipnat_t *np; 34732393Syz155240 { 34742393Syz155240 frtuc_t *ft; 34752393Syz155240 34762393Syz155240 if (fin->fin_v != 4) 34772393Syz155240 return 0; 34782393Syz155240 34792393Syz155240 if (np->in_p && fin->fin_p != np->in_p) 34802393Syz155240 return 0; 34812393Syz155240 34822393Syz155240 if (fin->fin_out) { 34832393Syz155240 if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK))) 34842393Syz155240 return 0; 34852393Syz155240 if (((fin->fin_fi.fi_saddr & np->in_inmsk) != np->in_inip) 34862393Syz155240 ^ ((np->in_flags & IPN_NOTSRC) != 0)) 34872393Syz155240 return 0; 34882393Syz155240 if (((fin->fin_fi.fi_daddr & np->in_srcmsk) != np->in_srcip) 34892393Syz155240 ^ ((np->in_flags & IPN_NOTDST) != 0)) 34902393Syz155240 return 0; 34912393Syz155240 } else { 34922393Syz155240 if (!(np->in_redir & NAT_REDIRECT)) 34932393Syz155240 return 0; 34942393Syz155240 if (((fin->fin_fi.fi_saddr & np->in_srcmsk) != np->in_srcip) 34952393Syz155240 ^ ((np->in_flags & IPN_NOTSRC) != 0)) 34962393Syz155240 return 0; 34972393Syz155240 if (((fin->fin_fi.fi_daddr & np->in_outmsk) != np->in_outip) 34982393Syz155240 ^ ((np->in_flags & IPN_NOTDST) != 0)) 34992393Syz155240 return 0; 35002393Syz155240 } 35012393Syz155240 35022393Syz155240 ft = &np->in_tuc; 35032393Syz155240 if (!(fin->fin_flx & FI_TCPUDP) || 35042393Syz155240 (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { 35052393Syz155240 if (ft->ftu_scmp || ft->ftu_dcmp) 35062393Syz155240 return 0; 35072393Syz155240 return 1; 35082393Syz155240 } 35092393Syz155240 35102393Syz155240 return fr_tcpudpchk(fin, ft); 35112393Syz155240 } 35122393Syz155240 35132393Syz155240 35142393Syz155240 /* ------------------------------------------------------------------------ */ 35152393Syz155240 /* Function: nat_update */ 35162393Syz155240 /* Returns: Nil */ 35172393Syz155240 /* Parameters: nat(I) - pointer to NAT structure */ 35182393Syz155240 /* np(I) - pointer to NAT rule */ 35192393Syz155240 /* */ 35202393Syz155240 /* Updates the lifetime of a NAT table entry for non-TCP packets. Must be */ 35212393Syz155240 /* called with fin_rev updated - i.e. after calling nat_proto(). */ 35222393Syz155240 /* ------------------------------------------------------------------------ */ 35232393Syz155240 void nat_update(fin, nat, np) 35242393Syz155240 fr_info_t *fin; 35252393Syz155240 nat_t *nat; 35262393Syz155240 ipnat_t *np; 35272393Syz155240 { 35282393Syz155240 ipftq_t *ifq, *ifq2; 35292393Syz155240 ipftqent_t *tqe; 35303448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 35312393Syz155240 35322393Syz155240 MUTEX_ENTER(&nat->nat_lock); 35332393Syz155240 tqe = &nat->nat_tqe; 35342393Syz155240 ifq = tqe->tqe_ifq; 35352393Syz155240 35362393Syz155240 /* 35372393Syz155240 * We allow over-riding of NAT timeouts from NAT rules, even for 35382393Syz155240 * TCP, however, if it is TCP and there is no rule timeout set, 35392393Syz155240 * then do not update the timeout here. 35402393Syz155240 */ 35412393Syz155240 if (np != NULL) 35422393Syz155240 ifq2 = np->in_tqehead[fin->fin_rev]; 35432393Syz155240 else 35442393Syz155240 ifq2 = NULL; 35452393Syz155240 35462393Syz155240 if (nat->nat_p == IPPROTO_TCP && ifq2 == NULL) { 35473448Sdh155122 (void) fr_tcp_age(&nat->nat_tqe, fin, ifs->ifs_nat_tqb, 0); 35482393Syz155240 } else { 35492393Syz155240 if (ifq2 == NULL) { 35502393Syz155240 if (nat->nat_p == IPPROTO_UDP) 35513448Sdh155122 ifq2 = &ifs->ifs_nat_udptq; 35522393Syz155240 else if (nat->nat_p == IPPROTO_ICMP) 35533448Sdh155122 ifq2 = &ifs->ifs_nat_icmptq; 35542393Syz155240 else 35553448Sdh155122 ifq2 = &ifs->ifs_nat_iptq; 35562393Syz155240 } 35572393Syz155240 35583448Sdh155122 fr_movequeue(tqe, ifq, ifq2, ifs); 35592393Syz155240 } 35602393Syz155240 MUTEX_EXIT(&nat->nat_lock); 35612393Syz155240 } 35622393Syz155240 35632393Syz155240 35642393Syz155240 /* ------------------------------------------------------------------------ */ 35652393Syz155240 /* Function: fr_checknatout */ 35662393Syz155240 /* Returns: int - -1 == packet failed NAT checks so block it, */ 35672393Syz155240 /* 0 == no packet translation occurred, */ 35682393Syz155240 /* 1 == packet was successfully translated. */ 35692393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 35702393Syz155240 /* passp(I) - pointer to filtering result flags */ 35712393Syz155240 /* */ 35722393Syz155240 /* Check to see if an outcoming packet should be changed. ICMP packets are */ 35732393Syz155240 /* first checked to see if they match an existing entry (if an error), */ 35742393Syz155240 /* otherwise a search of the current NAT table is made. If neither results */ 35752393Syz155240 /* in a match then a search for a matching NAT rule is made. Create a new */ 35762393Syz155240 /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 35772393Syz155240 /* packet header(s) as required. */ 35782393Syz155240 /* ------------------------------------------------------------------------ */ 35792393Syz155240 int fr_checknatout(fin, passp) 35802393Syz155240 fr_info_t *fin; 35812393Syz155240 u_32_t *passp; 35822393Syz155240 { 35832393Syz155240 struct ifnet *ifp, *sifp; 35842393Syz155240 icmphdr_t *icmp = NULL; 35852393Syz155240 tcphdr_t *tcp = NULL; 35862393Syz155240 int rval, natfailed; 35872393Syz155240 ipnat_t *np = NULL; 35882393Syz155240 u_int nflags = 0; 35892393Syz155240 u_32_t ipa, iph; 35902393Syz155240 int natadd = 1; 35912393Syz155240 frentry_t *fr; 35922393Syz155240 nat_t *nat; 35933448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 35943448Sdh155122 35953448Sdh155122 if (ifs->ifs_nat_stats.ns_rules == 0 || ifs->ifs_fr_nat_lock != 0) 35962393Syz155240 return 0; 35972393Syz155240 35982393Syz155240 natfailed = 0; 35992393Syz155240 fr = fin->fin_fr; 36002393Syz155240 sifp = fin->fin_ifp; 36012393Syz155240 if ((fr != NULL) && !(fr->fr_flags & FR_DUP) && 36023894Sjojemann fr->fr_tifs[fin->fin_rev].fd_ifp && 36033894Sjojemann fr->fr_tifs[fin->fin_rev].fd_ifp != (void *)-1) 36043894Sjojemann fin->fin_ifp = fr->fr_tifs[fin->fin_rev].fd_ifp; 36052393Syz155240 ifp = fin->fin_ifp; 36062393Syz155240 36072393Syz155240 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 36082393Syz155240 switch (fin->fin_p) 36092393Syz155240 { 36102393Syz155240 case IPPROTO_TCP : 36112393Syz155240 nflags = IPN_TCP; 36122393Syz155240 break; 36132393Syz155240 case IPPROTO_UDP : 36142393Syz155240 nflags = IPN_UDP; 36152393Syz155240 break; 36162393Syz155240 case IPPROTO_ICMP : 36172393Syz155240 icmp = fin->fin_dp; 36182393Syz155240 36192393Syz155240 /* 36202393Syz155240 * This is an incoming packet, so the destination is 36212393Syz155240 * the icmp_id and the source port equals 0 36222393Syz155240 */ 36232393Syz155240 if (nat_icmpquerytype4(icmp->icmp_type)) 36242393Syz155240 nflags = IPN_ICMPQUERY; 36252393Syz155240 break; 36262393Syz155240 default : 36272393Syz155240 break; 36282393Syz155240 } 36292393Syz155240 36302393Syz155240 if ((nflags & IPN_TCPUDP)) 36312393Syz155240 tcp = fin->fin_dp; 36322393Syz155240 } 36332393Syz155240 36342393Syz155240 ipa = fin->fin_saddr; 36352393Syz155240 36363448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 36372393Syz155240 36382393Syz155240 if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && 36392393Syz155240 (nat = nat_icmperror(fin, &nflags, NAT_OUTBOUND))) 36402393Syz155240 /*EMPTY*/; 36412393Syz155240 else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin))) 36422393Syz155240 natadd = 0; 36432393Syz155240 else if ((nat = nat_outlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, 36442393Syz155240 fin->fin_src, fin->fin_dst))) { 36452393Syz155240 nflags = nat->nat_flags; 36462393Syz155240 } else { 36472393Syz155240 u_32_t hv, msk, nmsk; 36482393Syz155240 36492393Syz155240 /* 36502393Syz155240 * If there is no current entry in the nat table for this IP#, 36512393Syz155240 * create one for it (if there is a matching rule). 36522393Syz155240 */ 36533448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 36542393Syz155240 msk = 0xffffffff; 36553448Sdh155122 nmsk = ifs->ifs_nat_masks; 36563448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 36572393Syz155240 maskloop: 36582393Syz155240 iph = ipa & htonl(msk); 36593448Sdh155122 hv = NAT_HASH_FN(iph, 0, ifs->ifs_ipf_natrules_sz); 36603448Sdh155122 for (np = ifs->ifs_nat_rules[hv]; np; np = np->in_mnext) 36612393Syz155240 { 36622508Syz155240 if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) 36632393Syz155240 continue; 36642393Syz155240 if (np->in_v != fin->fin_v) 36652393Syz155240 continue; 36662393Syz155240 if (np->in_p && (np->in_p != fin->fin_p)) 36672393Syz155240 continue; 36682393Syz155240 if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) 36692393Syz155240 continue; 36702393Syz155240 if (np->in_flags & IPN_FILTER) { 36712393Syz155240 if (!nat_match(fin, np)) 36722393Syz155240 continue; 36732393Syz155240 } else if ((ipa & np->in_inmsk) != np->in_inip) 36742393Syz155240 continue; 36752393Syz155240 36762393Syz155240 if ((fr != NULL) && 36772393Syz155240 !fr_matchtag(&np->in_tag, &fr->fr_nattag)) 36782393Syz155240 continue; 36792393Syz155240 36802393Syz155240 if (*np->in_plabel != '\0') { 36812393Syz155240 if (((np->in_flags & IPN_FILTER) == 0) && 36822393Syz155240 (np->in_dport != tcp->th_dport)) 36832393Syz155240 continue; 36842393Syz155240 if (appr_ok(fin, tcp, np) == 0) 36852393Syz155240 continue; 36862393Syz155240 } 36872393Syz155240 36882393Syz155240 if ((nat = nat_new(fin, np, NULL, nflags, 36892393Syz155240 NAT_OUTBOUND))) { 36902393Syz155240 np->in_hits++; 36912393Syz155240 break; 36922393Syz155240 } else 36932393Syz155240 natfailed = -1; 36942393Syz155240 } 36952393Syz155240 if ((np == NULL) && (nmsk != 0)) { 36962393Syz155240 while (nmsk) { 36972393Syz155240 msk <<= 1; 36982393Syz155240 if (nmsk & 0x80000000) 36992393Syz155240 break; 37002393Syz155240 nmsk <<= 1; 37012393Syz155240 } 37022393Syz155240 if (nmsk != 0) { 37032393Syz155240 nmsk <<= 1; 37042393Syz155240 goto maskloop; 37052393Syz155240 } 37062393Syz155240 } 37073448Sdh155122 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); 37082393Syz155240 } 37092393Syz155240 37102393Syz155240 if (nat != NULL) { 37112393Syz155240 rval = fr_natout(fin, nat, natadd, nflags); 37122393Syz155240 if (rval == 1) { 37132393Syz155240 MUTEX_ENTER(&nat->nat_lock); 37142393Syz155240 nat->nat_ref++; 37152393Syz155240 MUTEX_EXIT(&nat->nat_lock); 37164817San207044 nat->nat_touched = ifs->ifs_fr_ticks; 37172393Syz155240 fin->fin_nat = nat; 37182393Syz155240 } 37192393Syz155240 } else 37202393Syz155240 rval = natfailed; 37213448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 37222393Syz155240 37232393Syz155240 if (rval == -1) { 37242393Syz155240 if (passp != NULL) 37252393Syz155240 *passp = FR_BLOCK; 37262393Syz155240 fin->fin_flx |= FI_BADNAT; 37272393Syz155240 } 37282393Syz155240 fin->fin_ifp = sifp; 37292393Syz155240 return rval; 37302393Syz155240 } 37312393Syz155240 37322393Syz155240 /* ------------------------------------------------------------------------ */ 37332393Syz155240 /* Function: fr_natout */ 37342393Syz155240 /* Returns: int - -1 == packet failed NAT checks so block it, */ 37352393Syz155240 /* 1 == packet was successfully translated. */ 37362393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 37372393Syz155240 /* nat(I) - pointer to NAT structure */ 37382393Syz155240 /* natadd(I) - flag indicating if it is safe to add frag cache */ 37392393Syz155240 /* nflags(I) - NAT flags set for this packet */ 37402393Syz155240 /* */ 37412393Syz155240 /* Translate a packet coming "out" on an interface. */ 37422393Syz155240 /* ------------------------------------------------------------------------ */ 37432393Syz155240 int fr_natout(fin, nat, natadd, nflags) 37442393Syz155240 fr_info_t *fin; 37452393Syz155240 nat_t *nat; 37462393Syz155240 int natadd; 37472393Syz155240 u_32_t nflags; 37482393Syz155240 { 37492393Syz155240 icmphdr_t *icmp; 37502393Syz155240 u_short *csump; 37512958Sdr146992 u_32_t sumd; 37522393Syz155240 tcphdr_t *tcp; 37532393Syz155240 ipnat_t *np; 37542393Syz155240 int i; 37553448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 37562393Syz155240 37572958Sdr146992 #if SOLARIS && defined(_KERNEL) 37582958Sdr146992 net_data_t net_data_p; 37592958Sdr146992 if (fin->fin_v == 4) 37603448Sdh155122 net_data_p = ifs->ifs_ipf_ipv4; 37612958Sdr146992 else 37623448Sdh155122 net_data_p = ifs->ifs_ipf_ipv6; 37632958Sdr146992 #endif 37642958Sdr146992 37652393Syz155240 tcp = NULL; 37662393Syz155240 icmp = NULL; 37672393Syz155240 csump = NULL; 37682393Syz155240 np = nat->nat_ptr; 37692393Syz155240 37704712Szf203873 if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) 37712393Syz155240 (void) fr_nat_newfrag(fin, 0, nat); 37722393Syz155240 37732393Syz155240 MUTEX_ENTER(&nat->nat_lock); 37742393Syz155240 nat->nat_bytes[1] += fin->fin_plen; 37752393Syz155240 nat->nat_pkts[1]++; 37762393Syz155240 MUTEX_EXIT(&nat->nat_lock); 37772958Sdr146992 37782393Syz155240 /* 37792393Syz155240 * Fix up checksums, not by recalculating them, but 37802393Syz155240 * simply computing adjustments. 37812393Syz155240 * This is only done for STREAMS based IP implementations where the 37822393Syz155240 * checksum has already been calculated by IP. In all other cases, 37832393Syz155240 * IPFilter is called before the checksum needs calculating so there 37842393Syz155240 * is no call to modify whatever is in the header now. 37852393Syz155240 */ 37862958Sdr146992 ASSERT(fin->fin_m != NULL); 37872958Sdr146992 if (fin->fin_v == 4 && !NET_IS_HCK_L3_FULL(net_data_p, fin->fin_m)) { 37882393Syz155240 if (nflags == IPN_ICMPERR) { 37892958Sdr146992 u_32_t s1, s2; 37902393Syz155240 37912393Syz155240 s1 = LONG_SUM(ntohl(fin->fin_saddr)); 37922393Syz155240 s2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)); 37932393Syz155240 CALC_SUMD(s1, s2, sumd); 37942958Sdr146992 37952958Sdr146992 fix_outcksum(&fin->fin_ip->ip_sum, sumd); 37962393Syz155240 } 37972393Syz155240 #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 37982393Syz155240 defined(linux) || defined(BRIDGE_IPF) 37992393Syz155240 else { 38002393Syz155240 /* 38012393Syz155240 * Strictly speaking, this isn't necessary on BSD 38022393Syz155240 * kernels because they do checksum calculation after 38032393Syz155240 * this code has run BUT if ipfilter is being used 38042393Syz155240 * to do NAT as a bridge, that code doesn't exist. 38052393Syz155240 */ 38062393Syz155240 if (nat->nat_dir == NAT_OUTBOUND) 38072958Sdr146992 fix_outcksum(&fin->fin_ip->ip_sum, 38082958Sdr146992 nat->nat_ipsumd); 38092393Syz155240 else 38102958Sdr146992 fix_incksum(&fin->fin_ip->ip_sum, 38112958Sdr146992 nat->nat_ipsumd); 38122393Syz155240 } 38132393Syz155240 #endif 38142393Syz155240 } 38152393Syz155240 38162393Syz155240 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 38172393Syz155240 if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) { 38182393Syz155240 tcp = fin->fin_dp; 38192393Syz155240 38202393Syz155240 tcp->th_sport = nat->nat_outport; 38212393Syz155240 fin->fin_data[0] = ntohs(nat->nat_outport); 38222393Syz155240 } 38232393Syz155240 38242393Syz155240 if ((nat->nat_outport != 0) && (nflags & IPN_ICMPQUERY)) { 38252393Syz155240 icmp = fin->fin_dp; 38262393Syz155240 icmp->icmp_id = nat->nat_outport; 38272393Syz155240 } 38282393Syz155240 38292393Syz155240 csump = nat_proto(fin, nat, nflags); 38302393Syz155240 } 38312393Syz155240 38322393Syz155240 fin->fin_ip->ip_src = nat->nat_outip; 38332393Syz155240 38342393Syz155240 nat_update(fin, nat, np); 38352393Syz155240 38362393Syz155240 /* 38372393Syz155240 * The above comments do not hold for layer 4 (or higher) checksums... 38382393Syz155240 */ 38392958Sdr146992 if (csump != NULL && !NET_IS_HCK_L4_FULL(net_data_p, fin->fin_m)) { 38402958Sdr146992 if (nflags & IPN_TCPUDP && 38412958Sdr146992 NET_IS_HCK_L4_PART(net_data_p, fin->fin_m)) 38422958Sdr146992 sumd = nat->nat_sumd[1]; 38432958Sdr146992 else 38442958Sdr146992 sumd = nat->nat_sumd[0]; 38452958Sdr146992 38462393Syz155240 if (nat->nat_dir == NAT_OUTBOUND) 38472958Sdr146992 fix_outcksum(csump, sumd); 38482393Syz155240 else 38492958Sdr146992 fix_incksum(csump, sumd); 38502393Syz155240 } 38512393Syz155240 #ifdef IPFILTER_SYNC 38522393Syz155240 ipfsync_update(SMC_NAT, fin, nat->nat_sync); 38532393Syz155240 #endif 38542393Syz155240 /* ------------------------------------------------------------- */ 38552393Syz155240 /* A few quick notes: */ 38562393Syz155240 /* Following are test conditions prior to calling the */ 38572393Syz155240 /* appr_check routine. */ 38582393Syz155240 /* */ 38592393Syz155240 /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 38602393Syz155240 /* with a redirect rule, we attempt to match the packet's */ 38612393Syz155240 /* source port against in_dport, otherwise we'd compare the */ 38622393Syz155240 /* packet's destination. */ 38632393Syz155240 /* ------------------------------------------------------------- */ 38642393Syz155240 if ((np != NULL) && (np->in_apr != NULL)) { 38652393Syz155240 i = appr_check(fin, nat); 38662393Syz155240 if (i == 0) 38672393Syz155240 i = 1; 38682393Syz155240 } else 38692393Syz155240 i = 1; 38703448Sdh155122 ATOMIC_INCL(ifs->ifs_nat_stats.ns_mapped[1]); 38712393Syz155240 fin->fin_flx |= FI_NATED; 38722393Syz155240 return i; 38732393Syz155240 } 38742393Syz155240 38752393Syz155240 38762393Syz155240 /* ------------------------------------------------------------------------ */ 38772393Syz155240 /* Function: fr_checknatin */ 38782393Syz155240 /* Returns: int - -1 == packet failed NAT checks so block it, */ 38792393Syz155240 /* 0 == no packet translation occurred, */ 38802393Syz155240 /* 1 == packet was successfully translated. */ 38812393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 38822393Syz155240 /* passp(I) - pointer to filtering result flags */ 38832393Syz155240 /* */ 38842393Syz155240 /* Check to see if an incoming packet should be changed. ICMP packets are */ 38852393Syz155240 /* first checked to see if they match an existing entry (if an error), */ 38862393Syz155240 /* otherwise a search of the current NAT table is made. If neither results */ 38872393Syz155240 /* in a match then a search for a matching NAT rule is made. Create a new */ 38882393Syz155240 /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 38892393Syz155240 /* packet header(s) as required. */ 38902393Syz155240 /* ------------------------------------------------------------------------ */ 38912393Syz155240 int fr_checknatin(fin, passp) 38922393Syz155240 fr_info_t *fin; 38932393Syz155240 u_32_t *passp; 38942393Syz155240 { 38952393Syz155240 u_int nflags, natadd; 38962393Syz155240 int rval, natfailed; 38972393Syz155240 struct ifnet *ifp; 38982393Syz155240 struct in_addr in; 38992393Syz155240 icmphdr_t *icmp; 39002393Syz155240 tcphdr_t *tcp; 39012393Syz155240 u_short dport; 39022393Syz155240 ipnat_t *np; 39032393Syz155240 nat_t *nat; 39042393Syz155240 u_32_t iph; 39053448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 39063448Sdh155122 39073448Sdh155122 if (ifs->ifs_nat_stats.ns_rules == 0 || ifs->ifs_fr_nat_lock != 0) 39082393Syz155240 return 0; 39092393Syz155240 39102393Syz155240 tcp = NULL; 39112393Syz155240 icmp = NULL; 39122393Syz155240 dport = 0; 39132393Syz155240 natadd = 1; 39142393Syz155240 nflags = 0; 39152393Syz155240 natfailed = 0; 39162393Syz155240 ifp = fin->fin_ifp; 39172393Syz155240 39182393Syz155240 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 39192393Syz155240 switch (fin->fin_p) 39202393Syz155240 { 39212393Syz155240 case IPPROTO_TCP : 39222393Syz155240 nflags = IPN_TCP; 39232393Syz155240 break; 39242393Syz155240 case IPPROTO_UDP : 39252393Syz155240 nflags = IPN_UDP; 39262393Syz155240 break; 39272393Syz155240 case IPPROTO_ICMP : 39282393Syz155240 icmp = fin->fin_dp; 39292393Syz155240 39302393Syz155240 /* 39312393Syz155240 * This is an incoming packet, so the destination is 39322393Syz155240 * the icmp_id and the source port equals 0 39332393Syz155240 */ 39342393Syz155240 if (nat_icmpquerytype4(icmp->icmp_type)) { 39352393Syz155240 nflags = IPN_ICMPQUERY; 39362393Syz155240 dport = icmp->icmp_id; 39372393Syz155240 } break; 39382393Syz155240 default : 39392393Syz155240 break; 39402393Syz155240 } 39412393Syz155240 39422393Syz155240 if ((nflags & IPN_TCPUDP)) { 39432393Syz155240 tcp = fin->fin_dp; 39442393Syz155240 dport = tcp->th_dport; 39452393Syz155240 } 39462393Syz155240 } 39472393Syz155240 39482393Syz155240 in = fin->fin_dst; 39492393Syz155240 39503448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 39512393Syz155240 39522393Syz155240 if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && 39532393Syz155240 (nat = nat_icmperror(fin, &nflags, NAT_INBOUND))) 39542393Syz155240 /*EMPTY*/; 39552393Syz155240 else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin))) 39562393Syz155240 natadd = 0; 39572393Syz155240 else if ((nat = nat_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, 39582393Syz155240 fin->fin_src, in))) { 39592393Syz155240 nflags = nat->nat_flags; 39602393Syz155240 } else { 39612393Syz155240 u_32_t hv, msk, rmsk; 39622393Syz155240 39633448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 39643448Sdh155122 rmsk = ifs->ifs_rdr_masks; 39652393Syz155240 msk = 0xffffffff; 39663448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 39672393Syz155240 /* 39682393Syz155240 * If there is no current entry in the nat table for this IP#, 39692393Syz155240 * create one for it (if there is a matching rule). 39702393Syz155240 */ 39712393Syz155240 maskloop: 39722393Syz155240 iph = in.s_addr & htonl(msk); 39733448Sdh155122 hv = NAT_HASH_FN(iph, 0, ifs->ifs_ipf_rdrrules_sz); 39743448Sdh155122 for (np = ifs->ifs_rdr_rules[hv]; np; np = np->in_rnext) { 39752393Syz155240 if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) 39762393Syz155240 continue; 39772393Syz155240 if (np->in_v != fin->fin_v) 39782393Syz155240 continue; 39792393Syz155240 if (np->in_p && (np->in_p != fin->fin_p)) 39802393Syz155240 continue; 39812393Syz155240 if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) 39822393Syz155240 continue; 39832393Syz155240 if (np->in_flags & IPN_FILTER) { 39842393Syz155240 if (!nat_match(fin, np)) 39852393Syz155240 continue; 39862393Syz155240 } else { 39872393Syz155240 if ((in.s_addr & np->in_outmsk) != np->in_outip) 39882393Syz155240 continue; 39892393Syz155240 if (np->in_pmin && 39902393Syz155240 ((ntohs(np->in_pmax) < ntohs(dport)) || 39912393Syz155240 (ntohs(dport) < ntohs(np->in_pmin)))) 39922393Syz155240 continue; 39932393Syz155240 } 39942393Syz155240 39952393Syz155240 if (*np->in_plabel != '\0') { 39962393Syz155240 if (!appr_ok(fin, tcp, np)) { 39972393Syz155240 continue; 39982393Syz155240 } 39992393Syz155240 } 40002393Syz155240 40012393Syz155240 nat = nat_new(fin, np, NULL, nflags, NAT_INBOUND); 40022393Syz155240 if (nat != NULL) { 40032393Syz155240 np->in_hits++; 40042393Syz155240 break; 40052393Syz155240 } else 40062393Syz155240 natfailed = -1; 40072393Syz155240 } 40082393Syz155240 40092393Syz155240 if ((np == NULL) && (rmsk != 0)) { 40102393Syz155240 while (rmsk) { 40112393Syz155240 msk <<= 1; 40122393Syz155240 if (rmsk & 0x80000000) 40132393Syz155240 break; 40142393Syz155240 rmsk <<= 1; 40152393Syz155240 } 40162393Syz155240 if (rmsk != 0) { 40172393Syz155240 rmsk <<= 1; 40182393Syz155240 goto maskloop; 40192393Syz155240 } 40202393Syz155240 } 40213448Sdh155122 MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); 40222393Syz155240 } 40232393Syz155240 if (nat != NULL) { 40242393Syz155240 rval = fr_natin(fin, nat, natadd, nflags); 40252393Syz155240 if (rval == 1) { 40262393Syz155240 MUTEX_ENTER(&nat->nat_lock); 40272393Syz155240 nat->nat_ref++; 40282393Syz155240 MUTEX_EXIT(&nat->nat_lock); 40294817San207044 nat->nat_touched = ifs->ifs_fr_ticks; 40302393Syz155240 fin->fin_nat = nat; 40312393Syz155240 fin->fin_state = nat->nat_state; 40322393Syz155240 } 40332393Syz155240 } else 40342393Syz155240 rval = natfailed; 40353448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 40362393Syz155240 40372393Syz155240 if (rval == -1) { 40382393Syz155240 if (passp != NULL) 40392393Syz155240 *passp = FR_BLOCK; 40402393Syz155240 fin->fin_flx |= FI_BADNAT; 40412393Syz155240 } 40422393Syz155240 return rval; 40432393Syz155240 } 40442393Syz155240 40452393Syz155240 40462393Syz155240 /* ------------------------------------------------------------------------ */ 40472393Syz155240 /* Function: fr_natin */ 40482393Syz155240 /* Returns: int - -1 == packet failed NAT checks so block it, */ 40492393Syz155240 /* 1 == packet was successfully translated. */ 40502393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 40512393Syz155240 /* nat(I) - pointer to NAT structure */ 40522393Syz155240 /* natadd(I) - flag indicating if it is safe to add frag cache */ 40532393Syz155240 /* nflags(I) - NAT flags set for this packet */ 40542393Syz155240 /* Locks Held: ipf_nat (READ) */ 40552393Syz155240 /* */ 40562393Syz155240 /* Translate a packet coming "in" on an interface. */ 40572393Syz155240 /* ------------------------------------------------------------------------ */ 40582393Syz155240 int fr_natin(fin, nat, natadd, nflags) 40592393Syz155240 fr_info_t *fin; 40602393Syz155240 nat_t *nat; 40612393Syz155240 int natadd; 40622393Syz155240 u_32_t nflags; 40632393Syz155240 { 40642393Syz155240 icmphdr_t *icmp; 40652958Sdr146992 u_short *csump, *csump1; 40662958Sdr146992 u_32_t sumd; 40672393Syz155240 tcphdr_t *tcp; 40682393Syz155240 ipnat_t *np; 40692393Syz155240 int i; 40703448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 40712393Syz155240 40722958Sdr146992 #if SOLARIS && defined(_KERNEL) 40732958Sdr146992 net_data_t net_data_p; 40742958Sdr146992 if (fin->fin_v == 4) 40753448Sdh155122 net_data_p = ifs->ifs_ipf_ipv4; 40762958Sdr146992 else 40773448Sdh155122 net_data_p = ifs->ifs_ipf_ipv6; 40782958Sdr146992 #endif 40792958Sdr146992 40802393Syz155240 tcp = NULL; 40812393Syz155240 csump = NULL; 40822393Syz155240 np = nat->nat_ptr; 40832393Syz155240 fin->fin_fr = nat->nat_fr; 40842393Syz155240 40854712Szf203873 if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) 40864712Szf203873 (void) fr_nat_newfrag(fin, 0, nat); 40874712Szf203873 40882393Syz155240 if (np != NULL) { 40892393Syz155240 40902393Syz155240 /* ------------------------------------------------------------- */ 40912393Syz155240 /* A few quick notes: */ 40922393Syz155240 /* Following are test conditions prior to calling the */ 40932393Syz155240 /* appr_check routine. */ 40942393Syz155240 /* */ 40952393Syz155240 /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 40962393Syz155240 /* with a map rule, we attempt to match the packet's */ 40972393Syz155240 /* source port against in_dport, otherwise we'd compare the */ 40982393Syz155240 /* packet's destination. */ 40992393Syz155240 /* ------------------------------------------------------------- */ 41002393Syz155240 if (np->in_apr != NULL) { 41012393Syz155240 i = appr_check(fin, nat); 41022393Syz155240 if (i == -1) { 41032393Syz155240 return -1; 41042393Syz155240 } 41052393Syz155240 } 41062393Syz155240 } 41072393Syz155240 41082393Syz155240 #ifdef IPFILTER_SYNC 41092393Syz155240 ipfsync_update(SMC_NAT, fin, nat->nat_sync); 41102393Syz155240 #endif 41112393Syz155240 41122393Syz155240 MUTEX_ENTER(&nat->nat_lock); 41132393Syz155240 nat->nat_bytes[0] += fin->fin_plen; 41142393Syz155240 nat->nat_pkts[0]++; 41152393Syz155240 MUTEX_EXIT(&nat->nat_lock); 41162393Syz155240 41172393Syz155240 fin->fin_ip->ip_dst = nat->nat_inip; 41182393Syz155240 fin->fin_fi.fi_daddr = nat->nat_inip.s_addr; 41192393Syz155240 if (nflags & IPN_TCPUDP) 41202393Syz155240 tcp = fin->fin_dp; 41212393Syz155240 41222393Syz155240 /* 41232393Syz155240 * Fix up checksums, not by recalculating them, but 41242393Syz155240 * simply computing adjustments. 41252393Syz155240 * Why only do this for some platforms on inbound packets ? 41262393Syz155240 * Because for those that it is done, IP processing is yet to happen 41272393Syz155240 * and so the IPv4 header checksum has not yet been evaluated. 41282393Syz155240 * Perhaps it should always be done for the benefit of things like 41292393Syz155240 * fast forwarding (so that it doesn't need to be recomputed) but with 41302393Syz155240 * header checksum offloading, perhaps it is a moot point. 41312393Syz155240 */ 41322393Syz155240 #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 41332393Syz155240 defined(__osf__) || defined(linux) 41342393Syz155240 if (nat->nat_dir == NAT_OUTBOUND) 41352958Sdr146992 fix_incksum(&fin->fin_ip->ip_sum, nat->nat_ipsumd); 41362393Syz155240 else 41372958Sdr146992 fix_outcksum(&fin->fin_ip->ip_sum, nat->nat_ipsumd); 41382393Syz155240 #endif 41392393Syz155240 41402393Syz155240 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 41412393Syz155240 if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) { 41422393Syz155240 tcp->th_dport = nat->nat_inport; 41432393Syz155240 fin->fin_data[1] = ntohs(nat->nat_inport); 41442393Syz155240 } 41452393Syz155240 41462393Syz155240 41472393Syz155240 if ((nat->nat_inport != 0) && (nflags & IPN_ICMPQUERY)) { 41482393Syz155240 icmp = fin->fin_dp; 41492393Syz155240 41502393Syz155240 icmp->icmp_id = nat->nat_inport; 41512393Syz155240 } 41522393Syz155240 41532393Syz155240 csump = nat_proto(fin, nat, nflags); 41542393Syz155240 } 41552393Syz155240 41562393Syz155240 nat_update(fin, nat, np); 41572393Syz155240 41582958Sdr146992 #if SOLARIS && defined(_KERNEL) 41592958Sdr146992 if (nflags & IPN_TCPUDP && 41602958Sdr146992 NET_IS_HCK_L4_PART(net_data_p, fin->fin_m)) { 41612958Sdr146992 sumd = nat->nat_sumd[1]; 41622958Sdr146992 csump1 = &(fin->fin_m->b_datap->db_struioun.cksum.cksum_val.u16); 41632958Sdr146992 if (csump1 != NULL) { 41642958Sdr146992 if (nat->nat_dir == NAT_OUTBOUND) 41652958Sdr146992 fix_incksum(csump1, sumd); 41662958Sdr146992 else 41672958Sdr146992 fix_outcksum(csump1, sumd); 41682958Sdr146992 } 41692958Sdr146992 } else 41702958Sdr146992 #endif 41712958Sdr146992 sumd = nat->nat_sumd[0]; 41722958Sdr146992 41732393Syz155240 /* 41742958Sdr146992 * Inbound packets always need to have their address adjusted in case 41752958Sdr146992 * code following this validates it. 41762393Syz155240 */ 41772393Syz155240 if (csump != NULL) { 41782393Syz155240 if (nat->nat_dir == NAT_OUTBOUND) 41792958Sdr146992 fix_incksum(csump, sumd); 41802393Syz155240 else 41812958Sdr146992 fix_outcksum(csump, sumd); 41822393Syz155240 } 41833448Sdh155122 ATOMIC_INCL(ifs->ifs_nat_stats.ns_mapped[0]); 41842393Syz155240 fin->fin_flx |= FI_NATED; 41852393Syz155240 if (np != NULL && np->in_tag.ipt_num[0] != 0) 41862393Syz155240 fin->fin_nattag = &np->in_tag; 41872393Syz155240 return 1; 41882393Syz155240 } 41892393Syz155240 41902393Syz155240 41912393Syz155240 /* ------------------------------------------------------------------------ */ 41922393Syz155240 /* Function: nat_proto */ 41932393Syz155240 /* Returns: u_short* - pointer to transport header checksum to update, */ 41942393Syz155240 /* NULL if the transport protocol is not recognised */ 41952393Syz155240 /* as needing a checksum update. */ 41962393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 41972393Syz155240 /* nat(I) - pointer to NAT structure */ 41982393Syz155240 /* nflags(I) - NAT flags set for this packet */ 41992393Syz155240 /* */ 42002393Syz155240 /* Return the pointer to the checksum field for each protocol so understood.*/ 42012393Syz155240 /* If support for making other changes to a protocol header is required, */ 42022393Syz155240 /* that is not strictly 'address' translation, such as clamping the MSS in */ 42032393Syz155240 /* TCP down to a specific value, then do it from here. */ 42042393Syz155240 /* ------------------------------------------------------------------------ */ 42052393Syz155240 u_short *nat_proto(fin, nat, nflags) 42062393Syz155240 fr_info_t *fin; 42072393Syz155240 nat_t *nat; 42082393Syz155240 u_int nflags; 42092393Syz155240 { 42102393Syz155240 icmphdr_t *icmp; 42112393Syz155240 u_short *csump; 42122393Syz155240 tcphdr_t *tcp; 42132393Syz155240 udphdr_t *udp; 42142393Syz155240 42152393Syz155240 csump = NULL; 42162393Syz155240 if (fin->fin_out == 0) { 42172393Syz155240 fin->fin_rev = (nat->nat_dir == NAT_OUTBOUND); 42182393Syz155240 } else { 42192393Syz155240 fin->fin_rev = (nat->nat_dir == NAT_INBOUND); 42202393Syz155240 } 42212393Syz155240 42222393Syz155240 switch (fin->fin_p) 42232393Syz155240 { 42242393Syz155240 case IPPROTO_TCP : 42252393Syz155240 tcp = fin->fin_dp; 42262393Syz155240 42272393Syz155240 csump = &tcp->th_sum; 42282393Syz155240 42292393Syz155240 /* 42302393Syz155240 * Do a MSS CLAMPING on a SYN packet, 42312393Syz155240 * only deal IPv4 for now. 42322393Syz155240 */ 42332393Syz155240 if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0) 42342958Sdr146992 nat_mssclamp(tcp, nat->nat_mssclamp, csump); 42352393Syz155240 42362393Syz155240 break; 42372393Syz155240 42382393Syz155240 case IPPROTO_UDP : 42392393Syz155240 udp = fin->fin_dp; 42402393Syz155240 42412393Syz155240 if (udp->uh_sum) 42422393Syz155240 csump = &udp->uh_sum; 42432393Syz155240 break; 42442393Syz155240 42452393Syz155240 case IPPROTO_ICMP : 42462393Syz155240 icmp = fin->fin_dp; 42472393Syz155240 42482393Syz155240 if ((nflags & IPN_ICMPQUERY) != 0) { 42492393Syz155240 if (icmp->icmp_cksum != 0) 42502393Syz155240 csump = &icmp->icmp_cksum; 42512393Syz155240 } 42522393Syz155240 break; 42532393Syz155240 } 42542393Syz155240 return csump; 42552393Syz155240 } 42562393Syz155240 42572393Syz155240 42582393Syz155240 /* ------------------------------------------------------------------------ */ 42592393Syz155240 /* Function: fr_natunload */ 42602393Syz155240 /* Returns: Nil */ 42612393Syz155240 /* Parameters: Nil */ 42622393Syz155240 /* */ 42632393Syz155240 /* Free all memory used by NAT structures allocated at runtime. */ 42642393Syz155240 /* ------------------------------------------------------------------------ */ 42653448Sdh155122 void fr_natunload(ifs) 42663448Sdh155122 ipf_stack_t *ifs; 42672393Syz155240 { 42682393Syz155240 ipftq_t *ifq, *ifqnext; 42692393Syz155240 42703448Sdh155122 (void) nat_clearlist(ifs); 42713448Sdh155122 (void) nat_flushtable(ifs); 42722393Syz155240 42732393Syz155240 /* 42742393Syz155240 * Proxy timeout queues are not cleaned here because although they 42752393Syz155240 * exist on the NAT list, appr_unload is called after fr_natunload 42762393Syz155240 * and the proxies actually are responsible for them being created. 42772393Syz155240 * Should the proxy timeouts have their own list? There's no real 42782393Syz155240 * justification as this is the only complication. 42792393Syz155240 */ 42803448Sdh155122 for (ifq = ifs->ifs_nat_utqe; ifq != NULL; ifq = ifqnext) { 42812393Syz155240 ifqnext = ifq->ifq_next; 42822393Syz155240 if (((ifq->ifq_flags & IFQF_PROXY) == 0) && 42832393Syz155240 (fr_deletetimeoutqueue(ifq) == 0)) 42843448Sdh155122 fr_freetimeoutqueue(ifq, ifs); 42852393Syz155240 } 42862393Syz155240 42873448Sdh155122 if (ifs->ifs_nat_table[0] != NULL) { 42883448Sdh155122 KFREES(ifs->ifs_nat_table[0], 42893448Sdh155122 sizeof(nat_t *) * ifs->ifs_ipf_nattable_sz); 42903448Sdh155122 ifs->ifs_nat_table[0] = NULL; 42912393Syz155240 } 42923448Sdh155122 if (ifs->ifs_nat_table[1] != NULL) { 42933448Sdh155122 KFREES(ifs->ifs_nat_table[1], 42943448Sdh155122 sizeof(nat_t *) * ifs->ifs_ipf_nattable_sz); 42953448Sdh155122 ifs->ifs_nat_table[1] = NULL; 42962393Syz155240 } 42973448Sdh155122 if (ifs->ifs_nat_rules != NULL) { 42983448Sdh155122 KFREES(ifs->ifs_nat_rules, 42993448Sdh155122 sizeof(ipnat_t *) * ifs->ifs_ipf_natrules_sz); 43003448Sdh155122 ifs->ifs_nat_rules = NULL; 43012393Syz155240 } 43023448Sdh155122 if (ifs->ifs_rdr_rules != NULL) { 43033448Sdh155122 KFREES(ifs->ifs_rdr_rules, 43043448Sdh155122 sizeof(ipnat_t *) * ifs->ifs_ipf_rdrrules_sz); 43053448Sdh155122 ifs->ifs_rdr_rules = NULL; 43062393Syz155240 } 43073448Sdh155122 if (ifs->ifs_maptable != NULL) { 43083448Sdh155122 KFREES(ifs->ifs_maptable, 43093448Sdh155122 sizeof(hostmap_t *) * ifs->ifs_ipf_hostmap_sz); 43103448Sdh155122 ifs->ifs_maptable = NULL; 43112393Syz155240 } 43123448Sdh155122 if (ifs->ifs_nat_stats.ns_bucketlen[0] != NULL) { 43133448Sdh155122 KFREES(ifs->ifs_nat_stats.ns_bucketlen[0], 43143448Sdh155122 sizeof(u_long *) * ifs->ifs_ipf_nattable_sz); 43153448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[0] = NULL; 43162393Syz155240 } 43173448Sdh155122 if (ifs->ifs_nat_stats.ns_bucketlen[1] != NULL) { 43183448Sdh155122 KFREES(ifs->ifs_nat_stats.ns_bucketlen[1], 43193448Sdh155122 sizeof(u_long *) * ifs->ifs_ipf_nattable_sz); 43203448Sdh155122 ifs->ifs_nat_stats.ns_bucketlen[1] = NULL; 43212393Syz155240 } 43222393Syz155240 43233448Sdh155122 if (ifs->ifs_fr_nat_maxbucket_reset == 1) 43243448Sdh155122 ifs->ifs_fr_nat_maxbucket = 0; 43253448Sdh155122 43263448Sdh155122 if (ifs->ifs_fr_nat_init == 1) { 43273448Sdh155122 ifs->ifs_fr_nat_init = 0; 43283448Sdh155122 fr_sttab_destroy(ifs->ifs_nat_tqb); 43293448Sdh155122 43303448Sdh155122 RW_DESTROY(&ifs->ifs_ipf_natfrag); 43313448Sdh155122 RW_DESTROY(&ifs->ifs_ipf_nat); 43323448Sdh155122 43333448Sdh155122 MUTEX_DESTROY(&ifs->ifs_ipf_nat_new); 43343448Sdh155122 MUTEX_DESTROY(&ifs->ifs_ipf_natio); 43353448Sdh155122 43363448Sdh155122 MUTEX_DESTROY(&ifs->ifs_nat_udptq.ifq_lock); 43373448Sdh155122 MUTEX_DESTROY(&ifs->ifs_nat_icmptq.ifq_lock); 43383448Sdh155122 MUTEX_DESTROY(&ifs->ifs_nat_iptq.ifq_lock); 43392393Syz155240 } 43402393Syz155240 } 43412393Syz155240 43422393Syz155240 43432393Syz155240 /* ------------------------------------------------------------------------ */ 43442393Syz155240 /* Function: fr_natexpire */ 43452393Syz155240 /* Returns: Nil */ 43462393Syz155240 /* Parameters: Nil */ 43472393Syz155240 /* */ 43482393Syz155240 /* Check all of the timeout queues for entries at the top which need to be */ 43492393Syz155240 /* expired. */ 43502393Syz155240 /* ------------------------------------------------------------------------ */ 43513448Sdh155122 void fr_natexpire(ifs) 43523448Sdh155122 ipf_stack_t *ifs; 43532393Syz155240 { 43542393Syz155240 ipftq_t *ifq, *ifqnext; 43552393Syz155240 ipftqent_t *tqe, *tqn; 43562393Syz155240 int i; 43572393Syz155240 SPL_INT(s); 43582393Syz155240 43592393Syz155240 SPL_NET(s); 43603448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 43613448Sdh155122 for (ifq = ifs->ifs_nat_tqb, i = 0; ifq != NULL; ifq = ifq->ifq_next) { 43622393Syz155240 for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { 43633448Sdh155122 if (tqe->tqe_die > ifs->ifs_fr_ticks) 43642393Syz155240 break; 43652393Syz155240 tqn = tqe->tqe_next; 43663448Sdh155122 nat_delete(tqe->tqe_parent, NL_EXPIRE, ifs); 43672393Syz155240 } 43682393Syz155240 } 43692393Syz155240 43703448Sdh155122 for (ifq = ifs->ifs_nat_utqe; ifq != NULL; ifq = ifqnext) { 43712393Syz155240 ifqnext = ifq->ifq_next; 43722393Syz155240 43732393Syz155240 for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { 43743448Sdh155122 if (tqe->tqe_die > ifs->ifs_fr_ticks) 43752393Syz155240 break; 43762393Syz155240 tqn = tqe->tqe_next; 43773448Sdh155122 nat_delete(tqe->tqe_parent, NL_EXPIRE, ifs); 43782393Syz155240 } 43792393Syz155240 } 43802393Syz155240 43813448Sdh155122 for (ifq = ifs->ifs_nat_utqe; ifq != NULL; ifq = ifqnext) { 43822393Syz155240 ifqnext = ifq->ifq_next; 43832393Syz155240 43842393Syz155240 if (((ifq->ifq_flags & IFQF_DELETE) != 0) && 43852393Syz155240 (ifq->ifq_ref == 0)) { 43863448Sdh155122 fr_freetimeoutqueue(ifq, ifs); 43872393Syz155240 } 43882393Syz155240 } 43892393Syz155240 43904817San207044 if (ifs->ifs_nat_doflush != 0) { 43914817San207044 (void) nat_extraflush(2, ifs); 43924817San207044 ifs->ifs_nat_doflush = 0; 43934817San207044 } 43944817San207044 43953448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 43962393Syz155240 SPL_X(s); 43972393Syz155240 } 43982393Syz155240 43992393Syz155240 44002393Syz155240 /* ------------------------------------------------------------------------ */ 44012958Sdr146992 /* Function: fr_nataddrsync */ 44022393Syz155240 /* Returns: Nil */ 44032958Sdr146992 /* Parameters: ifp(I) - pointer to network interface */ 44042958Sdr146992 /* addr(I) - pointer to new network address */ 44052393Syz155240 /* */ 44062393Syz155240 /* Walk through all of the currently active NAT sessions, looking for those */ 44072958Sdr146992 /* which need to have their translated address updated (where the interface */ 44082958Sdr146992 /* matches the one passed in) and change it, recalculating the checksum sum */ 44092958Sdr146992 /* difference too. */ 44102393Syz155240 /* ------------------------------------------------------------------------ */ 44113448Sdh155122 void fr_nataddrsync(ifp, addr, ifs) 44122393Syz155240 void *ifp; 44132958Sdr146992 struct in_addr *addr; 44143448Sdh155122 ipf_stack_t *ifs; 44152393Syz155240 { 44162393Syz155240 u_32_t sum1, sum2, sumd; 44172393Syz155240 nat_t *nat; 44182958Sdr146992 ipnat_t *np; 44192393Syz155240 SPL_INT(s); 44202393Syz155240 44213448Sdh155122 if (ifs->ifs_fr_running <= 0) 44222393Syz155240 return; 44232393Syz155240 44242393Syz155240 SPL_NET(s); 44253448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 44263448Sdh155122 44273448Sdh155122 if (ifs->ifs_fr_running <= 0) { 44283448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 44292393Syz155240 return; 44302393Syz155240 } 44312393Syz155240 44322958Sdr146992 /* 44332958Sdr146992 * Change IP addresses for NAT sessions for any protocol except TCP 44342958Sdr146992 * since it will break the TCP connection anyway. The only rules 44352958Sdr146992 * which will get changed are those which are "map ... -> 0/32", 44362958Sdr146992 * where the rule specifies the address is taken from the interface. 44372958Sdr146992 */ 44383448Sdh155122 for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) { 44392958Sdr146992 if (addr != NULL) { 44402958Sdr146992 if (((ifp != NULL) && ifp != (nat->nat_ifps[0])) || 44412958Sdr146992 ((nat->nat_flags & IPN_TCP) != 0)) 44422958Sdr146992 continue; 44432958Sdr146992 if (((np = nat->nat_ptr) == NULL) || 44442958Sdr146992 (np->in_nip || (np->in_outmsk != 0xffffffff))) 44452393Syz155240 continue; 44462393Syz155240 44472393Syz155240 /* 44482393Syz155240 * Change the map-to address to be the same as the 44492393Syz155240 * new one. 44502393Syz155240 */ 44512393Syz155240 sum1 = nat->nat_outip.s_addr; 44522958Sdr146992 nat->nat_outip = *addr; 44532958Sdr146992 sum2 = nat->nat_outip.s_addr; 44542958Sdr146992 44552958Sdr146992 } else if (((ifp == NULL) || (ifp == nat->nat_ifps[0])) && 44562958Sdr146992 !(nat->nat_flags & IPN_TCP) && (np = nat->nat_ptr) && 44572958Sdr146992 (np->in_outmsk == 0xffffffff) && !np->in_nip) { 44582958Sdr146992 struct in_addr in; 44592958Sdr146992 44602958Sdr146992 /* 44612958Sdr146992 * Change the map-to address to be the same as the 44622958Sdr146992 * new one. 44632958Sdr146992 */ 44642958Sdr146992 sum1 = nat->nat_outip.s_addr; 44652958Sdr146992 if (fr_ifpaddr(4, FRI_NORMAL, nat->nat_ifps[0], 44663448Sdh155122 &in, NULL, ifs) != -1) 44672393Syz155240 nat->nat_outip = in; 44682393Syz155240 sum2 = nat->nat_outip.s_addr; 44692958Sdr146992 } else { 44702958Sdr146992 continue; 44712393Syz155240 } 44722958Sdr146992 44732958Sdr146992 if (sum1 == sum2) 44742958Sdr146992 continue; 44752958Sdr146992 /* 44762958Sdr146992 * Readjust the checksum adjustment to take into 44772958Sdr146992 * account the new IP#. 44782958Sdr146992 */ 44792958Sdr146992 CALC_SUMD(sum1, sum2, sumd); 44802958Sdr146992 /* XXX - dont change for TCP when solaris does 44812958Sdr146992 * hardware checksumming. 44822958Sdr146992 */ 44832958Sdr146992 sumd += nat->nat_sumd[0]; 44842958Sdr146992 nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 44852958Sdr146992 nat->nat_sumd[1] = nat->nat_sumd[0]; 44862393Syz155240 } 44872393Syz155240 44883448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 44892958Sdr146992 SPL_X(s); 44902958Sdr146992 } 44912958Sdr146992 44922958Sdr146992 44932958Sdr146992 /* ------------------------------------------------------------------------ */ 44942958Sdr146992 /* Function: fr_natifpsync */ 44952958Sdr146992 /* Returns: Nil */ 44962958Sdr146992 /* Parameters: action(I) - how we are syncing */ 44972958Sdr146992 /* ifp(I) - pointer to network interface */ 44982958Sdr146992 /* name(I) - name of interface to sync to */ 44992958Sdr146992 /* */ 45002958Sdr146992 /* This function is used to resync the mapping of interface names and their */ 45012958Sdr146992 /* respective 'pointers'. For "action == IPFSYNC_RESYNC", resync all */ 45022958Sdr146992 /* interfaces by doing a new lookup of name to 'pointer'. For "action == */ 45032958Sdr146992 /* IPFSYNC_NEWIFP", treat ifp as the new pointer value associated with */ 45042958Sdr146992 /* "name" and for "action == IPFSYNC_OLDIFP", ifp is a pointer for which */ 45052958Sdr146992 /* there is no longer any interface associated with it. */ 45062958Sdr146992 /* ------------------------------------------------------------------------ */ 45073448Sdh155122 void fr_natifpsync(action, ifp, name, ifs) 45082958Sdr146992 int action; 45092958Sdr146992 void *ifp; 45102958Sdr146992 char *name; 45113448Sdh155122 ipf_stack_t *ifs; 45122958Sdr146992 { 45132958Sdr146992 #if defined(_KERNEL) && !defined(MENTAT) && defined(USE_SPL) 45142958Sdr146992 int s; 45152958Sdr146992 #endif 45162958Sdr146992 nat_t *nat; 45172958Sdr146992 ipnat_t *n; 45182958Sdr146992 45193448Sdh155122 if (ifs->ifs_fr_running <= 0) 45202958Sdr146992 return; 45212958Sdr146992 45222958Sdr146992 SPL_NET(s); 45233448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 45243448Sdh155122 45253448Sdh155122 if (ifs->ifs_fr_running <= 0) { 45263448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 45272958Sdr146992 return; 45282958Sdr146992 } 45292958Sdr146992 45302958Sdr146992 switch (action) 45312958Sdr146992 { 45322958Sdr146992 case IPFSYNC_RESYNC : 45333448Sdh155122 for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) { 45342958Sdr146992 if ((ifp == nat->nat_ifps[0]) || 45352958Sdr146992 (nat->nat_ifps[0] == (void *)-1)) { 45362958Sdr146992 nat->nat_ifps[0] = 45373448Sdh155122 fr_resolvenic(nat->nat_ifnames[0], 4, ifs); 45382958Sdr146992 } 45392958Sdr146992 45402958Sdr146992 if ((ifp == nat->nat_ifps[1]) || 45412958Sdr146992 (nat->nat_ifps[1] == (void *)-1)) { 45422958Sdr146992 nat->nat_ifps[1] = 45433448Sdh155122 fr_resolvenic(nat->nat_ifnames[1], 4, ifs); 45442958Sdr146992 } 45452958Sdr146992 } 45462958Sdr146992 45473448Sdh155122 for (n = ifs->ifs_nat_list; (n != NULL); n = n->in_next) { 45482958Sdr146992 if (n->in_ifps[0] == ifp || 45492958Sdr146992 n->in_ifps[0] == (void *)-1) { 45502958Sdr146992 n->in_ifps[0] = 45513448Sdh155122 fr_resolvenic(n->in_ifnames[0], 4, ifs); 45522958Sdr146992 } 45532958Sdr146992 if (n->in_ifps[1] == ifp || 45542958Sdr146992 n->in_ifps[1] == (void *)-1) { 45552958Sdr146992 n->in_ifps[1] = 45563448Sdh155122 fr_resolvenic(n->in_ifnames[1], 4, ifs); 45572958Sdr146992 } 45582958Sdr146992 } 45592958Sdr146992 break; 45602958Sdr146992 case IPFSYNC_NEWIFP : 45613448Sdh155122 for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) { 45622958Sdr146992 if (!strncmp(name, nat->nat_ifnames[0], 45632958Sdr146992 sizeof(nat->nat_ifnames[0]))) 45642958Sdr146992 nat->nat_ifps[0] = ifp; 45652958Sdr146992 if (!strncmp(name, nat->nat_ifnames[1], 45662958Sdr146992 sizeof(nat->nat_ifnames[1]))) 45672958Sdr146992 nat->nat_ifps[1] = ifp; 45682958Sdr146992 } 45693448Sdh155122 for (n = ifs->ifs_nat_list; (n != NULL); n = n->in_next) { 45702958Sdr146992 if (!strncmp(name, n->in_ifnames[0], 45712958Sdr146992 sizeof(n->in_ifnames[0]))) 45722958Sdr146992 n->in_ifps[0] = ifp; 45732958Sdr146992 if (!strncmp(name, n->in_ifnames[1], 45742958Sdr146992 sizeof(n->in_ifnames[1]))) 45752958Sdr146992 n->in_ifps[1] = ifp; 45762958Sdr146992 } 45772958Sdr146992 break; 45782958Sdr146992 case IPFSYNC_OLDIFP : 45793448Sdh155122 for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) { 45802958Sdr146992 if (ifp == nat->nat_ifps[0]) 45812958Sdr146992 nat->nat_ifps[0] = (void *)-1; 45822958Sdr146992 if (ifp == nat->nat_ifps[1]) 45832958Sdr146992 nat->nat_ifps[1] = (void *)-1; 45842958Sdr146992 } 45853448Sdh155122 for (n = ifs->ifs_nat_list; (n != NULL); n = n->in_next) { 45862958Sdr146992 if (n->in_ifps[0] == ifp) 45872958Sdr146992 n->in_ifps[0] = (void *)-1; 45882958Sdr146992 if (n->in_ifps[1] == ifp) 45892958Sdr146992 n->in_ifps[1] = (void *)-1; 45902958Sdr146992 } 45912958Sdr146992 break; 45922393Syz155240 } 45933448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 45942393Syz155240 SPL_X(s); 45952393Syz155240 } 45962393Syz155240 45972393Syz155240 45982393Syz155240 /* ------------------------------------------------------------------------ */ 45992393Syz155240 /* Function: nat_icmpquerytype4 */ 46002393Syz155240 /* Returns: int - 1 == success, 0 == failure */ 46012393Syz155240 /* Parameters: icmptype(I) - ICMP type number */ 46022393Syz155240 /* */ 46032393Syz155240 /* Tests to see if the ICMP type number passed is a query/response type or */ 46042393Syz155240 /* not. */ 46052393Syz155240 /* ------------------------------------------------------------------------ */ 46062393Syz155240 static INLINE int nat_icmpquerytype4(icmptype) 46072393Syz155240 int icmptype; 46082393Syz155240 { 46092393Syz155240 46102393Syz155240 /* 46112393Syz155240 * For the ICMP query NAT code, it is essential that both the query 46122393Syz155240 * and the reply match on the NAT rule. Because the NAT structure 46132393Syz155240 * does not keep track of the icmptype, and a single NAT structure 46142393Syz155240 * is used for all icmp types with the same src, dest and id, we 46152393Syz155240 * simply define the replies as queries as well. The funny thing is, 46162393Syz155240 * altough it seems silly to call a reply a query, this is exactly 46172393Syz155240 * as it is defined in the IPv4 specification 46182393Syz155240 */ 46192393Syz155240 46202393Syz155240 switch (icmptype) 46212393Syz155240 { 46222393Syz155240 46232393Syz155240 case ICMP_ECHOREPLY: 46242393Syz155240 case ICMP_ECHO: 46252393Syz155240 /* route aedvertisement/solliciation is currently unsupported: */ 46262393Syz155240 /* it would require rewriting the ICMP data section */ 46272393Syz155240 case ICMP_TSTAMP: 46282393Syz155240 case ICMP_TSTAMPREPLY: 46292393Syz155240 case ICMP_IREQ: 46302393Syz155240 case ICMP_IREQREPLY: 46312393Syz155240 case ICMP_MASKREQ: 46322393Syz155240 case ICMP_MASKREPLY: 46332393Syz155240 return 1; 46342393Syz155240 default: 46352393Syz155240 return 0; 46362393Syz155240 } 46372393Syz155240 } 46382393Syz155240 46392393Syz155240 46402393Syz155240 /* ------------------------------------------------------------------------ */ 46412393Syz155240 /* Function: nat_log */ 46422393Syz155240 /* Returns: Nil */ 46432393Syz155240 /* Parameters: nat(I) - pointer to NAT structure */ 46442393Syz155240 /* type(I) - type of log entry to create */ 46452393Syz155240 /* */ 46462393Syz155240 /* Creates a NAT log entry. */ 46472393Syz155240 /* ------------------------------------------------------------------------ */ 46483448Sdh155122 void nat_log(nat, type, ifs) 46492393Syz155240 struct nat *nat; 46502393Syz155240 u_int type; 46513448Sdh155122 ipf_stack_t *ifs; 46522393Syz155240 { 46532393Syz155240 #ifdef IPFILTER_LOG 46542393Syz155240 # ifndef LARGE_NAT 46552393Syz155240 struct ipnat *np; 46562393Syz155240 int rulen; 46572393Syz155240 # endif 46582393Syz155240 struct natlog natl; 46592393Syz155240 void *items[1]; 46602393Syz155240 size_t sizes[1]; 46612393Syz155240 int types[1]; 46622393Syz155240 46632393Syz155240 natl.nl_inip = nat->nat_inip; 46642393Syz155240 natl.nl_outip = nat->nat_outip; 46652393Syz155240 natl.nl_origip = nat->nat_oip; 46662393Syz155240 natl.nl_bytes[0] = nat->nat_bytes[0]; 46672393Syz155240 natl.nl_bytes[1] = nat->nat_bytes[1]; 46682393Syz155240 natl.nl_pkts[0] = nat->nat_pkts[0]; 46692393Syz155240 natl.nl_pkts[1] = nat->nat_pkts[1]; 46702393Syz155240 natl.nl_origport = nat->nat_oport; 46712393Syz155240 natl.nl_inport = nat->nat_inport; 46722393Syz155240 natl.nl_outport = nat->nat_outport; 46732393Syz155240 natl.nl_p = nat->nat_p; 46742393Syz155240 natl.nl_type = type; 46752393Syz155240 natl.nl_rule = -1; 46762393Syz155240 # ifndef LARGE_NAT 46772393Syz155240 if (nat->nat_ptr != NULL) { 46783448Sdh155122 for (rulen = 0, np = ifs->ifs_nat_list; np; 46793448Sdh155122 np = np->in_next, rulen++) 46802393Syz155240 if (np == nat->nat_ptr) { 46812393Syz155240 natl.nl_rule = rulen; 46822393Syz155240 break; 46832393Syz155240 } 46842393Syz155240 } 46852393Syz155240 # endif 46862393Syz155240 items[0] = &natl; 46872393Syz155240 sizes[0] = sizeof(natl); 46882393Syz155240 types[0] = 0; 46892393Syz155240 46903448Sdh155122 (void) ipllog(IPL_LOGNAT, NULL, items, sizes, types, 1, ifs); 46912393Syz155240 #endif 46922393Syz155240 } 46932393Syz155240 46942393Syz155240 46952393Syz155240 #if defined(__OpenBSD__) 46962393Syz155240 /* ------------------------------------------------------------------------ */ 46972393Syz155240 /* Function: nat_ifdetach */ 46982393Syz155240 /* Returns: Nil */ 46992393Syz155240 /* Parameters: ifp(I) - pointer to network interface */ 47002393Syz155240 /* */ 47012393Syz155240 /* Compatibility interface for OpenBSD to trigger the correct updating of */ 47022393Syz155240 /* interface references within IPFilter. */ 47032393Syz155240 /* ------------------------------------------------------------------------ */ 47043448Sdh155122 void nat_ifdetach(ifp, ifs) 47052393Syz155240 void *ifp; 47063448Sdh155122 ipf_stack_t *ifs; 47072393Syz155240 { 47083448Sdh155122 frsync(ifp, ifs); 47092393Syz155240 return; 47102393Syz155240 } 47112393Syz155240 #endif 47122393Syz155240 47132393Syz155240 47142393Syz155240 /* ------------------------------------------------------------------------ */ 47153448Sdh155122 /* Function: fr_ipnatderef */ 47163448Sdh155122 /* Returns: Nil */ 47173448Sdh155122 /* Parameters: isp(I) - pointer to pointer to NAT rule */ 47183448Sdh155122 /* Write Locks: ipf_nat */ 47193448Sdh155122 /* */ 47203448Sdh155122 /* ------------------------------------------------------------------------ */ 47213448Sdh155122 void fr_ipnatderef(inp, ifs) 47223448Sdh155122 ipnat_t **inp; 47233448Sdh155122 ipf_stack_t *ifs; 47243448Sdh155122 { 47253448Sdh155122 ipnat_t *in; 47263448Sdh155122 47273448Sdh155122 in = *inp; 47283448Sdh155122 *inp = NULL; 47293448Sdh155122 in->in_space++; 47303448Sdh155122 in->in_use--; 47313448Sdh155122 if (in->in_use == 0 && (in->in_flags & IPN_DELETE)) { 47323448Sdh155122 if (in->in_apr) 47333448Sdh155122 appr_free(in->in_apr); 47343448Sdh155122 KFREE(in); 47353448Sdh155122 ifs->ifs_nat_stats.ns_rules--; 47363448Sdh155122 #ifdef notdef 47373448Sdh155122 #if SOLARIS 47383448Sdh155122 if (ifs->ifs_nat_stats.ns_rules == 0) 47393448Sdh155122 ifs->ifs_pfil_delayed_copy = 1; 47403448Sdh155122 #endif 47413448Sdh155122 #endif 47423448Sdh155122 } 47433448Sdh155122 } 47443448Sdh155122 47453448Sdh155122 47463448Sdh155122 /* ------------------------------------------------------------------------ */ 47472393Syz155240 /* Function: fr_natderef */ 47482393Syz155240 /* Returns: Nil */ 47492393Syz155240 /* Parameters: isp(I) - pointer to pointer to NAT table entry */ 47502393Syz155240 /* */ 47512393Syz155240 /* Decrement the reference counter for this NAT table entry and free it if */ 47522393Syz155240 /* there are no more things using it. */ 4753*5055Sdr146992 /* */ 4754*5055Sdr146992 /* IF nat_ref == 1 when this function is called, then we have an orphan nat */ 4755*5055Sdr146992 /* structure *because* it only gets called on paths _after_ nat_ref has been*/ 4756*5055Sdr146992 /* incremented. If nat_ref == 1 then we shouldn't decrement it here */ 4757*5055Sdr146992 /* because nat_delete() will do that and send nat_ref to -1. */ 4758*5055Sdr146992 /* */ 4759*5055Sdr146992 /* Holding the lock on nat_lock is required to serialise nat_delete() being */ 4760*5055Sdr146992 /* called from a NAT flush ioctl with a deref happening because of a packet.*/ 47612393Syz155240 /* ------------------------------------------------------------------------ */ 47623448Sdh155122 void fr_natderef(natp, ifs) 47632393Syz155240 nat_t **natp; 47643448Sdh155122 ipf_stack_t *ifs; 47652393Syz155240 { 47662393Syz155240 nat_t *nat; 47672393Syz155240 47682393Syz155240 nat = *natp; 47692393Syz155240 *natp = NULL; 4770*5055Sdr146992 4771*5055Sdr146992 MUTEX_ENTER(&nat->nat_lock); 4772*5055Sdr146992 if (nat->nat_ref > 1) { 4773*5055Sdr146992 nat->nat_ref--; 4774*5055Sdr146992 MUTEX_EXIT(&nat->nat_lock); 4775*5055Sdr146992 return; 4776*5055Sdr146992 } 4777*5055Sdr146992 MUTEX_EXIT(&nat->nat_lock); 4778*5055Sdr146992 47793448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 4780*5055Sdr146992 nat_delete(nat, NL_EXPIRE, ifs); 47813448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 47822393Syz155240 } 47832393Syz155240 47842393Syz155240 47852393Syz155240 /* ------------------------------------------------------------------------ */ 47862393Syz155240 /* Function: fr_natclone */ 47872393Syz155240 /* Returns: ipstate_t* - NULL == cloning failed, */ 47882393Syz155240 /* else pointer to new state structure */ 47892393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 47902393Syz155240 /* is(I) - pointer to master state structure */ 47912393Syz155240 /* Write Lock: ipf_nat */ 47922393Syz155240 /* */ 47932393Syz155240 /* Create a "duplcate" state table entry from the master. */ 47942393Syz155240 /* ------------------------------------------------------------------------ */ 47952393Syz155240 static nat_t *fr_natclone(fin, nat) 47962393Syz155240 fr_info_t *fin; 47972393Syz155240 nat_t *nat; 47982393Syz155240 { 47992393Syz155240 frentry_t *fr; 48002393Syz155240 nat_t *clone; 48012393Syz155240 ipnat_t *np; 48023448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 48032393Syz155240 48042393Syz155240 KMALLOC(clone, nat_t *); 48052393Syz155240 if (clone == NULL) 48062393Syz155240 return NULL; 48072393Syz155240 bcopy((char *)nat, (char *)clone, sizeof(*clone)); 48082393Syz155240 48092393Syz155240 MUTEX_NUKE(&clone->nat_lock); 48102393Syz155240 48112393Syz155240 clone->nat_aps = NULL; 48122393Syz155240 /* 48132393Syz155240 * Initialize all these so that nat_delete() doesn't cause a crash. 48142393Syz155240 */ 48152393Syz155240 clone->nat_tqe.tqe_pnext = NULL; 48162393Syz155240 clone->nat_tqe.tqe_next = NULL; 48172393Syz155240 clone->nat_tqe.tqe_ifq = NULL; 48182393Syz155240 clone->nat_tqe.tqe_parent = clone; 48192393Syz155240 48202393Syz155240 clone->nat_flags &= ~SI_CLONE; 48212393Syz155240 clone->nat_flags |= SI_CLONED; 48222393Syz155240 48232393Syz155240 if (clone->nat_hm) 48242393Syz155240 clone->nat_hm->hm_ref++; 48252393Syz155240 48263448Sdh155122 if (nat_insert(clone, fin->fin_rev, ifs) == -1) { 48272393Syz155240 KFREE(clone); 48282393Syz155240 return NULL; 48292393Syz155240 } 48302393Syz155240 np = clone->nat_ptr; 48312393Syz155240 if (np != NULL) { 48323448Sdh155122 if (ifs->ifs_nat_logging) 48333448Sdh155122 nat_log(clone, (u_int)np->in_redir, ifs); 48342393Syz155240 np->in_use++; 48352393Syz155240 } 48362393Syz155240 fr = clone->nat_fr; 48372393Syz155240 if (fr != NULL) { 48382393Syz155240 MUTEX_ENTER(&fr->fr_lock); 48392393Syz155240 fr->fr_ref++; 48402393Syz155240 MUTEX_EXIT(&fr->fr_lock); 48412393Syz155240 } 48422393Syz155240 48432393Syz155240 /* 48442393Syz155240 * Because the clone is created outside the normal loop of things and 48452393Syz155240 * TCP has special needs in terms of state, initialise the timeout 48462393Syz155240 * state of the new NAT from here. 48472393Syz155240 */ 48482393Syz155240 if (clone->nat_p == IPPROTO_TCP) { 48493448Sdh155122 (void) fr_tcp_age(&clone->nat_tqe, fin, ifs->ifs_nat_tqb, 48502393Syz155240 clone->nat_flags); 48512393Syz155240 } 48522393Syz155240 #ifdef IPFILTER_SYNC 48532393Syz155240 clone->nat_sync = ipfsync_new(SMC_NAT, fin, clone); 48542393Syz155240 #endif 48553448Sdh155122 if (ifs->ifs_nat_logging) 48563448Sdh155122 nat_log(clone, NL_CLONE, ifs); 48572393Syz155240 return clone; 48582393Syz155240 } 48592393Syz155240 48602393Syz155240 48612393Syz155240 /* ------------------------------------------------------------------------ */ 48622393Syz155240 /* Function: nat_wildok */ 48632393Syz155240 /* Returns: int - 1 == packet's ports match wildcards */ 48642393Syz155240 /* 0 == packet's ports don't match wildcards */ 48652393Syz155240 /* Parameters: nat(I) - NAT entry */ 48662393Syz155240 /* sport(I) - source port */ 48672393Syz155240 /* dport(I) - destination port */ 48682393Syz155240 /* flags(I) - wildcard flags */ 48692393Syz155240 /* dir(I) - packet direction */ 48702393Syz155240 /* */ 48712393Syz155240 /* Use NAT entry and packet direction to determine which combination of */ 48722393Syz155240 /* wildcard flags should be used. */ 48732393Syz155240 /* ------------------------------------------------------------------------ */ 48742393Syz155240 static INLINE int nat_wildok(nat, sport, dport, flags, dir) 48752393Syz155240 nat_t *nat; 48762393Syz155240 int sport; 48772393Syz155240 int dport; 48782393Syz155240 int flags; 48792393Syz155240 int dir; 48802393Syz155240 { 48812393Syz155240 /* 48822393Syz155240 * When called by dir is set to 48832393Syz155240 * nat_inlookup NAT_INBOUND (0) 48842393Syz155240 * nat_outlookup NAT_OUTBOUND (1) 48852393Syz155240 * 48862393Syz155240 * We simply combine the packet's direction in dir with the original 48872393Syz155240 * "intended" direction of that NAT entry in nat->nat_dir to decide 48882393Syz155240 * which combination of wildcard flags to allow. 48892393Syz155240 */ 48902393Syz155240 48912393Syz155240 switch ((dir << 1) | nat->nat_dir) 48922393Syz155240 { 48932393Syz155240 case 3: /* outbound packet / outbound entry */ 48942393Syz155240 if (((nat->nat_inport == sport) || 48952393Syz155240 (flags & SI_W_SPORT)) && 48962393Syz155240 ((nat->nat_oport == dport) || 48972393Syz155240 (flags & SI_W_DPORT))) 48982393Syz155240 return 1; 48992393Syz155240 break; 49002393Syz155240 case 2: /* outbound packet / inbound entry */ 49012393Syz155240 if (((nat->nat_outport == sport) || 49022393Syz155240 (flags & SI_W_DPORT)) && 49032393Syz155240 ((nat->nat_oport == dport) || 49042393Syz155240 (flags & SI_W_SPORT))) 49052393Syz155240 return 1; 49062393Syz155240 break; 49072393Syz155240 case 1: /* inbound packet / outbound entry */ 49082393Syz155240 if (((nat->nat_oport == sport) || 49092393Syz155240 (flags & SI_W_DPORT)) && 49102393Syz155240 ((nat->nat_outport == dport) || 49112393Syz155240 (flags & SI_W_SPORT))) 49122393Syz155240 return 1; 49132393Syz155240 break; 49142393Syz155240 case 0: /* inbound packet / inbound entry */ 49152393Syz155240 if (((nat->nat_oport == sport) || 49162393Syz155240 (flags & SI_W_SPORT)) && 49172393Syz155240 ((nat->nat_outport == dport) || 49182393Syz155240 (flags & SI_W_DPORT))) 49192393Syz155240 return 1; 49202393Syz155240 break; 49212393Syz155240 default: 49222393Syz155240 break; 49232393Syz155240 } 49242393Syz155240 49252393Syz155240 return(0); 49262393Syz155240 } 49272393Syz155240 49282393Syz155240 49292393Syz155240 /* ------------------------------------------------------------------------ */ 49302393Syz155240 /* Function: nat_mssclamp */ 49312393Syz155240 /* Returns: Nil */ 49322393Syz155240 /* Parameters: tcp(I) - pointer to TCP header */ 49332393Syz155240 /* maxmss(I) - value to clamp the TCP MSS to */ 49342393Syz155240 /* csump(I) - pointer to TCP checksum */ 49352393Syz155240 /* */ 49362393Syz155240 /* Check for MSS option and clamp it if necessary. If found and changed, */ 49372393Syz155240 /* then the TCP header checksum will be updated to reflect the change in */ 49382393Syz155240 /* the MSS. */ 49392393Syz155240 /* ------------------------------------------------------------------------ */ 49402958Sdr146992 static void nat_mssclamp(tcp, maxmss, csump) 49412393Syz155240 tcphdr_t *tcp; 49422393Syz155240 u_32_t maxmss; 49432393Syz155240 u_short *csump; 49442393Syz155240 { 49452393Syz155240 u_char *cp, *ep, opt; 49462393Syz155240 int hlen, advance; 49472393Syz155240 u_32_t mss, sumd; 49482393Syz155240 49492393Syz155240 hlen = TCP_OFF(tcp) << 2; 49502393Syz155240 if (hlen > sizeof(*tcp)) { 49512393Syz155240 cp = (u_char *)tcp + sizeof(*tcp); 49522393Syz155240 ep = (u_char *)tcp + hlen; 49532393Syz155240 49542393Syz155240 while (cp < ep) { 49552393Syz155240 opt = cp[0]; 49562393Syz155240 if (opt == TCPOPT_EOL) 49572393Syz155240 break; 49582393Syz155240 else if (opt == TCPOPT_NOP) { 49592393Syz155240 cp++; 49602393Syz155240 continue; 49612393Syz155240 } 49622393Syz155240 49632393Syz155240 if (cp + 1 >= ep) 49642393Syz155240 break; 49652393Syz155240 advance = cp[1]; 49662393Syz155240 if ((cp + advance > ep) || (advance <= 0)) 49672393Syz155240 break; 49682393Syz155240 switch (opt) 49692393Syz155240 { 49702393Syz155240 case TCPOPT_MAXSEG: 49712393Syz155240 if (advance != 4) 49722393Syz155240 break; 49732393Syz155240 mss = cp[2] * 256 + cp[3]; 49742393Syz155240 if (mss > maxmss) { 49752393Syz155240 cp[2] = maxmss / 256; 49762393Syz155240 cp[3] = maxmss & 0xff; 49772393Syz155240 CALC_SUMD(mss, maxmss, sumd); 49782958Sdr146992 fix_outcksum(csump, sumd); 49792393Syz155240 } 49802393Syz155240 break; 49812393Syz155240 default: 49822393Syz155240 /* ignore unknown options */ 49832393Syz155240 break; 49842393Syz155240 } 49852393Syz155240 49862393Syz155240 cp += advance; 49872393Syz155240 } 49882393Syz155240 } 49892393Syz155240 } 49902393Syz155240 49912393Syz155240 49922393Syz155240 /* ------------------------------------------------------------------------ */ 49932393Syz155240 /* Function: fr_setnatqueue */ 49942393Syz155240 /* Returns: Nil */ 49952393Syz155240 /* Parameters: nat(I)- pointer to NAT structure */ 49962393Syz155240 /* rev(I) - forward(0) or reverse(1) direction */ 49972393Syz155240 /* Locks: ipf_nat (read or write) */ 49982393Syz155240 /* */ 49992393Syz155240 /* Put the NAT entry on its default queue entry, using rev as a helped in */ 50002393Syz155240 /* determining which queue it should be placed on. */ 50012393Syz155240 /* ------------------------------------------------------------------------ */ 50023448Sdh155122 void fr_setnatqueue(nat, rev, ifs) 50032393Syz155240 nat_t *nat; 50042393Syz155240 int rev; 50053448Sdh155122 ipf_stack_t *ifs; 50062393Syz155240 { 50072393Syz155240 ipftq_t *oifq, *nifq; 50082393Syz155240 50092393Syz155240 if (nat->nat_ptr != NULL) 50102393Syz155240 nifq = nat->nat_ptr->in_tqehead[rev]; 50112393Syz155240 else 50122393Syz155240 nifq = NULL; 50132393Syz155240 50142393Syz155240 if (nifq == NULL) { 50152393Syz155240 switch (nat->nat_p) 50162393Syz155240 { 50172393Syz155240 case IPPROTO_UDP : 50183448Sdh155122 nifq = &ifs->ifs_nat_udptq; 50192393Syz155240 break; 50202393Syz155240 case IPPROTO_ICMP : 50213448Sdh155122 nifq = &ifs->ifs_nat_icmptq; 50222393Syz155240 break; 50232393Syz155240 case IPPROTO_TCP : 50243448Sdh155122 nifq = ifs->ifs_nat_tqb + nat->nat_tqe.tqe_state[rev]; 50252393Syz155240 break; 50262393Syz155240 default : 50273448Sdh155122 nifq = &ifs->ifs_nat_iptq; 50282393Syz155240 break; 50292393Syz155240 } 50302393Syz155240 } 50312393Syz155240 50322393Syz155240 oifq = nat->nat_tqe.tqe_ifq; 50332393Syz155240 /* 50342393Syz155240 * If it's currently on a timeout queue, move it from one queue to 50352393Syz155240 * another, else put it on the end of the newly determined queue. 50362393Syz155240 */ 50372393Syz155240 if (oifq != NULL) 50383448Sdh155122 fr_movequeue(&nat->nat_tqe, oifq, nifq, ifs); 50392393Syz155240 else 50403448Sdh155122 fr_queueappend(&nat->nat_tqe, nifq, nat, ifs); 50412393Syz155240 return; 50422393Syz155240 } 50433448Sdh155122 50443448Sdh155122 /* Function: nat_getnext */ 50453448Sdh155122 /* Returns: int - 0 == ok, else error */ 50463448Sdh155122 /* Parameters: t(I) - pointer to ipftoken structure */ 50473448Sdh155122 /* itp(I) - pointer to ipfgeniter_t structure */ 50483448Sdh155122 /* */ 50493448Sdh155122 /* Fetch the next nat/ipnat structure pointer from the linked list and */ 50503448Sdh155122 /* copy it out to the storage space pointed to by itp_data. The next item */ 50513448Sdh155122 /* in the list to look at is put back in the ipftoken struture. */ 50523448Sdh155122 /* If we call ipf_freetoken, the accompanying pointer is set to NULL because*/ 50533448Sdh155122 /* ipf_freetoken will call a deref function for us and we dont want to call */ 50543448Sdh155122 /* that twice (second time would be in the second switch statement below. */ 50553448Sdh155122 /* ------------------------------------------------------------------------ */ 50563448Sdh155122 static int nat_getnext(t, itp, ifs) 50573448Sdh155122 ipftoken_t *t; 50583448Sdh155122 ipfgeniter_t *itp; 50593448Sdh155122 ipf_stack_t *ifs; 50603448Sdh155122 { 50613448Sdh155122 hostmap_t *hm, *nexthm = NULL, zerohm; 50623448Sdh155122 ipnat_t *ipn, *nextipnat = NULL, zeroipn; 50633448Sdh155122 nat_t *nat, *nextnat = NULL, zeronat; 50643448Sdh155122 int error = 0; 50653448Sdh155122 50663448Sdh155122 READ_ENTER(&ifs->ifs_ipf_nat); 50673448Sdh155122 switch (itp->igi_type) 50683448Sdh155122 { 50693448Sdh155122 case IPFGENITER_HOSTMAP : 50703448Sdh155122 hm = t->ipt_data; 50713448Sdh155122 if (hm == NULL) { 50723448Sdh155122 nexthm = ifs->ifs_ipf_hm_maplist; 50733448Sdh155122 } else { 50743448Sdh155122 nexthm = hm->hm_hnext; 50753448Sdh155122 } 50763448Sdh155122 if (nexthm != NULL) { 50773448Sdh155122 if (nexthm->hm_hnext == NULL) { 50783448Sdh155122 t->ipt_alive = 0; 50793796Szf203873 ipf_unlinktoken(t, ifs); 50803796Szf203873 KFREE(t); 50813448Sdh155122 } else { 50823448Sdh155122 /*MUTEX_ENTER(&nexthm->hm_lock);*/ 50833448Sdh155122 nexthm->hm_ref++; 50843448Sdh155122 /*MUTEX_EXIT(&nextipnat->hm_lock);*/ 50853448Sdh155122 } 50863448Sdh155122 50873448Sdh155122 } else { 50883448Sdh155122 bzero(&zerohm, sizeof(zerohm)); 50893448Sdh155122 nexthm = &zerohm; 50903796Szf203873 ipf_freetoken(t, ifs); 50913448Sdh155122 } 50923448Sdh155122 break; 50933448Sdh155122 50943448Sdh155122 case IPFGENITER_IPNAT : 50953448Sdh155122 ipn = t->ipt_data; 50963448Sdh155122 if (ipn == NULL) { 50973448Sdh155122 nextipnat = ifs->ifs_nat_list; 50983448Sdh155122 } else { 50993448Sdh155122 nextipnat = ipn->in_next; 51003448Sdh155122 } 51013448Sdh155122 if (nextipnat != NULL) { 51023448Sdh155122 if (nextipnat->in_next == NULL) { 51033448Sdh155122 t->ipt_alive = 0; 51043796Szf203873 ipf_unlinktoken(t, ifs); 51053796Szf203873 KFREE(t); 51063448Sdh155122 } else { 51073448Sdh155122 /* MUTEX_ENTER(&nextipnat->in_lock); */ 51083448Sdh155122 nextipnat->in_use++; 51093448Sdh155122 /* MUTEX_EXIT(&nextipnat->in_lock); */ 51103448Sdh155122 } 51113448Sdh155122 } else { 51123448Sdh155122 bzero(&zeroipn, sizeof(zeroipn)); 51133448Sdh155122 nextipnat = &zeroipn; 51143796Szf203873 ipf_freetoken(t, ifs); 51153448Sdh155122 } 51163448Sdh155122 break; 51173448Sdh155122 51183448Sdh155122 case IPFGENITER_NAT : 51193448Sdh155122 nat = t->ipt_data; 51203448Sdh155122 if (nat == NULL) { 51213448Sdh155122 nextnat = ifs->ifs_nat_instances; 51223448Sdh155122 } else { 51233448Sdh155122 nextnat = nat->nat_next; 51243448Sdh155122 } 51253448Sdh155122 if (nextnat != NULL) { 51263448Sdh155122 if (nextnat->nat_next == NULL) { 51273448Sdh155122 t->ipt_alive = 0; 51283796Szf203873 ipf_unlinktoken(t, ifs); 51293796Szf203873 KFREE(t); 51303448Sdh155122 } else { 51313448Sdh155122 MUTEX_ENTER(&nextnat->nat_lock); 51323448Sdh155122 nextnat->nat_ref++; 51333448Sdh155122 MUTEX_EXIT(&nextnat->nat_lock); 51343448Sdh155122 } 51353448Sdh155122 } else { 51363448Sdh155122 bzero(&zeronat, sizeof(zeronat)); 51373448Sdh155122 nextnat = &zeronat; 51383796Szf203873 ipf_freetoken(t, ifs); 51393448Sdh155122 } 51403448Sdh155122 break; 51413448Sdh155122 } 51423448Sdh155122 51433448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 51443448Sdh155122 51453448Sdh155122 switch (itp->igi_type) 51463448Sdh155122 { 51473448Sdh155122 case IPFGENITER_HOSTMAP : 51483448Sdh155122 if (hm != NULL) { 51493448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_nat); 51503448Sdh155122 fr_hostmapderef(&hm); 51513448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 51523448Sdh155122 } 51533796Szf203873 if (nexthm->hm_hnext != NULL) 51543796Szf203873 t->ipt_data = nexthm; 51553448Sdh155122 error = COPYOUT(nexthm, itp->igi_data, sizeof(*nexthm)); 51563448Sdh155122 if (error != 0) 51573448Sdh155122 error = EFAULT; 51583448Sdh155122 break; 51593448Sdh155122 51603448Sdh155122 case IPFGENITER_IPNAT : 51613448Sdh155122 if (ipn != NULL) 51623448Sdh155122 fr_ipnatderef(&ipn, ifs); 51633796Szf203873 if (nextipnat->in_next != NULL) 51643796Szf203873 t->ipt_data = nextipnat; 51653448Sdh155122 error = COPYOUT(nextipnat, itp->igi_data, sizeof(*nextipnat)); 51663448Sdh155122 if (error != 0) 51673448Sdh155122 error = EFAULT; 51683448Sdh155122 break; 51693448Sdh155122 51703448Sdh155122 case IPFGENITER_NAT : 51713448Sdh155122 if (nat != NULL) 51723448Sdh155122 fr_natderef(&nat, ifs); 51733796Szf203873 if (nextnat->nat_next != NULL) 51743796Szf203873 t->ipt_data = nextnat; 51753448Sdh155122 error = COPYOUT(nextnat, itp->igi_data, sizeof(*nextnat)); 51763448Sdh155122 if (error != 0) 51773448Sdh155122 error = EFAULT; 51783448Sdh155122 break; 51793448Sdh155122 } 51803448Sdh155122 51813448Sdh155122 return error; 51823448Sdh155122 } 51833448Sdh155122 51843448Sdh155122 51853448Sdh155122 /* ------------------------------------------------------------------------ */ 51863448Sdh155122 /* Function: nat_iterator */ 51873448Sdh155122 /* Returns: int - 0 == ok, else error */ 51883448Sdh155122 /* Parameters: token(I) - pointer to ipftoken structure */ 51893448Sdh155122 /* itp(I) - pointer to ipfgeniter_t structure */ 51903448Sdh155122 /* */ 51913448Sdh155122 /* This function acts as a handler for the SIOCGENITER ioctls that use a */ 51923448Sdh155122 /* generic structure to iterate through a list. There are three different */ 51933448Sdh155122 /* linked lists of NAT related information to go through: NAT rules, active */ 51943448Sdh155122 /* NAT mappings and the NAT fragment cache. */ 51953448Sdh155122 /* ------------------------------------------------------------------------ */ 51963448Sdh155122 static int nat_iterator(token, itp, ifs) 51973448Sdh155122 ipftoken_t *token; 51983448Sdh155122 ipfgeniter_t *itp; 51993448Sdh155122 ipf_stack_t *ifs; 52003448Sdh155122 { 52013448Sdh155122 int error; 52023448Sdh155122 52033448Sdh155122 if (itp->igi_data == NULL) 52043448Sdh155122 return EFAULT; 52053448Sdh155122 52063448Sdh155122 token->ipt_subtype = itp->igi_type; 52073448Sdh155122 52083448Sdh155122 switch (itp->igi_type) 52093448Sdh155122 { 52103448Sdh155122 case IPFGENITER_HOSTMAP : 52113448Sdh155122 case IPFGENITER_IPNAT : 52123448Sdh155122 case IPFGENITER_NAT : 52133448Sdh155122 error = nat_getnext(token, itp, ifs); 52143448Sdh155122 break; 52153448Sdh155122 case IPFGENITER_NATFRAG : 52163448Sdh155122 error = fr_nextfrag(token, itp, &ifs->ifs_ipfr_natlist, 52173448Sdh155122 &ifs->ifs_ipfr_nattail, 52183448Sdh155122 &ifs->ifs_ipf_natfrag, ifs); 52193448Sdh155122 break; 52203448Sdh155122 default : 52213448Sdh155122 error = EINVAL; 52223448Sdh155122 break; 52233448Sdh155122 } 52243448Sdh155122 52253448Sdh155122 return error; 52263448Sdh155122 } 52274817San207044 52284817San207044 52294817San207044 /* -------------------------------------------------------------------- */ 52304817San207044 /* Function: nat_earlydrop */ 52314817San207044 /* Returns: number of dropped/removed entries from the queue */ 52324817San207044 /* Parameters: ifq - pointer to queue with entries to be processed */ 52334817San207044 /* maxidle - entry must be idle this long to be dropped */ 52344817San207044 /* ifs - ipf stack instance */ 52354817San207044 /* */ 52364817San207044 /* Function is invoked from nat_extraflush() only. Removes entries */ 52374817San207044 /* form specified timeout queue, based on how long they've sat idle, */ 52384817San207044 /* without waiting for it to happen on its own. */ 52394817San207044 /* -------------------------------------------------------------------- */ 52404817San207044 static int nat_earlydrop(ifq, maxidle, ifs) 52414817San207044 ipftq_t *ifq; 52424817San207044 int maxidle; 52434817San207044 ipf_stack_t *ifs; 52444817San207044 { 52454817San207044 ipftqent_t *tqe, *tqn; 52464817San207044 nat_t *nat; 52474817San207044 unsigned int dropped; 52484817San207044 int droptick; 52494817San207044 52504817San207044 if (ifq == NULL) 52514817San207044 return (0); 52524817San207044 52534817San207044 dropped = 0; 52544817San207044 52554817San207044 /* 52564817San207044 * Determine the tick representing the idle time we're interested 52574817San207044 * in. If an entry exists in the queue, and it was touched before 52584817San207044 * that tick, then it's been idle longer than maxidle ... remove it. 52594817San207044 */ 52604817San207044 droptick = ifs->ifs_fr_ticks - maxidle; 52614817San207044 tqn = ifq->ifq_head; 52624817San207044 while ((tqe = tqn) != NULL && tqe->tqe_touched < droptick) { 52634817San207044 tqn = tqe->tqe_next; 52644817San207044 nat = tqe->tqe_parent; 52654817San207044 nat_delete(nat, ISL_EXPIRE, ifs); 52664817San207044 dropped++; 52674817San207044 } 52684817San207044 return (dropped); 52694817San207044 } 52704817San207044 52714817San207044 52724817San207044 /* --------------------------------------------------------------------- */ 52734817San207044 /* Function: nat_flushclosing */ 52744817San207044 /* Returns: int - number of NAT entries deleted */ 52754817San207044 /* Parameters: stateval(I) - State at which to start removing entries */ 52764817San207044 /* ifs - ipf stack instance */ 52774817San207044 /* */ 52784817San207044 /* Remove nat table entries for TCP connections which are in the process */ 52794817San207044 /* of closing, and are in (or "beyond") state specified by 'stateval'. */ 52804817San207044 /* --------------------------------------------------------------------- */ 52814817San207044 static int nat_flushclosing(stateval, ifs) 52824817San207044 int stateval; 52834817San207044 ipf_stack_t *ifs; 52844817San207044 { 52854817San207044 ipftq_t *ifq, *ifqn; 52864817San207044 ipftqent_t *tqe, *tqn; 52874817San207044 nat_t *nat; 52884817San207044 int dropped; 52894817San207044 52904817San207044 dropped = 0; 52914817San207044 52924817San207044 /* 52934817San207044 * Start by deleting any entries in specific timeout queues. 52944817San207044 */ 52954817San207044 ifqn = &ifs->ifs_nat_tqb[stateval]; 52964817San207044 while ((ifq = ifqn) != NULL) { 52974817San207044 ifqn = ifq->ifq_next; 52984817San207044 dropped += nat_earlydrop(ifq, (int)0, ifs); 52994817San207044 } 53004817San207044 53014817San207044 /* 53024817San207044 * Next, look through user defined queues for closing entries. 53034817San207044 */ 53044817San207044 ifqn = ifs->ifs_nat_utqe; 53054817San207044 while ((ifq = ifqn) != NULL) { 53064817San207044 ifqn = ifq->ifq_next; 53074817San207044 tqn = ifq->ifq_head; 53084817San207044 while ((tqe = tqn) != NULL) { 53094817San207044 tqn = tqe->tqe_next; 53104817San207044 nat = tqe->tqe_parent; 53114817San207044 if (nat->nat_p != IPPROTO_TCP) 53124817San207044 continue; 53134817San207044 if ((nat->nat_tcpstate[0] >= stateval) && 53144817San207044 (nat->nat_tcpstate[1] >= stateval)) { 53154817San207044 nat_delete(nat, NL_EXPIRE, ifs); 53164817San207044 dropped++; 53174817San207044 } 53184817San207044 } 53194817San207044 } 53204817San207044 return (dropped); 53214817San207044 } 53224817San207044 53234817San207044 53244817San207044 /* --------------------------------------------------------------------- */ 53254817San207044 /* Function: nat_extraflush */ 53264817San207044 /* Returns: int - number of NAT entries deleted */ 53274817San207044 /* Parameters: which(I) - how to flush the active NAT table */ 53284817San207044 /* ifs - ipf stack instance */ 53294817San207044 /* Write Locks: ipf_nat */ 53304817San207044 /* */ 53314817San207044 /* Flush nat tables. Three actions currently defined: */ 53324817San207044 /* */ 53334817San207044 /* which == 0 : Flush all nat table entries. */ 53344817San207044 /* */ 53354817San207044 /* which == 1 : Flush entries with TCP connections which have started */ 53364817San207044 /* to close on both ends. */ 53374817San207044 /* */ 53384817San207044 /* which == 2 : First, flush entries which are "almost" closed. If that */ 53394817San207044 /* does not take us below specified threshold in the table, */ 53404817San207044 /* we want to flush entries with TCP connections which have */ 53414817San207044 /* been idle for a long time. Start with connections idle */ 53424817San207044 /* over 12 hours, and then work backwards in half hour */ 53434817San207044 /* increments to at most 30 minutes idle, and finally work */ 53444817San207044 /* back in 30 second increments to at most 30 seconds. */ 53454817San207044 /* --------------------------------------------------------------------- */ 53464817San207044 static int nat_extraflush(which, ifs) 53474817San207044 int which; 53484817San207044 ipf_stack_t *ifs; 53494817San207044 { 53504817San207044 ipftq_t *ifq, *ifqn; 53514817San207044 nat_t *nat, **natp; 53524817San207044 int idletime, removed, idle_idx; 53534817San207044 SPL_INT(s); 53544817San207044 53554817San207044 removed = 0; 53564817San207044 53574817San207044 SPL_NET(s); 53584817San207044 switch (which) 53594817San207044 { 53604817San207044 case 0: 53614817San207044 natp = &ifs->ifs_nat_instances; 53624817San207044 while ((nat = *natp) != NULL) { 53634817San207044 natp = &nat->nat_next; 53644817San207044 nat_delete(nat, ISL_FLUSH, ifs); 53654817San207044 removed++; 53664817San207044 } 53674817San207044 break; 53684817San207044 53694817San207044 case 1: 53704817San207044 removed = nat_flushclosing(IPF_TCPS_CLOSE_WAIT, ifs); 53714817San207044 break; 53724817San207044 53734817San207044 case 2: 53744817San207044 removed = nat_flushclosing(IPF_TCPS_FIN_WAIT_2, ifs); 53754817San207044 53764817San207044 /* 53774817San207044 * Be sure we haven't done this in the last 10 seconds. 53784817San207044 */ 53794817San207044 if (ifs->ifs_fr_ticks - ifs->ifs_nat_last_force_flush < 53804817San207044 IPF_TTLVAL(10)) 53814817San207044 break; 53824817San207044 ifs->ifs_nat_last_force_flush = ifs->ifs_fr_ticks; 53834817San207044 53844817San207044 /* 53854817San207044 * Determine initial threshold for minimum idle time based on 53864817San207044 * how long ipfilter has been running. Ipfilter needs to have 53874817San207044 * been up as long as the smallest interval to continue on. 53884817San207044 * 53894817San207044 * Minimum idle times stored in idletime_tab and indexed by 53904817San207044 * idle_idx. Start at upper end of array and work backwards. 53914817San207044 * 53924817San207044 * Once the index is found, set the initial idle time to the 53934817San207044 * first interval before the current ipfilter run time. 53944817San207044 */ 53954817San207044 if (ifs->ifs_fr_ticks < idletime_tab[0]) 53964817San207044 break; /* switch */ 53974817San207044 idle_idx = (sizeof (idletime_tab) / sizeof (int)) - 1; 53984817San207044 if (ifs->ifs_fr_ticks > idletime_tab[idle_idx]) { 53994817San207044 idletime = idletime_tab[idle_idx]; 54004817San207044 } else { 54014817San207044 while ((idle_idx > 0) && 54024817San207044 (ifs->ifs_fr_ticks < idletime_tab[idle_idx])) 54034817San207044 idle_idx--; 54044817San207044 idletime = (ifs->ifs_fr_ticks / 54054817San207044 idletime_tab[idle_idx]) * 54064817San207044 idletime_tab[idle_idx]; 54074817San207044 } 54084817San207044 54094817San207044 while ((idle_idx >= 0) && 54104817San207044 (NAT_TAB_WATER_LEVEL(ifs) > ifs->ifs_nat_flush_lvl_lo)) { 54114817San207044 /* 54124817San207044 * Start with appropriate timeout queue. 54134817San207044 */ 54144817San207044 removed += nat_earlydrop( 54154817San207044 &ifs->ifs_nat_tqb[IPF_TCPS_ESTABLISHED], 54164817San207044 idletime, ifs); 54174817San207044 54184817San207044 /* 54194817San207044 * Make sure we haven't already deleted enough 54204817San207044 * entries before checking the user defined queues. 54214817San207044 */ 54224817San207044 if (NAT_TAB_WATER_LEVEL(ifs) <= 54234817San207044 ifs->ifs_nat_flush_lvl_lo) 54244817San207044 break; 54254817San207044 54264817San207044 /* 54274817San207044 * Next, look through the user defined queues. 54284817San207044 */ 54294817San207044 ifqn = ifs->ifs_nat_utqe; 54304817San207044 while ((ifq = ifqn) != NULL) { 54314817San207044 ifqn = ifq->ifq_next; 54324817San207044 removed += nat_earlydrop(ifq, idletime, ifs); 54334817San207044 } 54344817San207044 54354817San207044 /* 54364817San207044 * Adjust the granularity of idle time. 54374817San207044 * 54384817San207044 * If we reach an interval boundary, we need to 54394817San207044 * either adjust the idle time accordingly or exit 54404817San207044 * the loop altogether (if this is very last check). 54414817San207044 */ 54424817San207044 idletime -= idletime_tab[idle_idx]; 54434817San207044 if (idletime < idletime_tab[idle_idx]) { 54444817San207044 if (idle_idx != 0) { 54454817San207044 idletime = idletime_tab[idle_idx] - 54464817San207044 idletime_tab[idle_idx - 1]; 54474817San207044 idle_idx--; 54484817San207044 } else { 54494817San207044 break; /* while */ 54504817San207044 } 54514817San207044 } 54524817San207044 } 54534817San207044 break; 54544817San207044 default: 54554817San207044 break; 54564817San207044 } 54574817San207044 54584817San207044 SPL_X(s); 54594817San207044 return (removed); 54604817San207044 } 5461