12393Syz155240 /* 22393Syz155240 * Copyright (C) 1995-2003 by Darren Reed. 32393Syz155240 * 42393Syz155240 * See the IPFILTER.LICENCE file for details on licencing. 52393Syz155240 * 62393Syz155240 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 72393Syz155240 * Use is subject to license terms. 82393Syz155240 */ 92393Syz155240 102393Syz155240 #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" 1032393Syz155240 #ifdef IPFILTER_SYNC 1042393Syz155240 #include "netinet/ip_sync.h" 1052393Syz155240 #endif 1062393Syz155240 #if (__FreeBSD_version >= 300000) 1072393Syz155240 # include <sys/malloc.h> 1082393Syz155240 #endif 1092393Syz155240 /* END OF INCLUDES */ 1102393Syz155240 1112393Syz155240 #undef SOCKADDR_IN 1122393Syz155240 #define SOCKADDR_IN struct sockaddr_in 1132393Syz155240 1142393Syz155240 #if !defined(lint) 1152393Syz155240 static const char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed"; 1162393Syz155240 static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.195.2.42 2005/08/11 19:51:36 darrenr Exp $"; 1172393Syz155240 #endif 1182393Syz155240 1192393Syz155240 1202393Syz155240 /* ======================================================================== */ 1212393Syz155240 /* How the NAT is organised and works. */ 1222393Syz155240 /* */ 1232393Syz155240 /* Inside (interface y) NAT Outside (interface x) */ 1242393Syz155240 /* -------------------- -+- ------------------------------------- */ 1252393Syz155240 /* Packet going | out, processsed by fr_checknatout() for x */ 1262393Syz155240 /* ------------> | ------------> */ 1272393Syz155240 /* src=10.1.1.1 | src=192.1.1.1 */ 1282393Syz155240 /* | */ 1292393Syz155240 /* | in, processed by fr_checknatin() for x */ 1302393Syz155240 /* <------------ | <------------ */ 1312393Syz155240 /* dst=10.1.1.1 | dst=192.1.1.1 */ 1322393Syz155240 /* -------------------- -+- ------------------------------------- */ 1332393Syz155240 /* fr_checknatout() - changes ip_src and if required, sport */ 1342393Syz155240 /* - creates a new mapping, if required. */ 1352393Syz155240 /* fr_checknatin() - changes ip_dst and if required, dport */ 1362393Syz155240 /* */ 1372393Syz155240 /* In the NAT table, internal source is recorded as "in" and externally */ 1382393Syz155240 /* seen as "out". */ 1392393Syz155240 /* ======================================================================== */ 1402393Syz155240 1412393Syz155240 1422393Syz155240 nat_t **nat_table[2] = { NULL, NULL }, 1432393Syz155240 *nat_instances = NULL; 1442393Syz155240 ipnat_t *nat_list = NULL; 1452393Syz155240 u_int ipf_nattable_max = NAT_TABLE_MAX; 1462393Syz155240 u_int ipf_nattable_sz = NAT_TABLE_SZ; 1472393Syz155240 u_int ipf_natrules_sz = NAT_SIZE; 1482393Syz155240 u_int ipf_rdrrules_sz = RDR_SIZE; 1492393Syz155240 u_int ipf_hostmap_sz = HOSTMAP_SIZE; 1502393Syz155240 u_int fr_nat_maxbucket = 0, 1512393Syz155240 fr_nat_maxbucket_reset = 1; 1522393Syz155240 u_32_t nat_masks = 0; 1532393Syz155240 u_32_t rdr_masks = 0; 1542393Syz155240 ipnat_t **nat_rules = NULL; 1552393Syz155240 ipnat_t **rdr_rules = NULL; 1562393Syz155240 hostmap_t **maptable = NULL; 1572393Syz155240 ipftq_t nat_tqb[IPF_TCP_NSTATES]; 1582393Syz155240 ipftq_t nat_udptq; 1592393Syz155240 ipftq_t nat_icmptq; 1602393Syz155240 ipftq_t nat_iptq; 1612393Syz155240 ipftq_t *nat_utqe = NULL; 1622393Syz155240 #ifdef IPFILTER_LOG 1632393Syz155240 int nat_logging = 1; 1642393Syz155240 #else 1652393Syz155240 int nat_logging = 0; 1662393Syz155240 #endif 1672393Syz155240 1682393Syz155240 u_long fr_defnatage = DEF_NAT_AGE, 1692393Syz155240 fr_defnatipage = 120, /* 60 seconds */ 1702393Syz155240 fr_defnaticmpage = 6; /* 3 seconds */ 1712393Syz155240 natstat_t nat_stats; 1722393Syz155240 int fr_nat_lock = 0; 1732393Syz155240 int fr_nat_init = 0; 1742393Syz155240 #if SOLARIS 1752393Syz155240 extern int pfil_delayed_copy; 1762393Syz155240 #endif 1772393Syz155240 1782393Syz155240 static int nat_flushtable __P((void)); 1792393Syz155240 static int nat_clearlist __P((void)); 1802393Syz155240 static void nat_addnat __P((struct ipnat *)); 1812393Syz155240 static void nat_addrdr __P((struct ipnat *)); 1822393Syz155240 static void nat_delete __P((struct nat *, int)); 1832393Syz155240 static void nat_delrdr __P((struct ipnat *)); 1842393Syz155240 static void nat_delnat __P((struct ipnat *)); 1852393Syz155240 static int fr_natgetent __P((caddr_t)); 1862393Syz155240 static int fr_natgetsz __P((caddr_t)); 1872393Syz155240 static int fr_natputent __P((caddr_t, int)); 1882393Syz155240 static void nat_tabmove __P((nat_t *)); 1892393Syz155240 static int nat_match __P((fr_info_t *, ipnat_t *)); 1902393Syz155240 static INLINE int nat_newmap __P((fr_info_t *, nat_t *, natinfo_t *)); 1912393Syz155240 static INLINE int nat_newrdr __P((fr_info_t *, nat_t *, natinfo_t *)); 1922393Syz155240 static hostmap_t *nat_hostmap __P((ipnat_t *, struct in_addr, 1932393Syz155240 struct in_addr, struct in_addr, u_32_t)); 1942393Syz155240 static void nat_hostmapdel __P((struct hostmap *)); 1952393Syz155240 static INLINE int nat_icmpquerytype4 __P((int)); 1962393Syz155240 static int nat_siocaddnat __P((ipnat_t *, ipnat_t **, int)); 1972393Syz155240 static void nat_siocdelnat __P((ipnat_t *, ipnat_t **, int)); 1982393Syz155240 static INLINE int nat_finalise __P((fr_info_t *, nat_t *, natinfo_t *, 1992393Syz155240 tcphdr_t *, nat_t **, int)); 2002393Syz155240 static void nat_resolverule __P((ipnat_t *)); 2012393Syz155240 static nat_t *fr_natclone __P((fr_info_t *, nat_t *)); 2022393Syz155240 static void nat_mssclamp __P((tcphdr_t *, u_32_t, fr_info_t *, u_short *)); 2032393Syz155240 static INLINE int nat_wildok __P((nat_t *, int, int, int, int)); 2042393Syz155240 2052393Syz155240 2062393Syz155240 /* ------------------------------------------------------------------------ */ 2072393Syz155240 /* Function: fr_natinit */ 2082393Syz155240 /* Returns: int - 0 == success, -1 == failure */ 2092393Syz155240 /* Parameters: Nil */ 2102393Syz155240 /* */ 2112393Syz155240 /* Initialise all of the NAT locks, tables and other structures. */ 2122393Syz155240 /* ------------------------------------------------------------------------ */ 2132393Syz155240 int fr_natinit() 2142393Syz155240 { 2152393Syz155240 int i; 2162393Syz155240 2172393Syz155240 KMALLOCS(nat_table[0], nat_t **, sizeof(nat_t *) * ipf_nattable_sz); 2182393Syz155240 if (nat_table[0] != NULL) 2192393Syz155240 bzero((char *)nat_table[0], ipf_nattable_sz * sizeof(nat_t *)); 2202393Syz155240 else 2212393Syz155240 return -1; 2222393Syz155240 2232393Syz155240 KMALLOCS(nat_table[1], nat_t **, sizeof(nat_t *) * ipf_nattable_sz); 2242393Syz155240 if (nat_table[1] != NULL) 2252393Syz155240 bzero((char *)nat_table[1], ipf_nattable_sz * sizeof(nat_t *)); 2262393Syz155240 else 2272393Syz155240 return -2; 2282393Syz155240 2292393Syz155240 KMALLOCS(nat_rules, ipnat_t **, sizeof(ipnat_t *) * ipf_natrules_sz); 2302393Syz155240 if (nat_rules != NULL) 2312393Syz155240 bzero((char *)nat_rules, ipf_natrules_sz * sizeof(ipnat_t *)); 2322393Syz155240 else 2332393Syz155240 return -3; 2342393Syz155240 2352393Syz155240 KMALLOCS(rdr_rules, ipnat_t **, sizeof(ipnat_t *) * ipf_rdrrules_sz); 2362393Syz155240 if (rdr_rules != NULL) 2372393Syz155240 bzero((char *)rdr_rules, ipf_rdrrules_sz * sizeof(ipnat_t *)); 2382393Syz155240 else 2392393Syz155240 return -4; 2402393Syz155240 2412393Syz155240 KMALLOCS(maptable, hostmap_t **, sizeof(hostmap_t *) * ipf_hostmap_sz); 2422393Syz155240 if (maptable != NULL) 2432393Syz155240 bzero((char *)maptable, sizeof(hostmap_t *) * ipf_hostmap_sz); 2442393Syz155240 else 2452393Syz155240 return -5; 2462393Syz155240 2472393Syz155240 KMALLOCS(nat_stats.ns_bucketlen[0], u_long *, 2482393Syz155240 ipf_nattable_sz * sizeof(u_long)); 2492393Syz155240 if (nat_stats.ns_bucketlen[0] == NULL) 2502393Syz155240 return -6; 2512393Syz155240 bzero((char *)nat_stats.ns_bucketlen[0], 2522393Syz155240 ipf_nattable_sz * sizeof(u_long)); 2532393Syz155240 2542393Syz155240 KMALLOCS(nat_stats.ns_bucketlen[1], u_long *, 2552393Syz155240 ipf_nattable_sz * sizeof(u_long)); 2562393Syz155240 if (nat_stats.ns_bucketlen[1] == NULL) 2572393Syz155240 return -7; 2582393Syz155240 2592393Syz155240 bzero((char *)nat_stats.ns_bucketlen[1], 2602393Syz155240 ipf_nattable_sz * sizeof(u_long)); 2612393Syz155240 2622393Syz155240 if (fr_nat_maxbucket == 0) { 2632393Syz155240 for (i = ipf_nattable_sz; i > 0; i >>= 1) 2642393Syz155240 fr_nat_maxbucket++; 2652393Syz155240 fr_nat_maxbucket *= 2; 2662393Syz155240 } 2672393Syz155240 2682393Syz155240 fr_sttab_init(nat_tqb); 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 */ 2732393Syz155240 nat_tqb[IPF_TCPS_CLOSED].ifq_ttl = fr_tcplastack; 2742393Syz155240 nat_tqb[IPF_TCP_NSTATES - 1].ifq_next = &nat_udptq; 2752393Syz155240 nat_udptq.ifq_ttl = fr_defnatage; 2762393Syz155240 nat_udptq.ifq_ref = 1; 2772393Syz155240 nat_udptq.ifq_head = NULL; 2782393Syz155240 nat_udptq.ifq_tail = &nat_udptq.ifq_head; 2792393Syz155240 MUTEX_INIT(&nat_udptq.ifq_lock, "nat ipftq udp tab"); 2802393Syz155240 nat_udptq.ifq_next = &nat_icmptq; 2812393Syz155240 nat_icmptq.ifq_ttl = fr_defnaticmpage; 2822393Syz155240 nat_icmptq.ifq_ref = 1; 2832393Syz155240 nat_icmptq.ifq_head = NULL; 2842393Syz155240 nat_icmptq.ifq_tail = &nat_icmptq.ifq_head; 2852393Syz155240 MUTEX_INIT(&nat_icmptq.ifq_lock, "nat icmp ipftq tab"); 2862393Syz155240 nat_icmptq.ifq_next = &nat_iptq; 2872393Syz155240 nat_iptq.ifq_ttl = fr_defnatipage; 2882393Syz155240 nat_iptq.ifq_ref = 1; 2892393Syz155240 nat_iptq.ifq_head = NULL; 2902393Syz155240 nat_iptq.ifq_tail = &nat_iptq.ifq_head; 2912393Syz155240 MUTEX_INIT(&nat_iptq.ifq_lock, "nat ip ipftq tab"); 2922393Syz155240 nat_iptq.ifq_next = NULL; 2932393Syz155240 2942393Syz155240 for (i = 0; i < IPF_TCP_NSTATES; i++) { 2952393Syz155240 if (nat_tqb[i].ifq_ttl < fr_defnaticmpage) 2962393Syz155240 nat_tqb[i].ifq_ttl = fr_defnaticmpage; 2972393Syz155240 #ifdef LARGE_NAT 2982393Syz155240 else if (nat_tqb[i].ifq_ttl > fr_defnatage) 2992393Syz155240 nat_tqb[i].ifq_ttl = 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 */ 3082393Syz155240 nat_tqb[IPF_TCPS_CLOSED].ifq_ttl = nat_tqb[IPF_TCPS_LAST_ACK].ifq_ttl; 3092393Syz155240 3102393Syz155240 RWLOCK_INIT(&ipf_nat, "ipf IP NAT rwlock"); 3112393Syz155240 RWLOCK_INIT(&ipf_natfrag, "ipf IP NAT-Frag rwlock"); 3122393Syz155240 MUTEX_INIT(&ipf_nat_new, "ipf nat new mutex"); 3132393Syz155240 MUTEX_INIT(&ipf_natio, "ipf nat io mutex"); 3142393Syz155240 3152393Syz155240 fr_nat_init = 1; 3162393Syz155240 3172393Syz155240 return 0; 3182393Syz155240 } 3192393Syz155240 3202393Syz155240 3212393Syz155240 /* ------------------------------------------------------------------------ */ 3222393Syz155240 /* Function: nat_addrdr */ 3232393Syz155240 /* Returns: Nil */ 3242393Syz155240 /* Parameters: n(I) - pointer to NAT rule to add */ 3252393Syz155240 /* */ 3262393Syz155240 /* Adds a redirect rule to the hash table of redirect rules and the list of */ 3272393Syz155240 /* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ 3282393Syz155240 /* use by redirect rules. */ 3292393Syz155240 /* ------------------------------------------------------------------------ */ 3302393Syz155240 static void nat_addrdr(n) 3312393Syz155240 ipnat_t *n; 3322393Syz155240 { 3332393Syz155240 ipnat_t **np; 3342393Syz155240 u_32_t j; 3352393Syz155240 u_int hv; 3362393Syz155240 int k; 3372393Syz155240 3382393Syz155240 k = count4bits(n->in_outmsk); 3392393Syz155240 if ((k >= 0) && (k != 32)) 3402393Syz155240 rdr_masks |= 1 << k; 3412393Syz155240 j = (n->in_outip & n->in_outmsk); 3422393Syz155240 hv = NAT_HASH_FN(j, 0, ipf_rdrrules_sz); 3432393Syz155240 np = rdr_rules + hv; 3442393Syz155240 while (*np != NULL) 3452393Syz155240 np = &(*np)->in_rnext; 3462393Syz155240 n->in_rnext = NULL; 3472393Syz155240 n->in_prnext = np; 3482393Syz155240 n->in_hv = hv; 3492393Syz155240 *np = n; 3502393Syz155240 } 3512393Syz155240 3522393Syz155240 3532393Syz155240 /* ------------------------------------------------------------------------ */ 3542393Syz155240 /* Function: nat_addnat */ 3552393Syz155240 /* Returns: Nil */ 3562393Syz155240 /* Parameters: n(I) - pointer to NAT rule to add */ 3572393Syz155240 /* */ 3582393Syz155240 /* Adds a NAT map rule to the hash table of rules and the list of loaded */ 3592393Syz155240 /* NAT rules. Updates the bitmask indicating which netmasks are in use by */ 3602393Syz155240 /* redirect rules. */ 3612393Syz155240 /* ------------------------------------------------------------------------ */ 3622393Syz155240 static void nat_addnat(n) 3632393Syz155240 ipnat_t *n; 3642393Syz155240 { 3652393Syz155240 ipnat_t **np; 3662393Syz155240 u_32_t j; 3672393Syz155240 u_int hv; 3682393Syz155240 int k; 3692393Syz155240 3702393Syz155240 k = count4bits(n->in_inmsk); 3712393Syz155240 if ((k >= 0) && (k != 32)) 3722393Syz155240 nat_masks |= 1 << k; 3732393Syz155240 j = (n->in_inip & n->in_inmsk); 3742393Syz155240 hv = NAT_HASH_FN(j, 0, ipf_natrules_sz); 3752393Syz155240 np = nat_rules + hv; 3762393Syz155240 while (*np != NULL) 3772393Syz155240 np = &(*np)->in_mnext; 3782393Syz155240 n->in_mnext = NULL; 3792393Syz155240 n->in_pmnext = np; 3802393Syz155240 n->in_hv = hv; 3812393Syz155240 *np = n; 3822393Syz155240 } 3832393Syz155240 3842393Syz155240 3852393Syz155240 /* ------------------------------------------------------------------------ */ 3862393Syz155240 /* Function: nat_delrdr */ 3872393Syz155240 /* Returns: Nil */ 3882393Syz155240 /* Parameters: n(I) - pointer to NAT rule to delete */ 3892393Syz155240 /* */ 3902393Syz155240 /* Removes a redirect rule from the hash table of redirect rules. */ 3912393Syz155240 /* ------------------------------------------------------------------------ */ 3922393Syz155240 static void nat_delrdr(n) 3932393Syz155240 ipnat_t *n; 3942393Syz155240 { 3952393Syz155240 if (n->in_rnext) 3962393Syz155240 n->in_rnext->in_prnext = n->in_prnext; 3972393Syz155240 *n->in_prnext = n->in_rnext; 3982393Syz155240 } 3992393Syz155240 4002393Syz155240 4012393Syz155240 /* ------------------------------------------------------------------------ */ 4022393Syz155240 /* Function: nat_delnat */ 4032393Syz155240 /* Returns: Nil */ 4042393Syz155240 /* Parameters: n(I) - pointer to NAT rule to delete */ 4052393Syz155240 /* */ 4062393Syz155240 /* Removes a NAT map rule from the hash table of NAT map rules. */ 4072393Syz155240 /* ------------------------------------------------------------------------ */ 4082393Syz155240 static void nat_delnat(n) 4092393Syz155240 ipnat_t *n; 4102393Syz155240 { 4112393Syz155240 if (n->in_mnext != NULL) 4122393Syz155240 n->in_mnext->in_pmnext = n->in_pmnext; 4132393Syz155240 *n->in_pmnext = n->in_mnext; 4142393Syz155240 } 4152393Syz155240 4162393Syz155240 4172393Syz155240 /* ------------------------------------------------------------------------ */ 4182393Syz155240 /* Function: nat_hostmap */ 4192393Syz155240 /* Returns: struct hostmap* - NULL if no hostmap could be created, */ 4202393Syz155240 /* else a pointer to the hostmapping to use */ 4212393Syz155240 /* Parameters: np(I) - pointer to NAT rule */ 4222393Syz155240 /* real(I) - real IP address */ 4232393Syz155240 /* map(I) - mapped IP address */ 4242393Syz155240 /* port(I) - destination port number */ 4252393Syz155240 /* Write Locks: ipf_nat */ 4262393Syz155240 /* */ 4272393Syz155240 /* Check if an ip address has already been allocated for a given mapping */ 4282393Syz155240 /* that is not doing port based translation. If is not yet allocated, then */ 4292393Syz155240 /* create a new entry if a non-NULL NAT rule pointer has been supplied. */ 4302393Syz155240 /* ------------------------------------------------------------------------ */ 4312393Syz155240 static struct hostmap *nat_hostmap(np, src, dst, map, port) 4322393Syz155240 ipnat_t *np; 4332393Syz155240 struct in_addr src; 4342393Syz155240 struct in_addr dst; 4352393Syz155240 struct in_addr map; 4362393Syz155240 u_32_t port; 4372393Syz155240 { 4382393Syz155240 hostmap_t *hm; 4392393Syz155240 u_int hv; 4402393Syz155240 4412393Syz155240 hv = (src.s_addr ^ dst.s_addr); 4422393Syz155240 hv += src.s_addr; 4432393Syz155240 hv += dst.s_addr; 4442393Syz155240 hv %= HOSTMAP_SIZE; 4452393Syz155240 for (hm = maptable[hv]; hm; hm = hm->hm_next) 4462393Syz155240 if ((hm->hm_srcip.s_addr == src.s_addr) && 4472393Syz155240 (hm->hm_dstip.s_addr == dst.s_addr) && 4482393Syz155240 ((np == NULL) || (np == hm->hm_ipnat)) && 4492393Syz155240 ((port == 0) || (port == hm->hm_port))) { 4502393Syz155240 hm->hm_ref++; 4512393Syz155240 return hm; 4522393Syz155240 } 4532393Syz155240 4542393Syz155240 if (np == NULL) 4552393Syz155240 return NULL; 4562393Syz155240 4572393Syz155240 KMALLOC(hm, hostmap_t *); 4582393Syz155240 if (hm) { 4592393Syz155240 hm->hm_next = maptable[hv]; 4602393Syz155240 hm->hm_pnext = maptable + hv; 4612393Syz155240 if (maptable[hv] != NULL) 4622393Syz155240 maptable[hv]->hm_pnext = &hm->hm_next; 4632393Syz155240 maptable[hv] = hm; 4642393Syz155240 hm->hm_ipnat = np; 4652393Syz155240 hm->hm_srcip = src; 4662393Syz155240 hm->hm_dstip = dst; 4672393Syz155240 hm->hm_mapip = map; 4682393Syz155240 hm->hm_ref = 1; 4692393Syz155240 hm->hm_port = port; 4702393Syz155240 } 4712393Syz155240 return hm; 4722393Syz155240 } 4732393Syz155240 4742393Syz155240 4752393Syz155240 /* ------------------------------------------------------------------------ */ 4762393Syz155240 /* Function: nat_hostmapdel */ 4772393Syz155240 /* Returns: Nil */ 4782393Syz155240 /* Parameters: hm(I) - pointer to hostmap structure */ 4792393Syz155240 /* Write Locks: ipf_nat */ 4802393Syz155240 /* */ 4812393Syz155240 /* Decrement the references to this hostmap structure by one. If this */ 4822393Syz155240 /* reaches zero then remove it and free it. */ 4832393Syz155240 /* ------------------------------------------------------------------------ */ 4842393Syz155240 static void nat_hostmapdel(hm) 4852393Syz155240 struct hostmap *hm; 4862393Syz155240 { 4872393Syz155240 hm->hm_ref--; 4882393Syz155240 if (hm->hm_ref == 0) { 4892393Syz155240 if (hm->hm_next) 4902393Syz155240 hm->hm_next->hm_pnext = hm->hm_pnext; 4912393Syz155240 *hm->hm_pnext = hm->hm_next; 4922393Syz155240 KFREE(hm); 4932393Syz155240 } 4942393Syz155240 } 4952393Syz155240 4962393Syz155240 4972393Syz155240 /* ------------------------------------------------------------------------ */ 4982393Syz155240 /* Function: fix_outcksum */ 4992393Syz155240 /* Returns: Nil */ 5002393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 5012393Syz155240 /* sp(I) - location of 16bit checksum to update */ 5022393Syz155240 /* n((I) - amount to adjust checksum by */ 5032393Syz155240 /* */ 5042393Syz155240 /* Adjusts the 16bit checksum by "n" for packets going out. */ 5052393Syz155240 /* ------------------------------------------------------------------------ */ 5062393Syz155240 void fix_outcksum(fin, sp, n) 5072393Syz155240 fr_info_t *fin; 5082393Syz155240 u_short *sp; 5092393Syz155240 u_32_t n; 5102393Syz155240 { 5112393Syz155240 u_short sumshort; 5122393Syz155240 u_32_t sum1; 5132393Syz155240 5142393Syz155240 if (n == 0) 5152393Syz155240 return; 5162393Syz155240 5172393Syz155240 if (n & NAT_HW_CKSUM) { 5182393Syz155240 n &= 0xffff; 5192393Syz155240 n += fin->fin_dlen; 5202393Syz155240 n = (n & 0xffff) + (n >> 16); 5212393Syz155240 *sp = n & 0xffff; 5222393Syz155240 return; 5232393Syz155240 } 5242393Syz155240 sum1 = (~ntohs(*sp)) & 0xffff; 5252393Syz155240 sum1 += (n); 5262393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 5272393Syz155240 /* Again */ 5282393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 5292393Syz155240 sumshort = ~(u_short)sum1; 5302393Syz155240 *(sp) = htons(sumshort); 5312393Syz155240 } 5322393Syz155240 5332393Syz155240 5342393Syz155240 /* ------------------------------------------------------------------------ */ 5352393Syz155240 /* Function: fix_incksum */ 5362393Syz155240 /* Returns: Nil */ 5372393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 5382393Syz155240 /* sp(I) - location of 16bit checksum to update */ 5392393Syz155240 /* n((I) - amount to adjust checksum by */ 5402393Syz155240 /* */ 5412393Syz155240 /* Adjusts the 16bit checksum by "n" for packets going in. */ 5422393Syz155240 /* ------------------------------------------------------------------------ */ 5432393Syz155240 void fix_incksum(fin, sp, n) 5442393Syz155240 fr_info_t *fin; 5452393Syz155240 u_short *sp; 5462393Syz155240 u_32_t n; 5472393Syz155240 { 5482393Syz155240 u_short sumshort; 5492393Syz155240 u_32_t sum1; 5502393Syz155240 5512393Syz155240 if (n == 0) 5522393Syz155240 return; 5532393Syz155240 5542393Syz155240 if (n & NAT_HW_CKSUM) { 5552393Syz155240 n &= 0xffff; 5562393Syz155240 n += fin->fin_dlen; 5572393Syz155240 n = (n & 0xffff) + (n >> 16); 5582393Syz155240 *sp = n & 0xffff; 5592393Syz155240 return; 5602393Syz155240 } 5612393Syz155240 sum1 = (~ntohs(*sp)) & 0xffff; 5622393Syz155240 sum1 += ~(n) & 0xffff; 5632393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 5642393Syz155240 /* Again */ 5652393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 5662393Syz155240 sumshort = ~(u_short)sum1; 5672393Syz155240 *(sp) = htons(sumshort); 5682393Syz155240 } 5692393Syz155240 5702393Syz155240 5712393Syz155240 /* ------------------------------------------------------------------------ */ 5722393Syz155240 /* Function: fix_datacksum */ 5732393Syz155240 /* Returns: Nil */ 5742393Syz155240 /* Parameters: sp(I) - location of 16bit checksum to update */ 5752393Syz155240 /* n((I) - amount to adjust checksum by */ 5762393Syz155240 /* */ 5772393Syz155240 /* Fix_datacksum is used *only* for the adjustments of checksums in the */ 5782393Syz155240 /* data section of an IP packet. */ 5792393Syz155240 /* */ 5802393Syz155240 /* The only situation in which you need to do this is when NAT'ing an */ 5812393Syz155240 /* ICMP error message. Such a message, contains in its body the IP header */ 5822393Syz155240 /* of the original IP packet, that causes the error. */ 5832393Syz155240 /* */ 5842393Syz155240 /* You can't use fix_incksum or fix_outcksum in that case, because for the */ 5852393Syz155240 /* kernel the data section of the ICMP error is just data, and no special */ 5862393Syz155240 /* processing like hardware cksum or ntohs processing have been done by the */ 5872393Syz155240 /* kernel on the data section. */ 5882393Syz155240 /* ------------------------------------------------------------------------ */ 5892393Syz155240 void fix_datacksum(sp, n) 5902393Syz155240 u_short *sp; 5912393Syz155240 u_32_t n; 5922393Syz155240 { 5932393Syz155240 u_short sumshort; 5942393Syz155240 u_32_t sum1; 5952393Syz155240 5962393Syz155240 if (n == 0) 5972393Syz155240 return; 5982393Syz155240 5992393Syz155240 sum1 = (~ntohs(*sp)) & 0xffff; 6002393Syz155240 sum1 += (n); 6012393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 6022393Syz155240 /* Again */ 6032393Syz155240 sum1 = (sum1 >> 16) + (sum1 & 0xffff); 6042393Syz155240 sumshort = ~(u_short)sum1; 6052393Syz155240 *(sp) = htons(sumshort); 6062393Syz155240 } 6072393Syz155240 6082393Syz155240 6092393Syz155240 /* ------------------------------------------------------------------------ */ 6102393Syz155240 /* Function: fr_nat_ioctl */ 6112393Syz155240 /* Returns: int - 0 == success, != 0 == failure */ 6122393Syz155240 /* Parameters: data(I) - pointer to ioctl data */ 6132393Syz155240 /* cmd(I) - ioctl command integer */ 6142393Syz155240 /* mode(I) - file mode bits used with open */ 6152393Syz155240 /* */ 6162393Syz155240 /* Processes an ioctl call made to operate on the IP Filter NAT device. */ 6172393Syz155240 /* ------------------------------------------------------------------------ */ 6182393Syz155240 int fr_nat_ioctl(data, cmd, mode) 6192393Syz155240 ioctlcmd_t cmd; 6202393Syz155240 caddr_t data; 6212393Syz155240 int mode; 6222393Syz155240 { 6232393Syz155240 ipnat_t *nat, *nt, *n = NULL, **np = NULL; 6242393Syz155240 int error = 0, ret, arg, getlock; 6252393Syz155240 ipnat_t natd; 6262393Syz155240 6272393Syz155240 #if (BSD >= 199306) && defined(_KERNEL) 6282393Syz155240 if ((securelevel >= 2) && (mode & FWRITE)) 6292393Syz155240 return EPERM; 6302393Syz155240 #endif 6312393Syz155240 6322393Syz155240 #if defined(__osf__) && defined(_KERNEL) 6332393Syz155240 getlock = 0; 6342393Syz155240 #else 6352393Syz155240 getlock = (mode & NAT_LOCKHELD) ? 0 : 1; 6362393Syz155240 #endif 6372393Syz155240 6382393Syz155240 nat = NULL; /* XXX gcc -Wuninitialized */ 6392393Syz155240 if (cmd == (ioctlcmd_t)SIOCADNAT) { 6402393Syz155240 KMALLOC(nt, ipnat_t *); 6412393Syz155240 } else { 6422393Syz155240 nt = NULL; 6432393Syz155240 } 6442393Syz155240 6452393Syz155240 if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) { 6462393Syz155240 if (mode & NAT_SYSSPACE) { 6472393Syz155240 bcopy(data, (char *)&natd, sizeof(natd)); 6482393Syz155240 error = 0; 6492393Syz155240 } else { 6502393Syz155240 error = fr_inobj(data, &natd, IPFOBJ_IPNAT); 6512393Syz155240 } 6522393Syz155240 6532393Syz155240 } else if (cmd == (ioctlcmd_t)SIOCIPFFL) { /* SIOCFLNAT & SIOCCNATL */ 6542393Syz155240 BCOPYIN(data, &arg, sizeof(arg)); 6552393Syz155240 } 6562393Syz155240 6572393Syz155240 if (error != 0) 6582393Syz155240 goto done; 6592393Syz155240 6602393Syz155240 /* 6612393Syz155240 * For add/delete, look to see if the NAT entry is already present 6622393Syz155240 */ 6632393Syz155240 if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) { 6642393Syz155240 nat = &natd; 6652393Syz155240 if (nat->in_v == 0) /* For backward compat. */ 6662393Syz155240 nat->in_v = 4; 6672393Syz155240 nat->in_flags &= IPN_USERFLAGS; 6682393Syz155240 if ((nat->in_redir & NAT_MAPBLK) == 0) { 6692393Syz155240 if ((nat->in_flags & IPN_SPLIT) == 0) 6702393Syz155240 nat->in_inip &= nat->in_inmsk; 6712393Syz155240 if ((nat->in_flags & IPN_IPRANGE) == 0) 6722393Syz155240 nat->in_outip &= nat->in_outmsk; 6732393Syz155240 } 6742393Syz155240 MUTEX_ENTER(&ipf_natio); 6752393Syz155240 for (np = &nat_list; ((n = *np) != NULL); np = &n->in_next) 6762393Syz155240 if (!bcmp((char *)&nat->in_flags, (char *)&n->in_flags, 6772393Syz155240 IPN_CMPSIZ)) 6782393Syz155240 break; 6792393Syz155240 } 6802393Syz155240 6812393Syz155240 switch (cmd) 6822393Syz155240 { 6832393Syz155240 #ifdef IPFILTER_LOG 6842393Syz155240 case SIOCIPFFB : 6852393Syz155240 { 6862393Syz155240 int tmp; 6872393Syz155240 6882393Syz155240 if (!(mode & FWRITE)) 6892393Syz155240 error = EPERM; 6902393Syz155240 else { 6912393Syz155240 tmp = ipflog_clear(IPL_LOGNAT); 6922393Syz155240 BCOPYOUT((char *)&tmp, (char *)data, sizeof(tmp)); 6932393Syz155240 } 6942393Syz155240 break; 6952393Syz155240 } 6962393Syz155240 case SIOCSETLG : 6972393Syz155240 if (!(mode & FWRITE)) 6982393Syz155240 error = EPERM; 6992393Syz155240 else { 7002393Syz155240 BCOPYIN((char *)data, (char *)&nat_logging, 7012393Syz155240 sizeof(nat_logging)); 7022393Syz155240 } 7032393Syz155240 break; 7042393Syz155240 case SIOCGETLG : 7052393Syz155240 BCOPYOUT((char *)&nat_logging, (char *)data, 7062393Syz155240 sizeof(nat_logging)); 7072393Syz155240 break; 7082393Syz155240 case FIONREAD : 7092393Syz155240 arg = iplused[IPL_LOGNAT]; 7102393Syz155240 BCOPYOUT(&arg, data, sizeof(arg)); 7112393Syz155240 break; 7122393Syz155240 #endif 7132393Syz155240 case SIOCADNAT : 7142393Syz155240 if (!(mode & FWRITE)) { 7152393Syz155240 error = EPERM; 7162393Syz155240 } else if (n != NULL) { 7172393Syz155240 error = EEXIST; 7182393Syz155240 } else if (nt == NULL) { 7192393Syz155240 error = ENOMEM; 7202393Syz155240 } 7212393Syz155240 if (error != 0) { 7222393Syz155240 MUTEX_EXIT(&ipf_natio); 7232393Syz155240 break; 7242393Syz155240 } 7252393Syz155240 bcopy((char *)nat, (char *)nt, sizeof(*n)); 7262393Syz155240 error = nat_siocaddnat(nt, np, getlock); 7272393Syz155240 MUTEX_EXIT(&ipf_natio); 7282393Syz155240 if (error == 0) 7292393Syz155240 nt = NULL; 7302393Syz155240 break; 7312393Syz155240 case SIOCRMNAT : 7322393Syz155240 if (!(mode & FWRITE)) { 7332393Syz155240 error = EPERM; 7342393Syz155240 n = NULL; 7352393Syz155240 } else if (n == NULL) { 7362393Syz155240 error = ESRCH; 7372393Syz155240 } 7382393Syz155240 7392393Syz155240 if (error != 0) { 7402393Syz155240 MUTEX_EXIT(&ipf_natio); 7412393Syz155240 break; 7422393Syz155240 } 7432393Syz155240 nat_siocdelnat(n, np, getlock); 7442393Syz155240 7452393Syz155240 MUTEX_EXIT(&ipf_natio); 7462393Syz155240 n = NULL; 7472393Syz155240 break; 7482393Syz155240 case SIOCGNATS : 7492393Syz155240 nat_stats.ns_table[0] = nat_table[0]; 7502393Syz155240 nat_stats.ns_table[1] = nat_table[1]; 7512393Syz155240 nat_stats.ns_list = nat_list; 7522393Syz155240 nat_stats.ns_maptable = maptable; 7532393Syz155240 nat_stats.ns_nattab_sz = ipf_nattable_sz; 7542393Syz155240 nat_stats.ns_nattab_max = ipf_nattable_max; 7552393Syz155240 nat_stats.ns_rultab_sz = ipf_natrules_sz; 7562393Syz155240 nat_stats.ns_rdrtab_sz = ipf_rdrrules_sz; 7572393Syz155240 nat_stats.ns_hostmap_sz = ipf_hostmap_sz; 7582393Syz155240 nat_stats.ns_instances = nat_instances; 7592393Syz155240 nat_stats.ns_apslist = ap_sess_list; 7602393Syz155240 error = fr_outobj(data, &nat_stats, IPFOBJ_NATSTAT); 7612393Syz155240 break; 7622393Syz155240 case SIOCGNATL : 7632393Syz155240 { 7642393Syz155240 natlookup_t nl; 7652393Syz155240 7662393Syz155240 if (getlock) { 7672393Syz155240 READ_ENTER(&ipf_nat); 7682393Syz155240 } 7692393Syz155240 error = fr_inobj(data, &nl, IPFOBJ_NATLOOKUP); 7702393Syz155240 if (error == 0) { 7712393Syz155240 if (nat_lookupredir(&nl) != NULL) { 7722393Syz155240 error = fr_outobj(data, &nl, IPFOBJ_NATLOOKUP); 7732393Syz155240 } else { 7742393Syz155240 error = ESRCH; 7752393Syz155240 } 7762393Syz155240 } 7772393Syz155240 if (getlock) { 7782393Syz155240 RWLOCK_EXIT(&ipf_nat); 7792393Syz155240 } 7802393Syz155240 break; 7812393Syz155240 } 7822393Syz155240 case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */ 7832393Syz155240 if (!(mode & FWRITE)) { 7842393Syz155240 error = EPERM; 7852393Syz155240 break; 7862393Syz155240 } 7872393Syz155240 if (getlock) { 7882393Syz155240 WRITE_ENTER(&ipf_nat); 7892393Syz155240 } 7902393Syz155240 error = 0; 7912393Syz155240 if (arg == 0) 7922393Syz155240 ret = nat_flushtable(); 7932393Syz155240 else if (arg == 1) 7942393Syz155240 ret = nat_clearlist(); 7952393Syz155240 else 7962393Syz155240 error = EINVAL; 7972393Syz155240 if (getlock) { 7982393Syz155240 RWLOCK_EXIT(&ipf_nat); 7992393Syz155240 } 8002393Syz155240 if (error == 0) { 8012393Syz155240 BCOPYOUT(&ret, data, sizeof(ret)); 8022393Syz155240 } 8032393Syz155240 break; 8042393Syz155240 case SIOCPROXY : 8052393Syz155240 error = appr_ioctl(data, cmd, mode); 8062393Syz155240 break; 8072393Syz155240 case SIOCSTLCK : 8082393Syz155240 if (!(mode & FWRITE)) { 8092393Syz155240 error = EPERM; 8102393Syz155240 } else { 8112393Syz155240 fr_lock(data, &fr_nat_lock); 8122393Syz155240 } 8132393Syz155240 break; 8142393Syz155240 case SIOCSTPUT : 8152393Syz155240 if (fr_nat_lock && (mode & FWRITE)) { 8162393Syz155240 error = fr_natputent(data, getlock); 8172393Syz155240 } else { 8182393Syz155240 error = EACCES; 8192393Syz155240 } 8202393Syz155240 break; 8212393Syz155240 case SIOCSTGSZ : 8222393Syz155240 if (fr_nat_lock) { 8232393Syz155240 if (getlock) { 8242393Syz155240 READ_ENTER(&ipf_nat); 8252393Syz155240 } 8262393Syz155240 error = fr_natgetsz(data); 8272393Syz155240 if (getlock) { 8282393Syz155240 RWLOCK_EXIT(&ipf_nat); 8292393Syz155240 } 8302393Syz155240 } else 8312393Syz155240 error = EACCES; 8322393Syz155240 break; 8332393Syz155240 case SIOCSTGET : 8342393Syz155240 if (fr_nat_lock) { 8352393Syz155240 if (getlock) { 8362393Syz155240 READ_ENTER(&ipf_nat); 8372393Syz155240 } 8382393Syz155240 error = fr_natgetent(data); 8392393Syz155240 if (getlock) { 8402393Syz155240 RWLOCK_EXIT(&ipf_nat); 8412393Syz155240 } 8422393Syz155240 } else 8432393Syz155240 error = EACCES; 8442393Syz155240 break; 8452393Syz155240 default : 8462393Syz155240 error = EINVAL; 8472393Syz155240 break; 8482393Syz155240 } 8492393Syz155240 done: 8502393Syz155240 if (nt) 8512393Syz155240 KFREE(nt); 8522393Syz155240 return error; 8532393Syz155240 } 8542393Syz155240 8552393Syz155240 8562393Syz155240 /* ------------------------------------------------------------------------ */ 8572393Syz155240 /* Function: nat_siocaddnat */ 8582393Syz155240 /* Returns: int - 0 == success, != 0 == failure */ 8592393Syz155240 /* Parameters: n(I) - pointer to new NAT rule */ 8602393Syz155240 /* np(I) - pointer to where to insert new NAT rule */ 8612393Syz155240 /* getlock(I) - flag indicating if lock on ipf_nat is held */ 8622393Syz155240 /* Mutex Locks: ipf_natio */ 8632393Syz155240 /* */ 8642393Syz155240 /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 8652393Syz155240 /* from information passed to the kernel, then add it to the appropriate */ 8662393Syz155240 /* NAT rule table(s). */ 8672393Syz155240 /* ------------------------------------------------------------------------ */ 8682393Syz155240 static int nat_siocaddnat(n, np, getlock) 8692393Syz155240 ipnat_t *n, **np; 8702393Syz155240 int getlock; 8712393Syz155240 { 8722393Syz155240 int error = 0, i, j; 8732393Syz155240 8742393Syz155240 nat_resolverule(n); 8752393Syz155240 if (n->in_plabel[0] != '\0') { 8762393Syz155240 if (n->in_apr == NULL) 8772393Syz155240 return ENOENT; 8782393Syz155240 } 8792393Syz155240 8802393Syz155240 if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) 8812393Syz155240 return EINVAL; 8822393Syz155240 8832393Syz155240 n->in_use = 0; 8842393Syz155240 if (n->in_redir & NAT_MAPBLK) 8852393Syz155240 n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk); 8862393Syz155240 else if (n->in_flags & IPN_AUTOPORTMAP) 8872393Syz155240 n->in_space = USABLE_PORTS * ~ntohl(n->in_inmsk); 8882393Syz155240 else if (n->in_flags & IPN_IPRANGE) 8892393Syz155240 n->in_space = ntohl(n->in_outmsk) - ntohl(n->in_outip); 8902393Syz155240 else if (n->in_flags & IPN_SPLIT) 8912393Syz155240 n->in_space = 2; 8922393Syz155240 else if (n->in_outmsk != 0) 8932393Syz155240 n->in_space = ~ntohl(n->in_outmsk); 8942393Syz155240 else 8952393Syz155240 n->in_space = 1; 8962393Syz155240 8972393Syz155240 /* 8982393Syz155240 * Calculate the number of valid IP addresses in the output 8992393Syz155240 * mapping range. In all cases, the range is inclusive of 9002393Syz155240 * the start and ending IP addresses. 9012393Syz155240 * If to a CIDR address, lose 2: broadcast + network address 9022393Syz155240 * (so subtract 1) 9032393Syz155240 * If to a range, add one. 9042393Syz155240 * If to a single IP address, set to 1. 9052393Syz155240 */ 9062393Syz155240 if (n->in_space) { 9072393Syz155240 if ((n->in_flags & IPN_IPRANGE) != 0) 9082393Syz155240 n->in_space += 1; 9092393Syz155240 else 9102393Syz155240 n->in_space -= 1; 9112393Syz155240 } else 9122393Syz155240 n->in_space = 1; 9132393Syz155240 9142393Syz155240 if ((n->in_outmsk != 0xffffffff) && (n->in_outmsk != 0) && 9152393Syz155240 ((n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0)) 9162393Syz155240 n->in_nip = ntohl(n->in_outip) + 1; 9172393Syz155240 else if ((n->in_flags & IPN_SPLIT) && 9182393Syz155240 (n->in_redir & NAT_REDIRECT)) 9192393Syz155240 n->in_nip = ntohl(n->in_inip); 9202393Syz155240 else 9212393Syz155240 n->in_nip = ntohl(n->in_outip); 9222393Syz155240 if (n->in_redir & NAT_MAP) { 9232393Syz155240 n->in_pnext = ntohs(n->in_pmin); 9242393Syz155240 /* 9252393Syz155240 * Multiply by the number of ports made available. 9262393Syz155240 */ 9272393Syz155240 if (ntohs(n->in_pmax) >= ntohs(n->in_pmin)) { 9282393Syz155240 n->in_space *= (ntohs(n->in_pmax) - 9292393Syz155240 ntohs(n->in_pmin) + 1); 9302393Syz155240 /* 9312393Syz155240 * Because two different sources can map to 9322393Syz155240 * different destinations but use the same 9332393Syz155240 * local IP#/port #. 9342393Syz155240 * If the result is smaller than in_space, then 9352393Syz155240 * we may have wrapped around 32bits. 9362393Syz155240 */ 9372393Syz155240 i = n->in_inmsk; 9382393Syz155240 if ((i != 0) && (i != 0xffffffff)) { 9392393Syz155240 j = n->in_space * (~ntohl(i) + 1); 9402393Syz155240 if (j >= n->in_space) 9412393Syz155240 n->in_space = j; 9422393Syz155240 else 9432393Syz155240 n->in_space = 0xffffffff; 9442393Syz155240 } 9452393Syz155240 } 9462393Syz155240 /* 9472393Syz155240 * If no protocol is specified, multiple by 256 to allow for 9482393Syz155240 * at least one IP:IP mapping per protocol. 9492393Syz155240 */ 9502393Syz155240 if ((n->in_flags & IPN_TCPUDPICMP) == 0) { 9512393Syz155240 j = n->in_space * 256; 9522393Syz155240 if (j >= n->in_space) 9532393Syz155240 n->in_space = j; 9542393Syz155240 else 9552393Syz155240 n->in_space = 0xffffffff; 9562393Syz155240 } 9572393Syz155240 } 9582393Syz155240 9592393Syz155240 /* Otherwise, these fields are preset */ 9602393Syz155240 9612393Syz155240 if (getlock) { 9622393Syz155240 WRITE_ENTER(&ipf_nat); 9632393Syz155240 } 9642393Syz155240 n->in_next = NULL; 9652393Syz155240 *np = n; 9662393Syz155240 9672393Syz155240 if (n->in_age[0] != 0) 9682393Syz155240 n->in_tqehead[0] = fr_addtimeoutqueue(&nat_utqe, n->in_age[0]); 9692393Syz155240 9702393Syz155240 if (n->in_age[1] != 0) 9712393Syz155240 n->in_tqehead[1] = fr_addtimeoutqueue(&nat_utqe, n->in_age[1]); 9722393Syz155240 9732393Syz155240 if (n->in_redir & NAT_REDIRECT) { 9742393Syz155240 n->in_flags &= ~IPN_NOTDST; 9752393Syz155240 nat_addrdr(n); 9762393Syz155240 } 9772393Syz155240 if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { 9782393Syz155240 n->in_flags &= ~IPN_NOTSRC; 9792393Syz155240 nat_addnat(n); 9802393Syz155240 } 9812393Syz155240 n = NULL; 9822393Syz155240 nat_stats.ns_rules++; 9832393Syz155240 #if SOLARIS 9842393Syz155240 pfil_delayed_copy = 0; 9852393Syz155240 #endif 9862393Syz155240 if (getlock) { 9872393Syz155240 RWLOCK_EXIT(&ipf_nat); /* WRITE */ 9882393Syz155240 } 9892393Syz155240 9902393Syz155240 return error; 9912393Syz155240 } 9922393Syz155240 9932393Syz155240 9942393Syz155240 /* ------------------------------------------------------------------------ */ 9952393Syz155240 /* Function: nat_resolvrule */ 9962393Syz155240 /* Returns: Nil */ 9972393Syz155240 /* Parameters: n(I) - pointer to NAT rule */ 9982393Syz155240 /* */ 9992393Syz155240 /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 10002393Syz155240 /* from information passed to the kernel, then add it to the appropriate */ 10012393Syz155240 /* NAT rule table(s). */ 10022393Syz155240 /* ------------------------------------------------------------------------ */ 10032393Syz155240 static void nat_resolverule(n) 10042393Syz155240 ipnat_t *n; 10052393Syz155240 { 10062393Syz155240 n->in_ifnames[0][LIFNAMSIZ - 1] = '\0'; 10072393Syz155240 n->in_ifps[0] = fr_resolvenic(n->in_ifnames[0], 4); 10082393Syz155240 10092393Syz155240 n->in_ifnames[1][LIFNAMSIZ - 1] = '\0'; 10102393Syz155240 if (n->in_ifnames[1][0] == '\0') { 10112393Syz155240 (void) strncpy(n->in_ifnames[1], n->in_ifnames[0], LIFNAMSIZ); 10122393Syz155240 n->in_ifps[1] = n->in_ifps[0]; 10132393Syz155240 } else { 10142393Syz155240 n->in_ifps[1] = fr_resolvenic(n->in_ifnames[0], 4); 10152393Syz155240 } 10162393Syz155240 10172393Syz155240 if (n->in_plabel[0] != '\0') { 10182393Syz155240 n->in_apr = appr_lookup(n->in_p, n->in_plabel); 10192393Syz155240 } 10202393Syz155240 } 10212393Syz155240 10222393Syz155240 10232393Syz155240 /* ------------------------------------------------------------------------ */ 10242393Syz155240 /* Function: nat_siocdelnat */ 10252393Syz155240 /* Returns: int - 0 == success, != 0 == failure */ 10262393Syz155240 /* Parameters: n(I) - pointer to new NAT rule */ 10272393Syz155240 /* np(I) - pointer to where to insert new NAT rule */ 10282393Syz155240 /* getlock(I) - flag indicating if lock on ipf_nat is held */ 10292393Syz155240 /* Mutex Locks: ipf_natio */ 10302393Syz155240 /* */ 10312393Syz155240 /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 10322393Syz155240 /* from information passed to the kernel, then add it to the appropriate */ 10332393Syz155240 /* NAT rule table(s). */ 10342393Syz155240 /* ------------------------------------------------------------------------ */ 10352393Syz155240 static void nat_siocdelnat(n, np, getlock) 10362393Syz155240 ipnat_t *n, **np; 10372393Syz155240 int getlock; 10382393Syz155240 { 10392393Syz155240 if (getlock) { 10402393Syz155240 WRITE_ENTER(&ipf_nat); 10412393Syz155240 } 10422393Syz155240 if (n->in_redir & NAT_REDIRECT) 10432393Syz155240 nat_delrdr(n); 10442393Syz155240 if (n->in_redir & (NAT_MAPBLK|NAT_MAP)) 10452393Syz155240 nat_delnat(n); 10462393Syz155240 if (nat_list == NULL) { 10472393Syz155240 nat_masks = 0; 10482393Syz155240 rdr_masks = 0; 10492393Syz155240 } 10502393Syz155240 10512393Syz155240 if (n->in_tqehead[0] != NULL) { 10522393Syz155240 if (fr_deletetimeoutqueue(n->in_tqehead[0]) == 0) { 10532393Syz155240 fr_freetimeoutqueue(n->in_tqehead[1]); 10542393Syz155240 } 10552393Syz155240 } 10562393Syz155240 10572393Syz155240 if (n->in_tqehead[1] != NULL) { 10582393Syz155240 if (fr_deletetimeoutqueue(n->in_tqehead[1]) == 0) { 10592393Syz155240 fr_freetimeoutqueue(n->in_tqehead[1]); 10602393Syz155240 } 10612393Syz155240 } 10622393Syz155240 10632393Syz155240 *np = n->in_next; 10642393Syz155240 10652393Syz155240 if (n->in_use == 0) { 10662393Syz155240 if (n->in_apr) 10672393Syz155240 appr_free(n->in_apr); 10682393Syz155240 KFREE(n); 10692393Syz155240 nat_stats.ns_rules--; 10702393Syz155240 #if SOLARIS 10712393Syz155240 if (nat_stats.ns_rules == 0) 10722393Syz155240 pfil_delayed_copy = 1; 10732393Syz155240 #endif 10742393Syz155240 } else { 10752393Syz155240 n->in_flags |= IPN_DELETE; 10762393Syz155240 n->in_next = NULL; 10772393Syz155240 } 10782393Syz155240 if (getlock) { 10792393Syz155240 RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ 10802393Syz155240 } 10812393Syz155240 } 10822393Syz155240 10832393Syz155240 10842393Syz155240 /* ------------------------------------------------------------------------ */ 10852393Syz155240 /* Function: fr_natgetsz */ 10862393Syz155240 /* Returns: int - 0 == success, != 0 is the error value. */ 10872393Syz155240 /* Parameters: data(I) - pointer to natget structure with kernel pointer */ 10882393Syz155240 /* get the size of. */ 10892393Syz155240 /* */ 10902393Syz155240 /* Handle SIOCSTGSZ. */ 10912393Syz155240 /* Return the size of the nat list entry to be copied back to user space. */ 10922393Syz155240 /* The size of the entry is stored in the ng_sz field and the enture natget */ 10932393Syz155240 /* structure is copied back to the user. */ 10942393Syz155240 /* ------------------------------------------------------------------------ */ 10952393Syz155240 static int fr_natgetsz(data) 10962393Syz155240 caddr_t data; 10972393Syz155240 { 10982393Syz155240 ap_session_t *aps; 10992393Syz155240 nat_t *nat, *n; 11002393Syz155240 natget_t ng; 11012393Syz155240 11022393Syz155240 BCOPYIN(data, &ng, sizeof(ng)); 11032393Syz155240 11042393Syz155240 nat = ng.ng_ptr; 11052393Syz155240 if (!nat) { 11062393Syz155240 nat = nat_instances; 11072393Syz155240 ng.ng_sz = 0; 11082393Syz155240 /* 11092393Syz155240 * Empty list so the size returned is 0. Simple. 11102393Syz155240 */ 11112393Syz155240 if (nat == NULL) { 11122393Syz155240 BCOPYOUT(&ng, data, sizeof(ng)); 11132393Syz155240 return 0; 11142393Syz155240 } 11152393Syz155240 } else { 11162393Syz155240 /* 11172393Syz155240 * Make sure the pointer we're copying from exists in the 11182393Syz155240 * current list of entries. Security precaution to prevent 11192393Syz155240 * copying of random kernel data. 11202393Syz155240 */ 11212393Syz155240 for (n = nat_instances; n; n = n->nat_next) 11222393Syz155240 if (n == nat) 11232393Syz155240 break; 11242393Syz155240 if (!n) 11252393Syz155240 return ESRCH; 11262393Syz155240 } 11272393Syz155240 11282393Syz155240 /* 11292393Syz155240 * Incluse any space required for proxy data structures. 11302393Syz155240 */ 11312393Syz155240 ng.ng_sz = sizeof(nat_save_t); 11322393Syz155240 aps = nat->nat_aps; 11332393Syz155240 if (aps != NULL) { 11342393Syz155240 ng.ng_sz += sizeof(ap_session_t) - 4; 11352393Syz155240 if (aps->aps_data != 0) 11362393Syz155240 ng.ng_sz += aps->aps_psiz; 11372393Syz155240 } 11382393Syz155240 11392393Syz155240 BCOPYOUT(&ng, data, sizeof(ng)); 11402393Syz155240 return 0; 11412393Syz155240 } 11422393Syz155240 11432393Syz155240 11442393Syz155240 /* ------------------------------------------------------------------------ */ 11452393Syz155240 /* Function: fr_natgetent */ 11462393Syz155240 /* Returns: int - 0 == success, != 0 is the error value. */ 11472393Syz155240 /* Parameters: data(I) - pointer to natget structure with kernel pointer */ 11482393Syz155240 /* to NAT structure to copy out. */ 11492393Syz155240 /* */ 11502393Syz155240 /* Handle SIOCSTGET. */ 11512393Syz155240 /* Copies out NAT entry to user space. Any additional data held for a */ 11522393Syz155240 /* proxy is also copied, as to is the NAT rule which was responsible for it */ 11532393Syz155240 /* ------------------------------------------------------------------------ */ 11542393Syz155240 static int fr_natgetent(data) 11552393Syz155240 caddr_t data; 11562393Syz155240 { 11572393Syz155240 int error, outsize; 11582393Syz155240 ap_session_t *aps; 11592393Syz155240 nat_save_t *ipn, ipns; 11602393Syz155240 nat_t *n, *nat; 11612393Syz155240 11622393Syz155240 error = fr_inobj(data, &ipns, IPFOBJ_NATSAVE); 11632393Syz155240 if (error != 0) 11642393Syz155240 return error; 11652393Syz155240 11662393Syz155240 if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) 11672393Syz155240 return EINVAL; 11682393Syz155240 11692393Syz155240 KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize); 11702393Syz155240 if (ipn == NULL) 11712393Syz155240 return ENOMEM; 11722393Syz155240 11732393Syz155240 ipn->ipn_dsize = ipns.ipn_dsize; 11742393Syz155240 nat = ipns.ipn_next; 11752393Syz155240 if (nat == NULL) { 11762393Syz155240 nat = nat_instances; 11772393Syz155240 if (nat == NULL) { 11782393Syz155240 if (nat_instances == NULL) 11792393Syz155240 error = ENOENT; 11802393Syz155240 goto finished; 11812393Syz155240 } 11822393Syz155240 } else { 11832393Syz155240 /* 11842393Syz155240 * Make sure the pointer we're copying from exists in the 11852393Syz155240 * current list of entries. Security precaution to prevent 11862393Syz155240 * copying of random kernel data. 11872393Syz155240 */ 11882393Syz155240 for (n = nat_instances; n; n = n->nat_next) 11892393Syz155240 if (n == nat) 11902393Syz155240 break; 11912393Syz155240 if (n == NULL) { 11922393Syz155240 error = ESRCH; 11932393Syz155240 goto finished; 11942393Syz155240 } 11952393Syz155240 } 11962393Syz155240 ipn->ipn_next = nat->nat_next; 11972393Syz155240 11982393Syz155240 /* 11992393Syz155240 * Copy the NAT structure. 12002393Syz155240 */ 12012393Syz155240 bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat)); 12022393Syz155240 12032393Syz155240 /* 12042393Syz155240 * If we have a pointer to the NAT rule it belongs to, save that too. 12052393Syz155240 */ 12062393Syz155240 if (nat->nat_ptr != NULL) 12072393Syz155240 bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat, 12082393Syz155240 sizeof(ipn->ipn_ipnat)); 12092393Syz155240 12102393Syz155240 /* 12112393Syz155240 * If we also know the NAT entry has an associated filter rule, 12122393Syz155240 * save that too. 12132393Syz155240 */ 12142393Syz155240 if (nat->nat_fr != NULL) 12152393Syz155240 bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr, 12162393Syz155240 sizeof(ipn->ipn_fr)); 12172393Syz155240 12182393Syz155240 /* 12192393Syz155240 * Last but not least, if there is an application proxy session set 12202393Syz155240 * up for this NAT entry, then copy that out too, including any 12212393Syz155240 * private data saved along side it by the proxy. 12222393Syz155240 */ 12232393Syz155240 aps = nat->nat_aps; 12242393Syz155240 outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data); 12252393Syz155240 if (aps != NULL) { 12262393Syz155240 char *s; 12272393Syz155240 12282393Syz155240 if (outsize < sizeof(*aps)) { 12292393Syz155240 error = ENOBUFS; 12302393Syz155240 goto finished; 12312393Syz155240 } 12322393Syz155240 12332393Syz155240 s = ipn->ipn_data; 12342393Syz155240 bcopy((char *)aps, s, sizeof(*aps)); 12352393Syz155240 s += sizeof(*aps); 12362393Syz155240 outsize -= sizeof(*aps); 12372393Syz155240 if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz)) 12382393Syz155240 bcopy(aps->aps_data, s, aps->aps_psiz); 12392393Syz155240 else 12402393Syz155240 error = ENOBUFS; 12412393Syz155240 } 12422393Syz155240 if (error == 0) { 12432393Syz155240 error = fr_outobjsz(data, ipn, IPFOBJ_NATSAVE, ipns.ipn_dsize); 12442393Syz155240 } 12452393Syz155240 12462393Syz155240 finished: 12472393Syz155240 if (ipn != NULL) { 12482393Syz155240 KFREES(ipn, ipns.ipn_dsize); 12492393Syz155240 } 12502393Syz155240 return error; 12512393Syz155240 } 12522393Syz155240 12532393Syz155240 12542393Syz155240 /* ------------------------------------------------------------------------ */ 12552393Syz155240 /* Function: fr_natputent */ 12562393Syz155240 /* Returns: int - 0 == success, != 0 is the error value. */ 12572393Syz155240 /* Parameters: data(I) - pointer to natget structure with NAT */ 12582393Syz155240 /* structure information to load into the kernel */ 12592393Syz155240 /* getlock(I) - flag indicating whether or not a write lock */ 12602393Syz155240 /* on ipf_nat is already held. */ 12612393Syz155240 /* */ 12622393Syz155240 /* Handle SIOCSTPUT. */ 12632393Syz155240 /* Loads a NAT table entry from user space, including a NAT rule, proxy and */ 12642393Syz155240 /* firewall rule data structures, if pointers to them indicate so. */ 12652393Syz155240 /* ------------------------------------------------------------------------ */ 12662393Syz155240 static int fr_natputent(data, getlock) 12672393Syz155240 caddr_t data; 12682393Syz155240 int getlock; 12692393Syz155240 { 12702393Syz155240 nat_save_t ipn, *ipnn; 12712393Syz155240 ap_session_t *aps; 12722393Syz155240 nat_t *n, *nat; 12732393Syz155240 frentry_t *fr; 12742393Syz155240 fr_info_t fin; 12752393Syz155240 ipnat_t *in; 12762393Syz155240 int error; 12772393Syz155240 12782393Syz155240 error = fr_inobj(data, &ipn, IPFOBJ_NATSAVE); 12792393Syz155240 if (error != 0) 12802393Syz155240 return error; 12812393Syz155240 12822393Syz155240 /* 12832393Syz155240 * Initialise early because of code at junkput label. 12842393Syz155240 */ 12852393Syz155240 in = NULL; 12862393Syz155240 aps = NULL; 12872393Syz155240 nat = NULL; 12882393Syz155240 ipnn = NULL; 12892393Syz155240 12902393Syz155240 /* 12912393Syz155240 * New entry, copy in the rest of the NAT entry if it's size is more 12922393Syz155240 * than just the nat_t structure. 12932393Syz155240 */ 12942393Syz155240 fr = NULL; 12952393Syz155240 if (ipn.ipn_dsize > sizeof(ipn)) { 12962393Syz155240 if (ipn.ipn_dsize > 81920) { 12972393Syz155240 error = ENOMEM; 12982393Syz155240 goto junkput; 12992393Syz155240 } 13002393Syz155240 13012393Syz155240 KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize); 13022393Syz155240 if (ipnn == NULL) 13032393Syz155240 return ENOMEM; 13042393Syz155240 13052393Syz155240 error = fr_inobjsz(data, ipnn, IPFOBJ_NATSAVE, ipn.ipn_dsize); 13062393Syz155240 if (error != 0) { 13072393Syz155240 error = EFAULT; 13082393Syz155240 goto junkput; 13092393Syz155240 } 13102393Syz155240 } else 13112393Syz155240 ipnn = &ipn; 13122393Syz155240 13132393Syz155240 KMALLOC(nat, nat_t *); 13142393Syz155240 if (nat == NULL) { 13152393Syz155240 error = ENOMEM; 13162393Syz155240 goto junkput; 13172393Syz155240 } 13182393Syz155240 13192393Syz155240 bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat)); 13202393Syz155240 /* 13212393Syz155240 * Initialize all these so that nat_delete() doesn't cause a crash. 13222393Syz155240 */ 13232393Syz155240 bzero((char *)nat, offsetof(struct nat, nat_tqe)); 13242393Syz155240 nat->nat_tqe.tqe_pnext = NULL; 13252393Syz155240 nat->nat_tqe.tqe_next = NULL; 13262393Syz155240 nat->nat_tqe.tqe_ifq = NULL; 13272393Syz155240 nat->nat_tqe.tqe_parent = nat; 13282393Syz155240 13292393Syz155240 /* 13302393Syz155240 * Restore the rule associated with this nat session 13312393Syz155240 */ 13322393Syz155240 in = ipnn->ipn_nat.nat_ptr; 13332393Syz155240 if (in != NULL) { 13342393Syz155240 KMALLOC(in, ipnat_t *); 13352393Syz155240 nat->nat_ptr = in; 13362393Syz155240 if (in == NULL) { 13372393Syz155240 error = ENOMEM; 13382393Syz155240 goto junkput; 13392393Syz155240 } 13402393Syz155240 bzero((char *)in, offsetof(struct ipnat, in_next6)); 13412393Syz155240 bcopy((char *)&ipnn->ipn_ipnat, (char *)in, sizeof(*in)); 13422393Syz155240 in->in_use = 1; 13432393Syz155240 in->in_flags |= IPN_DELETE; 13442393Syz155240 13452393Syz155240 ATOMIC_INC(nat_stats.ns_rules); 13462393Syz155240 13472393Syz155240 nat_resolverule(in); 13482393Syz155240 } 13492393Syz155240 13502393Syz155240 /* 13512393Syz155240 * Check that the NAT entry doesn't already exist in the kernel. 13522393Syz155240 */ 13532393Syz155240 bzero((char *)&fin, sizeof(fin)); 13542393Syz155240 fin.fin_p = nat->nat_p; 13552393Syz155240 if (nat->nat_dir == NAT_OUTBOUND) { 13562393Syz155240 fin.fin_data[0] = ntohs(nat->nat_oport); 13572393Syz155240 fin.fin_data[1] = ntohs(nat->nat_outport); 1358*2508Syz155240 fin.fin_ifp = nat->nat_ifps[0]; 13592393Syz155240 if (getlock) { 13602393Syz155240 READ_ENTER(&ipf_nat); 13612393Syz155240 } 13622393Syz155240 n = nat_inlookup(&fin, nat->nat_flags, fin.fin_p, 13632393Syz155240 nat->nat_oip, nat->nat_outip); 13642393Syz155240 if (getlock) { 13652393Syz155240 RWLOCK_EXIT(&ipf_nat); 13662393Syz155240 } 13672393Syz155240 if (n != NULL) { 13682393Syz155240 error = EEXIST; 13692393Syz155240 goto junkput; 13702393Syz155240 } 13712393Syz155240 } else if (nat->nat_dir == NAT_INBOUND) { 13722393Syz155240 fin.fin_data[0] = ntohs(nat->nat_inport); 13732393Syz155240 fin.fin_data[1] = ntohs(nat->nat_oport); 1374*2508Syz155240 fin.fin_ifp = nat->nat_ifps[1]; 13752393Syz155240 if (getlock) { 13762393Syz155240 READ_ENTER(&ipf_nat); 13772393Syz155240 } 13782393Syz155240 n = nat_outlookup(&fin, nat->nat_flags, fin.fin_p, 1379*2508Syz155240 nat->nat_inip, nat->nat_oip); 13802393Syz155240 if (getlock) { 13812393Syz155240 RWLOCK_EXIT(&ipf_nat); 13822393Syz155240 } 13832393Syz155240 if (n != NULL) { 13842393Syz155240 error = EEXIST; 13852393Syz155240 goto junkput; 13862393Syz155240 } 13872393Syz155240 } else { 13882393Syz155240 error = EINVAL; 13892393Syz155240 goto junkput; 13902393Syz155240 } 13912393Syz155240 13922393Syz155240 /* 13932393Syz155240 * Restore ap_session_t structure. Include the private data allocated 13942393Syz155240 * if it was there. 13952393Syz155240 */ 13962393Syz155240 aps = nat->nat_aps; 13972393Syz155240 if (aps != NULL) { 13982393Syz155240 KMALLOC(aps, ap_session_t *); 13992393Syz155240 nat->nat_aps = aps; 14002393Syz155240 if (aps == NULL) { 14012393Syz155240 error = ENOMEM; 14022393Syz155240 goto junkput; 14032393Syz155240 } 14042393Syz155240 bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps)); 14052393Syz155240 if (in != NULL) 14062393Syz155240 aps->aps_apr = in->in_apr; 14072393Syz155240 else 14082393Syz155240 aps->aps_apr = NULL; 14092393Syz155240 if (aps->aps_psiz != 0) { 14102393Syz155240 if (aps->aps_psiz > 81920) { 14112393Syz155240 error = ENOMEM; 14122393Syz155240 goto junkput; 14132393Syz155240 } 14142393Syz155240 KMALLOCS(aps->aps_data, void *, aps->aps_psiz); 14152393Syz155240 if (aps->aps_data == NULL) { 14162393Syz155240 error = ENOMEM; 14172393Syz155240 goto junkput; 14182393Syz155240 } 14192393Syz155240 bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data, 14202393Syz155240 aps->aps_psiz); 14212393Syz155240 } else { 14222393Syz155240 aps->aps_psiz = 0; 14232393Syz155240 aps->aps_data = NULL; 14242393Syz155240 } 14252393Syz155240 } 14262393Syz155240 14272393Syz155240 /* 14282393Syz155240 * If there was a filtering rule associated with this entry then 14292393Syz155240 * build up a new one. 14302393Syz155240 */ 14312393Syz155240 fr = nat->nat_fr; 14322393Syz155240 if (fr != NULL) { 14332393Syz155240 if ((nat->nat_flags & SI_NEWFR) != 0) { 14342393Syz155240 KMALLOC(fr, frentry_t *); 14352393Syz155240 nat->nat_fr = fr; 14362393Syz155240 if (fr == NULL) { 14372393Syz155240 error = ENOMEM; 14382393Syz155240 goto junkput; 14392393Syz155240 } 14402393Syz155240 ipnn->ipn_nat.nat_fr = fr; 14412393Syz155240 fr->fr_ref = 1; 14422393Syz155240 (void) fr_outobj(data, ipnn, IPFOBJ_NATSAVE); 14432393Syz155240 bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr)); 14442393Syz155240 MUTEX_NUKE(&fr->fr_lock); 14452393Syz155240 MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock"); 14462393Syz155240 } else { 14472393Syz155240 READ_ENTER(&ipf_nat); 14482393Syz155240 for (n = nat_instances; n; n = n->nat_next) 14492393Syz155240 if (n->nat_fr == fr) 14502393Syz155240 break; 14512393Syz155240 14522393Syz155240 if (n != NULL) { 14532393Syz155240 MUTEX_ENTER(&fr->fr_lock); 14542393Syz155240 fr->fr_ref++; 14552393Syz155240 MUTEX_EXIT(&fr->fr_lock); 14562393Syz155240 } 14572393Syz155240 RWLOCK_EXIT(&ipf_nat); 14582393Syz155240 14592393Syz155240 if (!n) { 14602393Syz155240 error = ESRCH; 14612393Syz155240 goto junkput; 14622393Syz155240 } 14632393Syz155240 } 14642393Syz155240 } 14652393Syz155240 14662393Syz155240 if (ipnn != &ipn) { 14672393Syz155240 KFREES(ipnn, ipn.ipn_dsize); 14682393Syz155240 ipnn = NULL; 14692393Syz155240 } 14702393Syz155240 14712393Syz155240 if (getlock) { 14722393Syz155240 WRITE_ENTER(&ipf_nat); 14732393Syz155240 } 14742393Syz155240 error = nat_insert(nat, nat->nat_rev); 14752393Syz155240 if ((error == 0) && (aps != NULL)) { 14762393Syz155240 aps->aps_next = ap_sess_list; 14772393Syz155240 ap_sess_list = aps; 14782393Syz155240 } 14792393Syz155240 if (getlock) { 14802393Syz155240 RWLOCK_EXIT(&ipf_nat); 14812393Syz155240 } 14822393Syz155240 14832393Syz155240 if (error == 0) 14842393Syz155240 return 0; 14852393Syz155240 14862393Syz155240 error = ENOMEM; 14872393Syz155240 14882393Syz155240 junkput: 14892393Syz155240 if (fr != NULL) 14902393Syz155240 (void) fr_derefrule(&fr); 14912393Syz155240 14922393Syz155240 if ((ipnn != NULL) && (ipnn != &ipn)) { 14932393Syz155240 KFREES(ipnn, ipn.ipn_dsize); 14942393Syz155240 } 14952393Syz155240 if (nat != NULL) { 14962393Syz155240 if (aps != NULL) { 14972393Syz155240 if (aps->aps_data != NULL) { 14982393Syz155240 KFREES(aps->aps_data, aps->aps_psiz); 14992393Syz155240 } 15002393Syz155240 KFREE(aps); 15012393Syz155240 } 15022393Syz155240 if (in != NULL) { 15032393Syz155240 if (in->in_apr) 15042393Syz155240 appr_free(in->in_apr); 15052393Syz155240 KFREE(in); 15062393Syz155240 } 15072393Syz155240 KFREE(nat); 15082393Syz155240 } 15092393Syz155240 return error; 15102393Syz155240 } 15112393Syz155240 15122393Syz155240 15132393Syz155240 /* ------------------------------------------------------------------------ */ 15142393Syz155240 /* Function: nat_delete */ 15152393Syz155240 /* Returns: Nil */ 15162393Syz155240 /* Parameters: natd(I) - pointer to NAT structure to delete */ 15172393Syz155240 /* logtype(I) - type of LOG record to create before deleting */ 15182393Syz155240 /* Write Lock: ipf_nat */ 15192393Syz155240 /* */ 15202393Syz155240 /* Delete a nat entry from the various lists and table. If NAT logging is */ 15212393Syz155240 /* enabled then generate a NAT log record for this event. */ 15222393Syz155240 /* ------------------------------------------------------------------------ */ 15232393Syz155240 static void nat_delete(nat, logtype) 15242393Syz155240 struct nat *nat; 15252393Syz155240 int logtype; 15262393Syz155240 { 15272393Syz155240 struct ipnat *ipn; 15282393Syz155240 15292393Syz155240 if (logtype != 0 && nat_logging != 0) 15302393Syz155240 nat_log(nat, logtype); 15312393Syz155240 15322393Syz155240 MUTEX_ENTER(&ipf_nat_new); 15332393Syz155240 15342393Syz155240 /* 15352393Syz155240 * Take it as a general indication that all the pointers are set if 15362393Syz155240 * nat_pnext is set. 15372393Syz155240 */ 15382393Syz155240 if (nat->nat_pnext != NULL) { 15392393Syz155240 nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--; 15402393Syz155240 nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--; 15412393Syz155240 15422393Syz155240 *nat->nat_pnext = nat->nat_next; 15432393Syz155240 if (nat->nat_next != NULL) { 15442393Syz155240 nat->nat_next->nat_pnext = nat->nat_pnext; 15452393Syz155240 nat->nat_next = NULL; 15462393Syz155240 } 15472393Syz155240 nat->nat_pnext = NULL; 15482393Syz155240 15492393Syz155240 *nat->nat_phnext[0] = nat->nat_hnext[0]; 15502393Syz155240 if (nat->nat_hnext[0] != NULL) { 15512393Syz155240 nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 15522393Syz155240 nat->nat_hnext[0] = NULL; 15532393Syz155240 } 15542393Syz155240 nat->nat_phnext[0] = NULL; 15552393Syz155240 15562393Syz155240 *nat->nat_phnext[1] = nat->nat_hnext[1]; 15572393Syz155240 if (nat->nat_hnext[1] != NULL) { 15582393Syz155240 nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 15592393Syz155240 nat->nat_hnext[1] = NULL; 15602393Syz155240 } 15612393Syz155240 nat->nat_phnext[1] = NULL; 15622393Syz155240 15632393Syz155240 if ((nat->nat_flags & SI_WILDP) != 0) 15642393Syz155240 nat_stats.ns_wilds--; 15652393Syz155240 } 15662393Syz155240 15672393Syz155240 if (nat->nat_me != NULL) { 15682393Syz155240 *nat->nat_me = NULL; 15692393Syz155240 nat->nat_me = NULL; 15702393Syz155240 } 15712393Syz155240 15722393Syz155240 fr_deletequeueentry(&nat->nat_tqe); 15732393Syz155240 15742393Syz155240 nat->nat_ref--; 15752393Syz155240 if (nat->nat_ref > 0) { 15762393Syz155240 MUTEX_EXIT(&ipf_nat_new); 15772393Syz155240 return; 15782393Syz155240 } 15792393Syz155240 15802393Syz155240 #ifdef IPFILTER_SYNC 15812393Syz155240 if (nat->nat_sync) 15822393Syz155240 ipfsync_del(nat->nat_sync); 15832393Syz155240 #endif 15842393Syz155240 15852393Syz155240 if (nat->nat_fr != NULL) 15862393Syz155240 (void)fr_derefrule(&nat->nat_fr); 15872393Syz155240 15882393Syz155240 if (nat->nat_hm != NULL) 15892393Syz155240 nat_hostmapdel(nat->nat_hm); 15902393Syz155240 15912393Syz155240 /* 15922393Syz155240 * If there is an active reference from the nat entry to its parent 15932393Syz155240 * rule, decrement the rule's reference count and free it too if no 15942393Syz155240 * longer being used. 15952393Syz155240 */ 15962393Syz155240 ipn = nat->nat_ptr; 15972393Syz155240 if (ipn != NULL) { 15982393Syz155240 ipn->in_space++; 15992393Syz155240 ipn->in_use--; 16002393Syz155240 if (ipn->in_use == 0 && (ipn->in_flags & IPN_DELETE)) { 16012393Syz155240 if (ipn->in_apr) 16022393Syz155240 appr_free(ipn->in_apr); 16032393Syz155240 KFREE(ipn); 16042393Syz155240 nat_stats.ns_rules--; 16052393Syz155240 #if SOLARIS 16062393Syz155240 if (nat_stats.ns_rules == 0) 16072393Syz155240 pfil_delayed_copy = 1; 16082393Syz155240 #endif 16092393Syz155240 } 16102393Syz155240 } 16112393Syz155240 16122393Syz155240 MUTEX_DESTROY(&nat->nat_lock); 16132393Syz155240 16142393Syz155240 aps_free(nat->nat_aps); 16152393Syz155240 nat_stats.ns_inuse--; 16162393Syz155240 MUTEX_EXIT(&ipf_nat_new); 16172393Syz155240 16182393Syz155240 /* 16192393Syz155240 * If there's a fragment table entry too for this nat entry, then 16202393Syz155240 * dereference that as well. This is after nat_lock is released 16212393Syz155240 * because of Tru64. 16222393Syz155240 */ 16232393Syz155240 fr_forgetnat((void *)nat); 16242393Syz155240 16252393Syz155240 KFREE(nat); 16262393Syz155240 } 16272393Syz155240 16282393Syz155240 16292393Syz155240 /* ------------------------------------------------------------------------ */ 16302393Syz155240 /* Function: nat_flushtable */ 16312393Syz155240 /* Returns: int - number of NAT rules deleted */ 16322393Syz155240 /* Parameters: Nil */ 16332393Syz155240 /* */ 16342393Syz155240 /* Deletes all currently active NAT sessions. In deleting each NAT entry a */ 16352393Syz155240 /* log record should be emitted in nat_delete() if NAT logging is enabled. */ 16362393Syz155240 /* ------------------------------------------------------------------------ */ 16372393Syz155240 /* 16382393Syz155240 * nat_flushtable - clear the NAT table of all mapping entries. 16392393Syz155240 */ 16402393Syz155240 static int nat_flushtable() 16412393Syz155240 { 16422393Syz155240 nat_t *nat; 16432393Syz155240 int j = 0; 16442393Syz155240 16452393Syz155240 /* 16462393Syz155240 * ALL NAT mappings deleted, so lets just make the deletions 16472393Syz155240 * quicker. 16482393Syz155240 */ 16492393Syz155240 if (nat_table[0] != NULL) 16502393Syz155240 bzero((char *)nat_table[0], 16512393Syz155240 sizeof(nat_table[0]) * ipf_nattable_sz); 16522393Syz155240 if (nat_table[1] != NULL) 16532393Syz155240 bzero((char *)nat_table[1], 16542393Syz155240 sizeof(nat_table[1]) * ipf_nattable_sz); 16552393Syz155240 16562393Syz155240 while ((nat = nat_instances) != NULL) { 16572393Syz155240 nat_delete(nat, NL_FLUSH); 16582393Syz155240 j++; 16592393Syz155240 } 16602393Syz155240 16612393Syz155240 nat_stats.ns_inuse = 0; 16622393Syz155240 return j; 16632393Syz155240 } 16642393Syz155240 16652393Syz155240 16662393Syz155240 /* ------------------------------------------------------------------------ */ 16672393Syz155240 /* Function: nat_clearlist */ 16682393Syz155240 /* Returns: int - number of NAT/RDR rules deleted */ 16692393Syz155240 /* Parameters: Nil */ 16702393Syz155240 /* */ 16712393Syz155240 /* Delete all rules in the current list of rules. There is nothing elegant */ 16722393Syz155240 /* about this cleanup: simply free all entries on the list of rules and */ 16732393Syz155240 /* clear out the tables used for hashed NAT rule lookups. */ 16742393Syz155240 /* ------------------------------------------------------------------------ */ 16752393Syz155240 static int nat_clearlist() 16762393Syz155240 { 16772393Syz155240 ipnat_t *n, **np = &nat_list; 16782393Syz155240 int i = 0; 16792393Syz155240 16802393Syz155240 if (nat_rules != NULL) 16812393Syz155240 bzero((char *)nat_rules, sizeof(*nat_rules) * ipf_natrules_sz); 16822393Syz155240 if (rdr_rules != NULL) 16832393Syz155240 bzero((char *)rdr_rules, sizeof(*rdr_rules) * ipf_rdrrules_sz); 16842393Syz155240 16852393Syz155240 while ((n = *np) != NULL) { 16862393Syz155240 *np = n->in_next; 16872393Syz155240 if (n->in_use == 0) { 16882393Syz155240 if (n->in_apr != NULL) 16892393Syz155240 appr_free(n->in_apr); 16902393Syz155240 KFREE(n); 16912393Syz155240 nat_stats.ns_rules--; 16922393Syz155240 } else { 16932393Syz155240 n->in_flags |= IPN_DELETE; 16942393Syz155240 n->in_next = NULL; 16952393Syz155240 } 16962393Syz155240 i++; 16972393Syz155240 } 16982393Syz155240 #if SOLARIS 16992393Syz155240 pfil_delayed_copy = 1; 17002393Syz155240 #endif 17012393Syz155240 nat_masks = 0; 17022393Syz155240 rdr_masks = 0; 17032393Syz155240 return i; 17042393Syz155240 } 17052393Syz155240 17062393Syz155240 17072393Syz155240 /* ------------------------------------------------------------------------ */ 17082393Syz155240 /* Function: nat_newmap */ 17092393Syz155240 /* Returns: int - -1 == error, 0 == success */ 17102393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 17112393Syz155240 /* nat(I) - pointer to NAT entry */ 17122393Syz155240 /* ni(I) - pointer to structure with misc. information needed */ 17132393Syz155240 /* to create new NAT entry. */ 17142393Syz155240 /* */ 17152393Syz155240 /* Given an empty NAT structure, populate it with new information about a */ 17162393Syz155240 /* new NAT session, as defined by the matching NAT rule. */ 17172393Syz155240 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 17182393Syz155240 /* to the new IP address for the translation. */ 17192393Syz155240 /* ------------------------------------------------------------------------ */ 17202393Syz155240 static INLINE int nat_newmap(fin, nat, ni) 17212393Syz155240 fr_info_t *fin; 17222393Syz155240 nat_t *nat; 17232393Syz155240 natinfo_t *ni; 17242393Syz155240 { 17252393Syz155240 u_short st_port, dport, sport, port, sp, dp; 17262393Syz155240 struct in_addr in, inb; 17272393Syz155240 hostmap_t *hm; 17282393Syz155240 u_32_t flags; 17292393Syz155240 u_32_t st_ip; 17302393Syz155240 ipnat_t *np; 17312393Syz155240 nat_t *natl; 17322393Syz155240 int l; 17332393Syz155240 17342393Syz155240 /* 17352393Syz155240 * If it's an outbound packet which doesn't match any existing 17362393Syz155240 * record, then create a new port 17372393Syz155240 */ 17382393Syz155240 l = 0; 17392393Syz155240 hm = NULL; 17402393Syz155240 np = ni->nai_np; 17412393Syz155240 st_ip = np->in_nip; 17422393Syz155240 st_port = np->in_pnext; 17432393Syz155240 flags = ni->nai_flags; 17442393Syz155240 sport = ni->nai_sport; 17452393Syz155240 dport = ni->nai_dport; 17462393Syz155240 17472393Syz155240 /* 17482393Syz155240 * Do a loop until we either run out of entries to try or we find 17492393Syz155240 * a NAT mapping that isn't currently being used. This is done 17502393Syz155240 * because the change to the source is not (usually) being fixed. 17512393Syz155240 */ 17522393Syz155240 do { 17532393Syz155240 port = 0; 17542393Syz155240 in.s_addr = htonl(np->in_nip); 17552393Syz155240 if (l == 0) { 17562393Syz155240 /* 17572393Syz155240 * Check to see if there is an existing NAT 17582393Syz155240 * setup for this IP address pair. 17592393Syz155240 */ 17602393Syz155240 hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, 17612393Syz155240 in, 0); 17622393Syz155240 if (hm != NULL) 17632393Syz155240 in.s_addr = hm->hm_mapip.s_addr; 17642393Syz155240 } else if ((l == 1) && (hm != NULL)) { 17652393Syz155240 nat_hostmapdel(hm); 17662393Syz155240 hm = NULL; 17672393Syz155240 } 17682393Syz155240 in.s_addr = ntohl(in.s_addr); 17692393Syz155240 17702393Syz155240 nat->nat_hm = hm; 17712393Syz155240 17722393Syz155240 if ((np->in_outmsk == 0xffffffff) && (np->in_pnext == 0)) { 17732393Syz155240 if (l > 0) 17742393Syz155240 return -1; 17752393Syz155240 } 17762393Syz155240 17772393Syz155240 if (np->in_redir == NAT_BIMAP && 17782393Syz155240 np->in_inmsk == np->in_outmsk) { 17792393Syz155240 /* 17802393Syz155240 * map the address block in a 1:1 fashion 17812393Syz155240 */ 17822393Syz155240 in.s_addr = np->in_outip; 17832393Syz155240 in.s_addr |= fin->fin_saddr & ~np->in_inmsk; 17842393Syz155240 in.s_addr = ntohl(in.s_addr); 17852393Syz155240 17862393Syz155240 } else if (np->in_redir & NAT_MAPBLK) { 17872393Syz155240 if ((l >= np->in_ppip) || ((l > 0) && 17882393Syz155240 !(flags & IPN_TCPUDP))) 17892393Syz155240 return -1; 17902393Syz155240 /* 17912393Syz155240 * map-block - Calculate destination address. 17922393Syz155240 */ 17932393Syz155240 in.s_addr = ntohl(fin->fin_saddr); 17942393Syz155240 in.s_addr &= ntohl(~np->in_inmsk); 17952393Syz155240 inb.s_addr = in.s_addr; 17962393Syz155240 in.s_addr /= np->in_ippip; 17972393Syz155240 in.s_addr &= ntohl(~np->in_outmsk); 17982393Syz155240 in.s_addr += ntohl(np->in_outip); 17992393Syz155240 /* 18002393Syz155240 * Calculate destination port. 18012393Syz155240 */ 18022393Syz155240 if ((flags & IPN_TCPUDP) && 18032393Syz155240 (np->in_ppip != 0)) { 18042393Syz155240 port = ntohs(sport) + l; 18052393Syz155240 port %= np->in_ppip; 18062393Syz155240 port += np->in_ppip * 18072393Syz155240 (inb.s_addr % np->in_ippip); 18082393Syz155240 port += MAPBLK_MINPORT; 18092393Syz155240 port = htons(port); 18102393Syz155240 } 18112393Syz155240 18122393Syz155240 } else if ((np->in_outip == 0) && 18132393Syz155240 (np->in_outmsk == 0xffffffff)) { 18142393Syz155240 /* 18152393Syz155240 * 0/32 - use the interface's IP address. 18162393Syz155240 */ 18172393Syz155240 if ((l > 0) || 18182393Syz155240 fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp, 18192393Syz155240 &in, NULL) == -1) 18202393Syz155240 return -1; 18212393Syz155240 in.s_addr = ntohl(in.s_addr); 18222393Syz155240 18232393Syz155240 } else if ((np->in_outip == 0) && (np->in_outmsk == 0)) { 18242393Syz155240 /* 18252393Syz155240 * 0/0 - use the original source address/port. 18262393Syz155240 */ 18272393Syz155240 if (l > 0) 18282393Syz155240 return -1; 18292393Syz155240 in.s_addr = ntohl(fin->fin_saddr); 18302393Syz155240 18312393Syz155240 } else if ((np->in_outmsk != 0xffffffff) && 18322393Syz155240 (np->in_pnext == 0) && ((l > 0) || (hm == NULL))) 18332393Syz155240 np->in_nip++; 18342393Syz155240 18352393Syz155240 natl = NULL; 18362393Syz155240 18372393Syz155240 if ((flags & IPN_TCPUDP) && 18382393Syz155240 ((np->in_redir & NAT_MAPBLK) == 0) && 18392393Syz155240 (np->in_flags & IPN_AUTOPORTMAP)) { 18402393Syz155240 /* 18412393Syz155240 * "ports auto" (without map-block) 18422393Syz155240 */ 18432393Syz155240 if ((l > 0) && (l % np->in_ppip == 0)) { 18442393Syz155240 if (l > np->in_space) { 18452393Syz155240 return -1; 18462393Syz155240 } else if ((l > np->in_ppip) && 18472393Syz155240 np->in_outmsk != 0xffffffff) 18482393Syz155240 np->in_nip++; 18492393Syz155240 } 18502393Syz155240 if (np->in_ppip != 0) { 18512393Syz155240 port = ntohs(sport); 18522393Syz155240 port += (l % np->in_ppip); 18532393Syz155240 port %= np->in_ppip; 18542393Syz155240 port += np->in_ppip * 18552393Syz155240 (ntohl(fin->fin_saddr) % 18562393Syz155240 np->in_ippip); 18572393Syz155240 port += MAPBLK_MINPORT; 18582393Syz155240 port = htons(port); 18592393Syz155240 } 18602393Syz155240 18612393Syz155240 } else if (((np->in_redir & NAT_MAPBLK) == 0) && 18622393Syz155240 (flags & IPN_TCPUDPICMP) && (np->in_pnext != 0)) { 18632393Syz155240 /* 18642393Syz155240 * Standard port translation. Select next port. 18652393Syz155240 */ 18662393Syz155240 port = htons(np->in_pnext++); 18672393Syz155240 18682393Syz155240 if (np->in_pnext > ntohs(np->in_pmax)) { 18692393Syz155240 np->in_pnext = ntohs(np->in_pmin); 18702393Syz155240 if (np->in_outmsk != 0xffffffff) 18712393Syz155240 np->in_nip++; 18722393Syz155240 } 18732393Syz155240 } 18742393Syz155240 18752393Syz155240 if (np->in_flags & IPN_IPRANGE) { 18762393Syz155240 if (np->in_nip > ntohl(np->in_outmsk)) 18772393Syz155240 np->in_nip = ntohl(np->in_outip); 18782393Syz155240 } else { 18792393Syz155240 if ((np->in_outmsk != 0xffffffff) && 18802393Syz155240 ((np->in_nip + 1) & ntohl(np->in_outmsk)) > 18812393Syz155240 ntohl(np->in_outip)) 18822393Syz155240 np->in_nip = ntohl(np->in_outip) + 1; 18832393Syz155240 } 18842393Syz155240 18852393Syz155240 if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) 18862393Syz155240 port = sport; 18872393Syz155240 18882393Syz155240 /* 18892393Syz155240 * Here we do a lookup of the connection as seen from 18902393Syz155240 * the outside. If an IP# pair already exists, try 18912393Syz155240 * again. So if you have A->B becomes C->B, you can 18922393Syz155240 * also have D->E become C->E but not D->B causing 18932393Syz155240 * another C->B. Also take protocol and ports into 18942393Syz155240 * account when determining whether a pre-existing 18952393Syz155240 * NAT setup will cause an external conflict where 18962393Syz155240 * this is appropriate. 18972393Syz155240 */ 18982393Syz155240 inb.s_addr = htonl(in.s_addr); 18992393Syz155240 sp = fin->fin_data[0]; 19002393Syz155240 dp = fin->fin_data[1]; 19012393Syz155240 fin->fin_data[0] = fin->fin_data[1]; 19022393Syz155240 fin->fin_data[1] = htons(port); 19032393Syz155240 natl = nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 19042393Syz155240 (u_int)fin->fin_p, fin->fin_dst, inb); 19052393Syz155240 fin->fin_data[0] = sp; 19062393Syz155240 fin->fin_data[1] = dp; 19072393Syz155240 19082393Syz155240 /* 19092393Syz155240 * Has the search wrapped around and come back to the 19102393Syz155240 * start ? 19112393Syz155240 */ 19122393Syz155240 if ((natl != NULL) && 19132393Syz155240 (np->in_pnext != 0) && (st_port == np->in_pnext) && 19142393Syz155240 (np->in_nip != 0) && (st_ip == np->in_nip)) 19152393Syz155240 return -1; 19162393Syz155240 l++; 19172393Syz155240 } while (natl != NULL); 19182393Syz155240 19192393Syz155240 if (np->in_space > 0) 19202393Syz155240 np->in_space--; 19212393Syz155240 19222393Syz155240 /* Setup the NAT table */ 19232393Syz155240 nat->nat_inip = fin->fin_src; 19242393Syz155240 nat->nat_outip.s_addr = htonl(in.s_addr); 19252393Syz155240 nat->nat_oip = fin->fin_dst; 19262393Syz155240 if (nat->nat_hm == NULL) 19272393Syz155240 nat->nat_hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, 19282393Syz155240 nat->nat_outip, 0); 19292393Syz155240 19302393Syz155240 /* 19312393Syz155240 * The ICMP checksum does not have a pseudo header containing 19322393Syz155240 * the IP addresses 19332393Syz155240 */ 19342393Syz155240 ni->nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr)); 19352393Syz155240 ni->nai_sum2 = LONG_SUM(in.s_addr); 19362393Syz155240 if ((flags & IPN_TCPUDP)) { 19372393Syz155240 ni->nai_sum1 += ntohs(sport); 19382393Syz155240 ni->nai_sum2 += ntohs(port); 19392393Syz155240 } 19402393Syz155240 19412393Syz155240 if (flags & IPN_TCPUDP) { 19422393Syz155240 nat->nat_inport = sport; 19432393Syz155240 nat->nat_outport = port; /* sport */ 19442393Syz155240 nat->nat_oport = dport; 19452393Syz155240 ((tcphdr_t *)fin->fin_dp)->th_sport = port; 19462393Syz155240 } else if (flags & IPN_ICMPQUERY) { 19472393Syz155240 ((icmphdr_t *)fin->fin_dp)->icmp_id = port; 19482393Syz155240 nat->nat_inport = port; 19492393Syz155240 nat->nat_outport = port; 19502393Syz155240 } 19512393Syz155240 19522393Syz155240 ni->nai_ip.s_addr = in.s_addr; 19532393Syz155240 ni->nai_port = port; 19542393Syz155240 ni->nai_nport = dport; 19552393Syz155240 return 0; 19562393Syz155240 } 19572393Syz155240 19582393Syz155240 19592393Syz155240 /* ------------------------------------------------------------------------ */ 19602393Syz155240 /* Function: nat_newrdr */ 19612393Syz155240 /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ 19622393Syz155240 /* allow rule to be moved if IPN_ROUNDR is set. */ 19632393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 19642393Syz155240 /* nat(I) - pointer to NAT entry */ 19652393Syz155240 /* ni(I) - pointer to structure with misc. information needed */ 19662393Syz155240 /* to create new NAT entry. */ 19672393Syz155240 /* */ 19682393Syz155240 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 19692393Syz155240 /* to the new IP address for the translation. */ 19702393Syz155240 /* ------------------------------------------------------------------------ */ 19712393Syz155240 static INLINE int nat_newrdr(fin, nat, ni) 19722393Syz155240 fr_info_t *fin; 19732393Syz155240 nat_t *nat; 19742393Syz155240 natinfo_t *ni; 19752393Syz155240 { 19762393Syz155240 u_short nport, dport, sport; 19772393Syz155240 struct in_addr in; 19782393Syz155240 hostmap_t *hm; 19792393Syz155240 u_32_t flags; 19802393Syz155240 ipnat_t *np; 19812393Syz155240 int move; 19822393Syz155240 19832393Syz155240 move = 1; 19842393Syz155240 hm = NULL; 19852393Syz155240 in.s_addr = 0; 19862393Syz155240 np = ni->nai_np; 19872393Syz155240 flags = ni->nai_flags; 19882393Syz155240 sport = ni->nai_sport; 19892393Syz155240 dport = ni->nai_dport; 19902393Syz155240 19912393Syz155240 /* 19922393Syz155240 * If the matching rule has IPN_STICKY set, then we want to have the 19932393Syz155240 * same rule kick in as before. Why would this happen? If you have 19942393Syz155240 * a collection of rdr rules with "round-robin sticky", the current 19952393Syz155240 * packet might match a different one to the previous connection but 19962393Syz155240 * we want the same destination to be used. 19972393Syz155240 */ 19982393Syz155240 if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == 19992393Syz155240 (IPN_ROUNDR|IPN_STICKY)) { 20002393Syz155240 hm = nat_hostmap(NULL, fin->fin_src, fin->fin_dst, in, 20012393Syz155240 (u_32_t)dport); 20022393Syz155240 if (hm != NULL) { 20032393Syz155240 in.s_addr = ntohl(hm->hm_mapip.s_addr); 20042393Syz155240 np = hm->hm_ipnat; 20052393Syz155240 ni->nai_np = np; 20062393Syz155240 move = 0; 20072393Syz155240 } 20082393Syz155240 } 20092393Syz155240 20102393Syz155240 /* 20112393Syz155240 * Otherwise, it's an inbound packet. Most likely, we don't 20122393Syz155240 * want to rewrite source ports and source addresses. Instead, 20132393Syz155240 * we want to rewrite to a fixed internal address and fixed 20142393Syz155240 * internal port. 20152393Syz155240 */ 20162393Syz155240 if (np->in_flags & IPN_SPLIT) { 20172393Syz155240 in.s_addr = np->in_nip; 20182393Syz155240 20192393Syz155240 if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { 20202393Syz155240 hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, 20212393Syz155240 in, (u_32_t)dport); 20222393Syz155240 if (hm != NULL) { 20232393Syz155240 in.s_addr = hm->hm_mapip.s_addr; 20242393Syz155240 move = 0; 20252393Syz155240 } 20262393Syz155240 } 20272393Syz155240 20282393Syz155240 if (hm == NULL || hm->hm_ref == 1) { 20292393Syz155240 if (np->in_inip == htonl(in.s_addr)) { 20302393Syz155240 np->in_nip = ntohl(np->in_inmsk); 20312393Syz155240 move = 0; 20322393Syz155240 } else { 20332393Syz155240 np->in_nip = ntohl(np->in_inip); 20342393Syz155240 } 20352393Syz155240 } 20362393Syz155240 20372393Syz155240 } else if ((np->in_inip == 0) && (np->in_inmsk == 0xffffffff)) { 20382393Syz155240 /* 20392393Syz155240 * 0/32 - use the interface's IP address. 20402393Syz155240 */ 20412393Syz155240 if (fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp, &in, NULL) == -1) 20422393Syz155240 return -1; 20432393Syz155240 in.s_addr = ntohl(in.s_addr); 20442393Syz155240 20452393Syz155240 } else if ((np->in_inip == 0) && (np->in_inmsk== 0)) { 20462393Syz155240 /* 20472393Syz155240 * 0/0 - use the original destination address/port. 20482393Syz155240 */ 20492393Syz155240 in.s_addr = ntohl(fin->fin_daddr); 20502393Syz155240 20512393Syz155240 } else if (np->in_redir == NAT_BIMAP && 20522393Syz155240 np->in_inmsk == np->in_outmsk) { 20532393Syz155240 /* 20542393Syz155240 * map the address block in a 1:1 fashion 20552393Syz155240 */ 20562393Syz155240 in.s_addr = np->in_inip; 20572393Syz155240 in.s_addr |= fin->fin_daddr & ~np->in_inmsk; 20582393Syz155240 in.s_addr = ntohl(in.s_addr); 20592393Syz155240 } else { 20602393Syz155240 in.s_addr = ntohl(np->in_inip); 20612393Syz155240 } 20622393Syz155240 20632393Syz155240 if ((np->in_pnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) 20642393Syz155240 nport = dport; 20652393Syz155240 else { 20662393Syz155240 /* 20672393Syz155240 * Whilst not optimized for the case where 20682393Syz155240 * pmin == pmax, the gain is not significant. 20692393Syz155240 */ 20702393Syz155240 if (((np->in_flags & IPN_FIXEDDPORT) == 0) && 20712393Syz155240 (np->in_pmin != np->in_pmax)) { 20722393Syz155240 nport = ntohs(dport) - ntohs(np->in_pmin) + 20732393Syz155240 ntohs(np->in_pnext); 20742393Syz155240 nport = htons(nport); 20752393Syz155240 } else 20762393Syz155240 nport = np->in_pnext; 20772393Syz155240 } 20782393Syz155240 20792393Syz155240 /* 20802393Syz155240 * When the redirect-to address is set to 0.0.0.0, just 20812393Syz155240 * assume a blank `forwarding' of the packet. We don't 20822393Syz155240 * setup any translation for this either. 20832393Syz155240 */ 20842393Syz155240 if (in.s_addr == 0) { 20852393Syz155240 if (nport == dport) 20862393Syz155240 return -1; 20872393Syz155240 in.s_addr = ntohl(fin->fin_daddr); 20882393Syz155240 } 20892393Syz155240 20902393Syz155240 nat->nat_inip.s_addr = htonl(in.s_addr); 20912393Syz155240 nat->nat_outip = fin->fin_dst; 20922393Syz155240 nat->nat_oip = fin->fin_src; 20932393Syz155240 20942393Syz155240 ni->nai_sum1 = LONG_SUM(ntohl(fin->fin_daddr)) + ntohs(dport); 20952393Syz155240 ni->nai_sum2 = LONG_SUM(in.s_addr) + ntohs(nport); 20962393Syz155240 20972393Syz155240 ni->nai_ip.s_addr = in.s_addr; 20982393Syz155240 ni->nai_nport = nport; 20992393Syz155240 ni->nai_port = sport; 21002393Syz155240 21012393Syz155240 if (flags & IPN_TCPUDP) { 21022393Syz155240 nat->nat_inport = nport; 21032393Syz155240 nat->nat_outport = dport; 21042393Syz155240 nat->nat_oport = sport; 21052393Syz155240 ((tcphdr_t *)fin->fin_dp)->th_dport = nport; 21062393Syz155240 } else if (flags & IPN_ICMPQUERY) { 21072393Syz155240 ((icmphdr_t *)fin->fin_dp)->icmp_id = nport; 21082393Syz155240 nat->nat_inport = nport; 21092393Syz155240 nat->nat_outport = nport; 21102393Syz155240 } 21112393Syz155240 21122393Syz155240 return move; 21132393Syz155240 } 21142393Syz155240 21152393Syz155240 /* ------------------------------------------------------------------------ */ 21162393Syz155240 /* Function: nat_new */ 21172393Syz155240 /* Returns: nat_t* - NULL == failure to create new NAT structure, */ 21182393Syz155240 /* else pointer to new NAT structure */ 21192393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 21202393Syz155240 /* np(I) - pointer to NAT rule */ 21212393Syz155240 /* natsave(I) - pointer to where to store NAT struct pointer */ 21222393Syz155240 /* flags(I) - flags describing the current packet */ 21232393Syz155240 /* direction(I) - direction of packet (in/out) */ 21242393Syz155240 /* Write Lock: ipf_nat */ 21252393Syz155240 /* */ 21262393Syz155240 /* Attempts to create a new NAT entry. Does not actually change the packet */ 21272393Syz155240 /* in any way. */ 21282393Syz155240 /* */ 21292393Syz155240 /* This fucntion is in three main parts: (1) deal with creating a new NAT */ 21302393Syz155240 /* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */ 21312393Syz155240 /* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */ 21322393Syz155240 /* and (3) building that structure and putting it into the NAT table(s). */ 21332393Syz155240 /* ------------------------------------------------------------------------ */ 21342393Syz155240 nat_t *nat_new(fin, np, natsave, flags, direction) 21352393Syz155240 fr_info_t *fin; 21362393Syz155240 ipnat_t *np; 21372393Syz155240 nat_t **natsave; 21382393Syz155240 u_int flags; 21392393Syz155240 int direction; 21402393Syz155240 { 21412393Syz155240 u_short port = 0, sport = 0, dport = 0, nport = 0; 21422393Syz155240 tcphdr_t *tcp = NULL; 21432393Syz155240 hostmap_t *hm = NULL; 21442393Syz155240 struct in_addr in; 21452393Syz155240 nat_t *nat, *natl; 21462393Syz155240 u_int nflags; 21472393Syz155240 natinfo_t ni; 21482393Syz155240 u_32_t sumd; 21492393Syz155240 int move; 21502393Syz155240 #if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC) 21512393Syz155240 qpktinfo_t *qpi = fin->fin_qpi; 21522393Syz155240 #endif 21532393Syz155240 21542393Syz155240 if (nat_stats.ns_inuse >= ipf_nattable_max) { 21552393Syz155240 nat_stats.ns_memfail++; 21562393Syz155240 return NULL; 21572393Syz155240 } 21582393Syz155240 21592393Syz155240 move = 1; 21602393Syz155240 nflags = np->in_flags & flags; 21612393Syz155240 nflags &= NAT_FROMRULE; 21622393Syz155240 21632393Syz155240 ni.nai_np = np; 21642393Syz155240 ni.nai_nflags = nflags; 21652393Syz155240 ni.nai_flags = flags; 21662393Syz155240 21672393Syz155240 /* Give me a new nat */ 21682393Syz155240 KMALLOC(nat, nat_t *); 21692393Syz155240 if (nat == NULL) { 21702393Syz155240 nat_stats.ns_memfail++; 21712393Syz155240 /* 21722393Syz155240 * Try to automatically tune the max # of entries in the 21732393Syz155240 * table allowed to be less than what will cause kmem_alloc() 21742393Syz155240 * to fail and try to eliminate panics due to out of memory 21752393Syz155240 * conditions arising. 21762393Syz155240 */ 21772393Syz155240 if (ipf_nattable_max > ipf_nattable_sz) { 21782393Syz155240 ipf_nattable_max = nat_stats.ns_inuse - 100; 21792393Syz155240 printf("ipf_nattable_max reduced to %d\n", 21802393Syz155240 ipf_nattable_max); 21812393Syz155240 } 21822393Syz155240 return NULL; 21832393Syz155240 } 21842393Syz155240 21852393Syz155240 if (flags & IPN_TCPUDP) { 21862393Syz155240 tcp = fin->fin_dp; 21872393Syz155240 ni.nai_sport = htons(fin->fin_sport); 21882393Syz155240 ni.nai_dport = htons(fin->fin_dport); 21892393Syz155240 } else if (flags & IPN_ICMPQUERY) { 21902393Syz155240 /* 21912393Syz155240 * In the ICMP query NAT code, we translate the ICMP id fields 21922393Syz155240 * to make them unique. This is indepedent of the ICMP type 21932393Syz155240 * (e.g. in the unlikely event that a host sends an echo and 21942393Syz155240 * an tstamp request with the same id, both packets will have 21952393Syz155240 * their ip address/id field changed in the same way). 21962393Syz155240 */ 21972393Syz155240 /* The icmp_id field is used by the sender to identify the 21982393Syz155240 * process making the icmp request. (the receiver justs 21992393Syz155240 * copies it back in its response). So, it closely matches 22002393Syz155240 * the concept of source port. We overlay sport, so we can 22012393Syz155240 * maximally reuse the existing code. 22022393Syz155240 */ 22032393Syz155240 ni.nai_sport = ((icmphdr_t *)fin->fin_dp)->icmp_id; 22042393Syz155240 ni.nai_dport = ni.nai_sport; 22052393Syz155240 } 22062393Syz155240 22072393Syz155240 bzero((char *)nat, sizeof(*nat)); 22082393Syz155240 nat->nat_flags = flags; 22092393Syz155240 22102393Syz155240 if ((flags & NAT_SLAVE) == 0) { 22112393Syz155240 MUTEX_ENTER(&ipf_nat_new); 22122393Syz155240 } 22132393Syz155240 22142393Syz155240 /* 22152393Syz155240 * Search the current table for a match. 22162393Syz155240 */ 22172393Syz155240 if (direction == NAT_OUTBOUND) { 22182393Syz155240 /* 22192393Syz155240 * We can now arrange to call this for the same connection 22202393Syz155240 * because ipf_nat_new doesn't protect the code path into 22212393Syz155240 * this function. 22222393Syz155240 */ 22232393Syz155240 natl = nat_outlookup(fin, nflags, (u_int)fin->fin_p, 22242393Syz155240 fin->fin_src, fin->fin_dst); 22252393Syz155240 if (natl != NULL) { 22262393Syz155240 nat = natl; 22272393Syz155240 goto done; 22282393Syz155240 } 22292393Syz155240 22302393Syz155240 move = nat_newmap(fin, nat, &ni); 22312393Syz155240 if (move == -1) 22322393Syz155240 goto badnat; 22332393Syz155240 22342393Syz155240 np = ni.nai_np; 22352393Syz155240 in = ni.nai_ip; 22362393Syz155240 } else { 22372393Syz155240 /* 22382393Syz155240 * NAT_INBOUND is used only for redirects rules 22392393Syz155240 */ 22402393Syz155240 natl = nat_inlookup(fin, nflags, (u_int)fin->fin_p, 22412393Syz155240 fin->fin_src, fin->fin_dst); 22422393Syz155240 if (natl != NULL) { 22432393Syz155240 nat = natl; 22442393Syz155240 goto done; 22452393Syz155240 } 22462393Syz155240 22472393Syz155240 move = nat_newrdr(fin, nat, &ni); 22482393Syz155240 if (move == -1) 22492393Syz155240 goto badnat; 22502393Syz155240 22512393Syz155240 np = ni.nai_np; 22522393Syz155240 in = ni.nai_ip; 22532393Syz155240 } 22542393Syz155240 port = ni.nai_port; 22552393Syz155240 nport = ni.nai_nport; 22562393Syz155240 22572393Syz155240 if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { 22582393Syz155240 if (np->in_redir == NAT_REDIRECT) { 22592393Syz155240 nat_delrdr(np); 22602393Syz155240 nat_addrdr(np); 22612393Syz155240 } else if (np->in_redir == NAT_MAP) { 22622393Syz155240 nat_delnat(np); 22632393Syz155240 nat_addnat(np); 22642393Syz155240 } 22652393Syz155240 } 22662393Syz155240 22672393Syz155240 if (flags & IPN_TCPUDP) { 22682393Syz155240 sport = ni.nai_sport; 22692393Syz155240 dport = ni.nai_dport; 22702393Syz155240 } else if (flags & IPN_ICMPQUERY) { 22712393Syz155240 sport = ni.nai_sport; 22722393Syz155240 dport = 0; 22732393Syz155240 } 22742393Syz155240 22752393Syz155240 CALC_SUMD(ni.nai_sum1, ni.nai_sum2, sumd); 22762393Syz155240 nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 22772393Syz155240 #if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC) 22782393Syz155240 if ((flags & IPN_TCP) && dohwcksum && 22792393Syz155240 #ifndef IRE_ILL_CN 22802393Syz155240 (((ill_t *)qpi->qpi_ill)->ill_ick.ick_magic == ICK_M_CTL_MAGIC)) { 22812393Syz155240 #else 22822393Syz155240 (((s_ill_t *)qpi->qpi_ill)->ill_ick.ick_magic == ICK_M_CTL_MAGIC)) { 22832393Syz155240 #endif /* IRE_ILL_CN */ 22842393Syz155240 if (direction == NAT_OUTBOUND) 22852393Syz155240 ni.nai_sum1 = LONG_SUM(in.s_addr); 22862393Syz155240 else 22872393Syz155240 ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr)); 22882393Syz155240 ni.nai_sum1 += LONG_SUM(ntohl(fin->fin_daddr)); 22892393Syz155240 ni.nai_sum1 += 30; 22902393Syz155240 ni.nai_sum1 = (ni.nai_sum1 & 0xffff) + (ni.nai_sum1 >> 16); 22912393Syz155240 nat->nat_sumd[1] = NAT_HW_CKSUM|(ni.nai_sum1 & 0xffff); 22922393Syz155240 } else 22932393Syz155240 #endif 22942393Syz155240 nat->nat_sumd[1] = nat->nat_sumd[0]; 22952393Syz155240 22962393Syz155240 if ((flags & IPN_TCPUDPICMP) && ((sport != port) || (dport != nport))) { 22972393Syz155240 if (direction == NAT_OUTBOUND) 22982393Syz155240 ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr)); 22992393Syz155240 else 23002393Syz155240 ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_daddr)); 23012393Syz155240 23022393Syz155240 ni.nai_sum2 = LONG_SUM(in.s_addr); 23032393Syz155240 23042393Syz155240 CALC_SUMD(ni.nai_sum1, ni.nai_sum2, sumd); 23052393Syz155240 nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); 23062393Syz155240 } else { 23072393Syz155240 nat->nat_ipsumd = nat->nat_sumd[0]; 23082393Syz155240 if (!(flags & IPN_TCPUDPICMP)) { 23092393Syz155240 nat->nat_sumd[0] = 0; 23102393Syz155240 nat->nat_sumd[1] = 0; 23112393Syz155240 } 23122393Syz155240 } 23132393Syz155240 23142393Syz155240 if (nat_finalise(fin, nat, &ni, tcp, natsave, direction) == -1) { 23152393Syz155240 goto badnat; 23162393Syz155240 } 23172393Syz155240 if (flags & SI_WILDP) 23182393Syz155240 nat_stats.ns_wilds++; 23192393Syz155240 goto done; 23202393Syz155240 badnat: 23212393Syz155240 nat_stats.ns_badnat++; 23222393Syz155240 if ((hm = nat->nat_hm) != NULL) 23232393Syz155240 nat_hostmapdel(hm); 23242393Syz155240 KFREE(nat); 23252393Syz155240 nat = NULL; 23262393Syz155240 done: 23272393Syz155240 if ((flags & NAT_SLAVE) == 0) { 23282393Syz155240 MUTEX_EXIT(&ipf_nat_new); 23292393Syz155240 } 23302393Syz155240 return nat; 23312393Syz155240 } 23322393Syz155240 23332393Syz155240 23342393Syz155240 /* ------------------------------------------------------------------------ */ 23352393Syz155240 /* Function: nat_finalise */ 23362393Syz155240 /* Returns: int - 0 == sucess, -1 == failure */ 23372393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 23382393Syz155240 /* nat(I) - pointer to NAT entry */ 23392393Syz155240 /* ni(I) - pointer to structure with misc. information needed */ 23402393Syz155240 /* to create new NAT entry. */ 23412393Syz155240 /* Write Lock: ipf_nat */ 23422393Syz155240 /* */ 23432393Syz155240 /* This is the tail end of constructing a new NAT entry and is the same */ 23442393Syz155240 /* for both IPv4 and IPv6. */ 23452393Syz155240 /* ------------------------------------------------------------------------ */ 23462393Syz155240 /*ARGSUSED*/ 23472393Syz155240 static INLINE int nat_finalise(fin, nat, ni, tcp, natsave, direction) 23482393Syz155240 fr_info_t *fin; 23492393Syz155240 nat_t *nat; 23502393Syz155240 natinfo_t *ni; 23512393Syz155240 tcphdr_t *tcp; 23522393Syz155240 nat_t **natsave; 23532393Syz155240 int direction; 23542393Syz155240 { 23552393Syz155240 frentry_t *fr; 23562393Syz155240 ipnat_t *np; 23572393Syz155240 23582393Syz155240 np = ni->nai_np; 23592393Syz155240 2360*2508Syz155240 if (np->in_ifps[0] != NULL) { 2361*2508Syz155240 (void) COPYIFNAME(np->in_ifps[0], nat->nat_ifnames[0]); 2362*2508Syz155240 } 2363*2508Syz155240 if (np->in_ifps[1] != NULL) { 2364*2508Syz155240 (void) COPYIFNAME(np->in_ifps[1], nat->nat_ifnames[1]); 2365*2508Syz155240 } 23662393Syz155240 #ifdef IPFILTER_SYNC 23672393Syz155240 if ((nat->nat_flags & SI_CLONE) == 0) 23682393Syz155240 nat->nat_sync = ipfsync_new(SMC_NAT, fin, nat); 23692393Syz155240 #endif 23702393Syz155240 23712393Syz155240 nat->nat_me = natsave; 23722393Syz155240 nat->nat_dir = direction; 2373*2508Syz155240 nat->nat_ifps[0] = np->in_ifps[0]; 2374*2508Syz155240 nat->nat_ifps[1] = np->in_ifps[1]; 23752393Syz155240 nat->nat_ptr = np; 23762393Syz155240 nat->nat_p = fin->fin_p; 23772393Syz155240 nat->nat_mssclamp = np->in_mssclamp; 23782393Syz155240 fr = fin->fin_fr; 23792393Syz155240 nat->nat_fr = fr; 23802393Syz155240 23812393Syz155240 if ((np->in_apr != NULL) && ((ni->nai_flags & NAT_SLAVE) == 0)) 23822393Syz155240 if (appr_new(fin, nat) == -1) 23832393Syz155240 return -1; 23842393Syz155240 23852393Syz155240 if (nat_insert(nat, fin->fin_rev) == 0) { 23862393Syz155240 if (nat_logging) 23872393Syz155240 nat_log(nat, (u_int)np->in_redir); 23882393Syz155240 np->in_use++; 23892393Syz155240 if (fr != NULL) { 23902393Syz155240 MUTEX_ENTER(&fr->fr_lock); 23912393Syz155240 fr->fr_ref++; 23922393Syz155240 MUTEX_EXIT(&fr->fr_lock); 23932393Syz155240 } 23942393Syz155240 return 0; 23952393Syz155240 } 23962393Syz155240 23972393Syz155240 /* 23982393Syz155240 * nat_insert failed, so cleanup time... 23992393Syz155240 */ 24002393Syz155240 return -1; 24012393Syz155240 } 24022393Syz155240 24032393Syz155240 24042393Syz155240 /* ------------------------------------------------------------------------ */ 24052393Syz155240 /* Function: nat_insert */ 24062393Syz155240 /* Returns: int - 0 == sucess, -1 == failure */ 24072393Syz155240 /* Parameters: nat(I) - pointer to NAT structure */ 24082393Syz155240 /* rev(I) - flag indicating forward/reverse direction of packet */ 24092393Syz155240 /* Write Lock: ipf_nat */ 24102393Syz155240 /* */ 24112393Syz155240 /* Insert a NAT entry into the hash tables for searching and add it to the */ 24122393Syz155240 /* list of active NAT entries. Adjust global counters when complete. */ 24132393Syz155240 /* ------------------------------------------------------------------------ */ 24142393Syz155240 int nat_insert(nat, rev) 24152393Syz155240 nat_t *nat; 24162393Syz155240 int rev; 24172393Syz155240 { 24182393Syz155240 u_int hv1, hv2; 24192393Syz155240 nat_t **natp; 24202393Syz155240 24212393Syz155240 /* 24222393Syz155240 * Try and return an error as early as possible, so calculate the hash 24232393Syz155240 * entry numbers first and then proceed. 24242393Syz155240 */ 24252393Syz155240 if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { 24262393Syz155240 hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 24272393Syz155240 0xffffffff); 24282393Syz155240 hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1 + nat->nat_oport, 24292393Syz155240 ipf_nattable_sz); 24302393Syz155240 hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, 24312393Syz155240 0xffffffff); 24322393Syz155240 hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2 + nat->nat_oport, 24332393Syz155240 ipf_nattable_sz); 24342393Syz155240 } else { 24352393Syz155240 hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, 0, 0xffffffff); 24362393Syz155240 hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1, ipf_nattable_sz); 24372393Syz155240 hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, 0, 0xffffffff); 24382393Syz155240 hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2, ipf_nattable_sz); 24392393Syz155240 } 24402393Syz155240 24412393Syz155240 if (nat_stats.ns_bucketlen[0][hv1] >= fr_nat_maxbucket || 24422393Syz155240 nat_stats.ns_bucketlen[1][hv2] >= fr_nat_maxbucket) { 24432393Syz155240 return -1; 24442393Syz155240 } 24452393Syz155240 24462393Syz155240 nat->nat_hv[0] = hv1; 24472393Syz155240 nat->nat_hv[1] = hv2; 24482393Syz155240 24492393Syz155240 MUTEX_INIT(&nat->nat_lock, "nat entry lock"); 24502393Syz155240 24512393Syz155240 nat->nat_rev = rev; 24522393Syz155240 nat->nat_ref = 1; 24532393Syz155240 nat->nat_bytes[0] = 0; 24542393Syz155240 nat->nat_pkts[0] = 0; 24552393Syz155240 nat->nat_bytes[1] = 0; 24562393Syz155240 nat->nat_pkts[1] = 0; 24572393Syz155240 24582393Syz155240 nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; 24592393Syz155240 nat->nat_ifps[0] = fr_resolvenic(nat->nat_ifnames[0], 4); 24602393Syz155240 24612393Syz155240 if (nat->nat_ifnames[1][0] !='\0') { 24622393Syz155240 nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 24632393Syz155240 nat->nat_ifps[1] = fr_resolvenic(nat->nat_ifnames[1], 4); 24642393Syz155240 } else { 24652393Syz155240 (void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0], 24662393Syz155240 LIFNAMSIZ); 24672393Syz155240 nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 24682393Syz155240 nat->nat_ifps[1] = nat->nat_ifps[0]; 24692393Syz155240 } 24702393Syz155240 24712393Syz155240 nat->nat_next = nat_instances; 24722393Syz155240 nat->nat_pnext = &nat_instances; 24732393Syz155240 if (nat_instances) 24742393Syz155240 nat_instances->nat_pnext = &nat->nat_next; 24752393Syz155240 nat_instances = nat; 24762393Syz155240 24772393Syz155240 natp = &nat_table[0][hv1]; 24782393Syz155240 if (*natp) 24792393Syz155240 (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 24802393Syz155240 nat->nat_phnext[0] = natp; 24812393Syz155240 nat->nat_hnext[0] = *natp; 24822393Syz155240 *natp = nat; 24832393Syz155240 nat_stats.ns_bucketlen[0][hv1]++; 24842393Syz155240 24852393Syz155240 natp = &nat_table[1][hv2]; 24862393Syz155240 if (*natp) 24872393Syz155240 (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 24882393Syz155240 nat->nat_phnext[1] = natp; 24892393Syz155240 nat->nat_hnext[1] = *natp; 24902393Syz155240 *natp = nat; 24912393Syz155240 nat_stats.ns_bucketlen[1][hv2]++; 24922393Syz155240 24932393Syz155240 fr_setnatqueue(nat, rev); 24942393Syz155240 24952393Syz155240 nat_stats.ns_added++; 24962393Syz155240 nat_stats.ns_inuse++; 24972393Syz155240 return 0; 24982393Syz155240 } 24992393Syz155240 25002393Syz155240 25012393Syz155240 /* ------------------------------------------------------------------------ */ 25022393Syz155240 /* Function: nat_icmperrorlookup */ 25032393Syz155240 /* Returns: nat_t* - point to matching NAT structure */ 25042393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 25052393Syz155240 /* dir(I) - direction of packet (in/out) */ 25062393Syz155240 /* */ 25072393Syz155240 /* Check if the ICMP error message is related to an existing TCP, UDP or */ 25082393Syz155240 /* ICMP query nat entry. It is assumed that the packet is already of the */ 25092393Syz155240 /* the required length. */ 25102393Syz155240 /* ------------------------------------------------------------------------ */ 25112393Syz155240 nat_t *nat_icmperrorlookup(fin, dir) 25122393Syz155240 fr_info_t *fin; 25132393Syz155240 int dir; 25142393Syz155240 { 25152393Syz155240 int flags = 0, minlen; 25162393Syz155240 icmphdr_t *orgicmp; 25172393Syz155240 tcphdr_t *tcp = NULL; 25182393Syz155240 u_short data[2]; 25192393Syz155240 nat_t *nat; 25202393Syz155240 ip_t *oip; 25212393Syz155240 u_int p; 25222393Syz155240 25232393Syz155240 /* 25242393Syz155240 * Does it at least have the return (basic) IP header ? 25252393Syz155240 * Only a basic IP header (no options) should be with an ICMP error 25262393Syz155240 * header. Also, if it's not an error type, then return. 25272393Syz155240 */ 25282393Syz155240 if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) 25292393Syz155240 return NULL; 25302393Syz155240 25312393Syz155240 /* 25322393Syz155240 * Check packet size 25332393Syz155240 */ 25342393Syz155240 oip = (ip_t *)((char *)fin->fin_dp + 8); 25352393Syz155240 minlen = IP_HL(oip) << 2; 25362393Syz155240 if ((minlen < sizeof(ip_t)) || 25372393Syz155240 (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) 25382393Syz155240 return NULL; 25392393Syz155240 /* 25402393Syz155240 * Is the buffer big enough for all of it ? It's the size of the IP 25412393Syz155240 * header claimed in the encapsulated part which is of concern. It 25422393Syz155240 * may be too big to be in this buffer but not so big that it's 25432393Syz155240 * outside the ICMP packet, leading to TCP deref's causing problems. 25442393Syz155240 * This is possible because we don't know how big oip_hl is when we 25452393Syz155240 * do the pullup early in fr_check() and thus can't gaurantee it is 25462393Syz155240 * all here now. 25472393Syz155240 */ 25482393Syz155240 #ifdef _KERNEL 25492393Syz155240 { 25502393Syz155240 mb_t *m; 25512393Syz155240 25522393Syz155240 m = fin->fin_m; 25532393Syz155240 # if defined(MENTAT) 25542393Syz155240 if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr) 25552393Syz155240 return NULL; 25562393Syz155240 # else 25572393Syz155240 if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > 25582393Syz155240 (char *)fin->fin_ip + M_LEN(m)) 25592393Syz155240 return NULL; 25602393Syz155240 # endif 25612393Syz155240 } 25622393Syz155240 #endif 25632393Syz155240 25642393Syz155240 if (fin->fin_daddr != oip->ip_src.s_addr) 25652393Syz155240 return NULL; 25662393Syz155240 25672393Syz155240 p = oip->ip_p; 25682393Syz155240 if (p == IPPROTO_TCP) 25692393Syz155240 flags = IPN_TCP; 25702393Syz155240 else if (p == IPPROTO_UDP) 25712393Syz155240 flags = IPN_UDP; 25722393Syz155240 else if (p == IPPROTO_ICMP) { 25732393Syz155240 orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 25742393Syz155240 25752393Syz155240 /* see if this is related to an ICMP query */ 25762393Syz155240 if (nat_icmpquerytype4(orgicmp->icmp_type)) { 25772393Syz155240 data[0] = fin->fin_data[0]; 25782393Syz155240 data[1] = fin->fin_data[1]; 25792393Syz155240 fin->fin_data[0] = 0; 25802393Syz155240 fin->fin_data[1] = orgicmp->icmp_id; 25812393Syz155240 25822393Syz155240 flags = IPN_ICMPERR|IPN_ICMPQUERY; 25832393Syz155240 /* 25842393Syz155240 * NOTE : dir refers to the direction of the original 25852393Syz155240 * ip packet. By definition the icmp error 25862393Syz155240 * message flows in the opposite direction. 25872393Syz155240 */ 25882393Syz155240 if (dir == NAT_INBOUND) 25892393Syz155240 nat = nat_inlookup(fin, flags, p, oip->ip_dst, 25902393Syz155240 oip->ip_src); 25912393Syz155240 else 25922393Syz155240 nat = nat_outlookup(fin, flags, p, oip->ip_dst, 25932393Syz155240 oip->ip_src); 25942393Syz155240 fin->fin_data[0] = data[0]; 25952393Syz155240 fin->fin_data[1] = data[1]; 25962393Syz155240 return nat; 25972393Syz155240 } 25982393Syz155240 } 25992393Syz155240 26002393Syz155240 if (flags & IPN_TCPUDP) { 26012393Syz155240 minlen += 8; /* + 64bits of data to get ports */ 26022393Syz155240 if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) 26032393Syz155240 return NULL; 26042393Syz155240 26052393Syz155240 data[0] = fin->fin_data[0]; 26062393Syz155240 data[1] = fin->fin_data[1]; 26072393Syz155240 tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 26082393Syz155240 fin->fin_data[0] = ntohs(tcp->th_dport); 26092393Syz155240 fin->fin_data[1] = ntohs(tcp->th_sport); 26102393Syz155240 26112393Syz155240 if (dir == NAT_INBOUND) { 26122393Syz155240 nat = nat_inlookup(fin, flags, p, oip->ip_dst, 26132393Syz155240 oip->ip_src); 26142393Syz155240 } else { 26152393Syz155240 nat = nat_outlookup(fin, flags, p, oip->ip_dst, 26162393Syz155240 oip->ip_src); 26172393Syz155240 } 26182393Syz155240 fin->fin_data[0] = data[0]; 26192393Syz155240 fin->fin_data[1] = data[1]; 26202393Syz155240 return nat; 26212393Syz155240 } 26222393Syz155240 if (dir == NAT_INBOUND) 26232393Syz155240 return nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src); 26242393Syz155240 else 26252393Syz155240 return nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src); 26262393Syz155240 } 26272393Syz155240 26282393Syz155240 26292393Syz155240 /* ------------------------------------------------------------------------ */ 26302393Syz155240 /* Function: nat_icmperror */ 26312393Syz155240 /* Returns: nat_t* - point to matching NAT structure */ 26322393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 26332393Syz155240 /* nflags(I) - NAT flags for this packet */ 26342393Syz155240 /* dir(I) - direction of packet (in/out) */ 26352393Syz155240 /* */ 26362393Syz155240 /* Fix up an ICMP packet which is an error message for an existing NAT */ 26372393Syz155240 /* session. This will correct both packet header data and checksums. */ 26382393Syz155240 /* */ 26392393Syz155240 /* This should *ONLY* be used for incoming ICMP error packets to make sure */ 26402393Syz155240 /* a NAT'd ICMP packet gets correctly recognised. */ 26412393Syz155240 /* ------------------------------------------------------------------------ */ 26422393Syz155240 nat_t *nat_icmperror(fin, nflags, dir) 26432393Syz155240 fr_info_t *fin; 26442393Syz155240 u_int *nflags; 26452393Syz155240 int dir; 26462393Syz155240 { 26472393Syz155240 u_32_t sum1, sum2, sumd, sumd2; 26482393Syz155240 struct in_addr in; 26492393Syz155240 icmphdr_t *icmp; 26502393Syz155240 int flags, dlen; 26512393Syz155240 u_short *csump; 26522393Syz155240 tcphdr_t *tcp; 26532393Syz155240 nat_t *nat; 26542393Syz155240 ip_t *oip; 26552393Syz155240 void *dp; 26562393Syz155240 26572393Syz155240 if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) 26582393Syz155240 return NULL; 26592393Syz155240 /* 26602393Syz155240 * nat_icmperrorlookup() will return NULL for `defective' packets. 26612393Syz155240 */ 26622393Syz155240 if ((fin->fin_v != 4) || !(nat = nat_icmperrorlookup(fin, dir))) 26632393Syz155240 return NULL; 26642393Syz155240 26652393Syz155240 tcp = NULL; 26662393Syz155240 csump = NULL; 26672393Syz155240 flags = 0; 26682393Syz155240 sumd2 = 0; 26692393Syz155240 *nflags = IPN_ICMPERR; 26702393Syz155240 icmp = fin->fin_dp; 26712393Syz155240 oip = (ip_t *)&icmp->icmp_ip; 26722393Syz155240 dp = (((char *)oip) + (IP_HL(oip) << 2)); 26732393Syz155240 if (oip->ip_p == IPPROTO_TCP) { 26742393Syz155240 tcp = (tcphdr_t *)dp; 26752393Syz155240 csump = (u_short *)&tcp->th_sum; 26762393Syz155240 flags = IPN_TCP; 26772393Syz155240 } else if (oip->ip_p == IPPROTO_UDP) { 26782393Syz155240 udphdr_t *udp; 26792393Syz155240 26802393Syz155240 udp = (udphdr_t *)dp; 26812393Syz155240 tcp = (tcphdr_t *)dp; 26822393Syz155240 csump = (u_short *)&udp->uh_sum; 26832393Syz155240 flags = IPN_UDP; 26842393Syz155240 } else if (oip->ip_p == IPPROTO_ICMP) 26852393Syz155240 flags = IPN_ICMPQUERY; 26862393Syz155240 dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip); 26872393Syz155240 26882393Syz155240 /* 26892393Syz155240 * Need to adjust ICMP header to include the real IP#'s and 26902393Syz155240 * port #'s. Only apply a checksum change relative to the 26912393Syz155240 * IP address change as it will be modified again in fr_checknatout 26922393Syz155240 * for both address and port. Two checksum changes are 26932393Syz155240 * necessary for the two header address changes. Be careful 26942393Syz155240 * to only modify the checksum once for the port # and twice 26952393Syz155240 * for the IP#. 26962393Syz155240 */ 26972393Syz155240 26982393Syz155240 /* 26992393Syz155240 * Step 1 27002393Syz155240 * Fix the IP addresses in the offending IP packet. You also need 27012393Syz155240 * to adjust the IP header checksum of that offending IP packet 27022393Syz155240 * and the ICMP checksum of the ICMP error message itself. 27032393Syz155240 * 27042393Syz155240 * Unfortunately, for UDP and TCP, the IP addresses are also contained 27052393Syz155240 * in the pseudo header that is used to compute the UDP resp. TCP 27062393Syz155240 * checksum. So, we must compensate that as well. Even worse, the 27072393Syz155240 * change in the UDP and TCP checksums require yet another 27082393Syz155240 * adjustment of the ICMP checksum of the ICMP error message. 27092393Syz155240 */ 27102393Syz155240 27112393Syz155240 if (oip->ip_dst.s_addr == nat->nat_oip.s_addr) { 27122393Syz155240 sum1 = LONG_SUM(ntohl(oip->ip_src.s_addr)); 27132393Syz155240 in = nat->nat_inip; 27142393Syz155240 oip->ip_src = in; 27152393Syz155240 } else { 27162393Syz155240 sum1 = LONG_SUM(ntohl(oip->ip_dst.s_addr)); 27172393Syz155240 in = nat->nat_outip; 27182393Syz155240 oip->ip_dst = in; 27192393Syz155240 } 27202393Syz155240 27212393Syz155240 sum2 = LONG_SUM(ntohl(in.s_addr)); 27222393Syz155240 27232393Syz155240 CALC_SUMD(sum1, sum2, sumd); 27242393Syz155240 27252393Syz155240 /* 27262393Syz155240 * Fix IP checksum of the offending IP packet to adjust for 27272393Syz155240 * the change in the IP address. 27282393Syz155240 * 27292393Syz155240 * Normally, you would expect that the ICMP checksum of the 27302393Syz155240 * ICMP error message needs to be adjusted as well for the 27312393Syz155240 * IP address change in oip. 27322393Syz155240 * However, this is a NOP, because the ICMP checksum is 27332393Syz155240 * calculated over the complete ICMP packet, which includes the 27342393Syz155240 * changed oip IP addresses and oip->ip_sum. However, these 27352393Syz155240 * two changes cancel each other out (if the delta for 27362393Syz155240 * the IP address is x, then the delta for ip_sum is minus x), 27372393Syz155240 * so no change in the icmp_cksum is necessary. 27382393Syz155240 * 27392393Syz155240 * Be careful that nat_dir refers to the direction of the 27402393Syz155240 * offending IP packet (oip), not to its ICMP response (icmp) 27412393Syz155240 */ 27422393Syz155240 fix_datacksum(&oip->ip_sum, sumd); 27432393Syz155240 /* Fix icmp cksum : IP Addr + Cksum */ 27442393Syz155240 sumd2 = (sumd >> 16); 27452393Syz155240 27462393Syz155240 /* 27472393Syz155240 * Fix UDP pseudo header checksum to compensate for the 27482393Syz155240 * IP address change. 27492393Syz155240 */ 27502393Syz155240 if ((oip->ip_p == IPPROTO_UDP) && (dlen >= 8) && (*csump != 0)) { 27512393Syz155240 /* 27522393Syz155240 * The UDP checksum is optional, only adjust it 27532393Syz155240 * if it has been set. 27542393Syz155240 */ 27552393Syz155240 sum1 = ntohs(*csump); 27562393Syz155240 fix_datacksum(csump, sumd); 27572393Syz155240 sum2 = ntohs(*csump); 27582393Syz155240 27592393Syz155240 /* 27602393Syz155240 * Fix ICMP checksum to compensate the UDP 27612393Syz155240 * checksum adjustment. 27622393Syz155240 */ 27632393Syz155240 sumd2 = sumd << 1; 27642393Syz155240 CALC_SUMD(sum1, sum2, sumd); 27652393Syz155240 sumd2 += sumd; 27662393Syz155240 } 27672393Syz155240 27682393Syz155240 /* 27692393Syz155240 * Fix TCP pseudo header checksum to compensate for the 27702393Syz155240 * IP address change. Before we can do the change, we 27712393Syz155240 * must make sure that oip is sufficient large to hold 27722393Syz155240 * the TCP checksum (normally it does not!). 27732393Syz155240 * 18 = offsetof(tcphdr_t, th_sum) + 2 27742393Syz155240 */ 27752393Syz155240 else if (oip->ip_p == IPPROTO_TCP && dlen >= 18) { 27762393Syz155240 sum1 = ntohs(*csump); 27772393Syz155240 fix_datacksum(csump, sumd); 27782393Syz155240 sum2 = ntohs(*csump); 27792393Syz155240 27802393Syz155240 /* 27812393Syz155240 * Fix ICMP checksum to compensate the TCP 27822393Syz155240 * checksum adjustment. 27832393Syz155240 */ 27842393Syz155240 sumd2 = sumd << 1; 27852393Syz155240 CALC_SUMD(sum1, sum2, sumd); 27862393Syz155240 sumd2 += sumd; 27872393Syz155240 } else { 27882393Syz155240 if (nat->nat_dir == NAT_OUTBOUND) 27892393Syz155240 sumd2 = ~sumd2; 27902393Syz155240 else 27912393Syz155240 sumd2 = ~sumd2 + 1; 27922393Syz155240 } 27932393Syz155240 27942393Syz155240 if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) { 27952393Syz155240 int mode = 0; 27962393Syz155240 27972393Syz155240 /* 27982393Syz155240 * Step 2 : 27992393Syz155240 * For offending TCP/UDP IP packets, translate the ports as 28002393Syz155240 * well, based on the NAT specification. Of course such 28012393Syz155240 * a change must be reflected in the ICMP checksum as well. 28022393Syz155240 * 28032393Syz155240 * Advance notice : Now it becomes complicated :-) 28042393Syz155240 * 28052393Syz155240 * Since the port fields are part of the TCP/UDP checksum 28062393Syz155240 * of the offending IP packet, you need to adjust that checksum 28072393Syz155240 * as well... but, if you change, you must change the icmp 28082393Syz155240 * checksum *again*, to reflect that change. 28092393Syz155240 * 28102393Syz155240 * To further complicate: the TCP checksum is not in the first 28112393Syz155240 * 8 bytes of the offending ip packet, so it most likely is not 28122393Syz155240 * available. Some OSses like Solaris return enough bytes to 28132393Syz155240 * include the TCP checksum. So we have to check if the 28142393Syz155240 * ip->ip_len actually holds the TCP checksum of the oip! 28152393Syz155240 */ 28162393Syz155240 28172393Syz155240 if (nat->nat_oport == tcp->th_dport) { 28182393Syz155240 if (tcp->th_sport != nat->nat_inport) { 28192393Syz155240 mode = 1; 28202393Syz155240 sum1 = ntohs(nat->nat_inport); 28212393Syz155240 sum2 = ntohs(tcp->th_sport); 28222393Syz155240 } 28232393Syz155240 } else if (tcp->th_sport == nat->nat_oport) { 28242393Syz155240 mode = 2; 28252393Syz155240 sum1 = ntohs(nat->nat_outport); 28262393Syz155240 sum2 = ntohs(tcp->th_dport); 28272393Syz155240 } 28282393Syz155240 28292393Syz155240 if (mode == 1) { 28302393Syz155240 /* 28312393Syz155240 * Fix ICMP checksum to compensate port adjustment. 28322393Syz155240 */ 28332393Syz155240 tcp->th_sport = htons(sum1); 28342393Syz155240 28352393Syz155240 /* 28362393Syz155240 * Fix udp checksum to compensate port adjustment. 28372393Syz155240 * NOTE : the offending IP packet flows the other 28382393Syz155240 * direction compared to the ICMP message. 28392393Syz155240 * 28402393Syz155240 * The UDP checksum is optional, only adjust it if 28412393Syz155240 * it has been set. 28422393Syz155240 */ 28432393Syz155240 if ((oip->ip_p == IPPROTO_UDP) && 28442393Syz155240 (dlen >= 8) && (*csump != 0)) { 28452393Syz155240 sumd = sum1 - sum2; 28462393Syz155240 sumd2 += sumd; 28472393Syz155240 28482393Syz155240 sum1 = ntohs(*csump); 28492393Syz155240 fix_datacksum(csump, sumd); 28502393Syz155240 sum2 = ntohs(*csump); 28512393Syz155240 28522393Syz155240 /* 28532393Syz155240 * Fix ICMP checksum to compenstate 28542393Syz155240 * UDP checksum adjustment. 28552393Syz155240 */ 28562393Syz155240 CALC_SUMD(sum1, sum2, sumd); 28572393Syz155240 sumd2 += sumd; 28582393Syz155240 } 28592393Syz155240 28602393Syz155240 /* 28612393Syz155240 * Fix TCP checksum (if present) to compensate port 28622393Syz155240 * adjustment. NOTE : the offending IP packet flows 28632393Syz155240 * the other direction compared to the ICMP message. 28642393Syz155240 */ 28652393Syz155240 if (oip->ip_p == IPPROTO_TCP) { 28662393Syz155240 if (dlen >= 18) { 28672393Syz155240 sumd = sum1 - sum2; 28682393Syz155240 sumd2 += sumd; 28692393Syz155240 28702393Syz155240 sum1 = ntohs(*csump); 28712393Syz155240 fix_datacksum(csump, sumd); 28722393Syz155240 sum2 = ntohs(*csump); 28732393Syz155240 28742393Syz155240 /* 28752393Syz155240 * Fix ICMP checksum to compensate 28762393Syz155240 * TCP checksum adjustment. 28772393Syz155240 */ 28782393Syz155240 CALC_SUMD(sum1, sum2, sumd); 28792393Syz155240 sumd2 += sumd; 28802393Syz155240 } else { 28812393Syz155240 sumd = sum2 - sum1 + 1; 28822393Syz155240 sumd2 += sumd; 28832393Syz155240 } 28842393Syz155240 } 28852393Syz155240 } else if (mode == 2) { 28862393Syz155240 /* 28872393Syz155240 * Fix ICMP checksum to compensate port adjustment. 28882393Syz155240 */ 28892393Syz155240 tcp->th_dport = htons(sum1); 28902393Syz155240 28912393Syz155240 /* 28922393Syz155240 * Fix UDP checksum to compensate port adjustment. 28932393Syz155240 * NOTE : the offending IP packet flows the other 28942393Syz155240 * direction compared to the ICMP message. 28952393Syz155240 * 28962393Syz155240 * The UDP checksum is optional, only adjust 28972393Syz155240 * it if it has been set. 28982393Syz155240 */ 28992393Syz155240 if ((oip->ip_p == IPPROTO_UDP) && 29002393Syz155240 (dlen >= 8) && (*csump != 0)) { 29012393Syz155240 sumd = sum1 - sum2; 29022393Syz155240 sumd2 += sumd; 29032393Syz155240 29042393Syz155240 sum1 = ntohs(*csump); 29052393Syz155240 fix_datacksum(csump, sumd); 29062393Syz155240 sum2 = ntohs(*csump); 29072393Syz155240 29082393Syz155240 /* 29092393Syz155240 * Fix ICMP checksum to compensate 29102393Syz155240 * UDP checksum adjustment. 29112393Syz155240 */ 29122393Syz155240 CALC_SUMD(sum1, sum2, sumd); 29132393Syz155240 sumd2 += sumd; 29142393Syz155240 } 29152393Syz155240 29162393Syz155240 /* 29172393Syz155240 * Fix TCP checksum (if present) to compensate port 29182393Syz155240 * adjustment. NOTE : the offending IP packet flows 29192393Syz155240 * the other direction compared to the ICMP message. 29202393Syz155240 */ 29212393Syz155240 if (oip->ip_p == IPPROTO_TCP) { 29222393Syz155240 if (dlen >= 18) { 29232393Syz155240 sumd = sum1 - sum2; 29242393Syz155240 sumd2 += sumd; 29252393Syz155240 29262393Syz155240 sum1 = ntohs(*csump); 29272393Syz155240 fix_datacksum(csump, sumd); 29282393Syz155240 sum2 = ntohs(*csump); 29292393Syz155240 29302393Syz155240 /* 29312393Syz155240 * Fix ICMP checksum to compensate 29322393Syz155240 * TCP checksum adjustment. 29332393Syz155240 */ 29342393Syz155240 CALC_SUMD(sum1, sum2, sumd); 29352393Syz155240 sumd2 += sumd; 29362393Syz155240 } else { 29372393Syz155240 if (nat->nat_dir == NAT_INBOUND) 29382393Syz155240 sumd = sum2 - sum1; 29392393Syz155240 else 29402393Syz155240 sumd = sum2 - sum1 + 1; 29412393Syz155240 sumd2 += sumd; 29422393Syz155240 } 29432393Syz155240 } 29442393Syz155240 } 29452393Syz155240 if (sumd2 != 0) { 29462393Syz155240 sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 29472393Syz155240 sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 29482393Syz155240 fix_incksum(fin, &icmp->icmp_cksum, sumd2); 29492393Syz155240 } 29502393Syz155240 } else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) { 29512393Syz155240 icmphdr_t *orgicmp; 29522393Syz155240 29532393Syz155240 /* 29542393Syz155240 * XXX - what if this is bogus hl and we go off the end ? 29552393Syz155240 * In this case, nat_icmperrorlookup() will have returned NULL. 29562393Syz155240 */ 29572393Syz155240 orgicmp = (icmphdr_t *)dp; 29582393Syz155240 29592393Syz155240 if (nat->nat_dir == NAT_OUTBOUND) { 29602393Syz155240 if (orgicmp->icmp_id != nat->nat_inport) { 29612393Syz155240 29622393Syz155240 /* 29632393Syz155240 * Fix ICMP checksum (of the offening ICMP 29642393Syz155240 * query packet) to compensate the change 29652393Syz155240 * in the ICMP id of the offending ICMP 29662393Syz155240 * packet. 29672393Syz155240 * 29682393Syz155240 * Since you modify orgicmp->icmp_id with 29692393Syz155240 * a delta (say x) and you compensate that 29702393Syz155240 * in origicmp->icmp_cksum with a delta 29712393Syz155240 * minus x, you don't have to adjust the 29722393Syz155240 * overall icmp->icmp_cksum 29732393Syz155240 */ 29742393Syz155240 sum1 = ntohs(orgicmp->icmp_id); 29752393Syz155240 sum2 = ntohs(nat->nat_inport); 29762393Syz155240 CALC_SUMD(sum1, sum2, sumd); 29772393Syz155240 orgicmp->icmp_id = nat->nat_inport; 29782393Syz155240 fix_datacksum(&orgicmp->icmp_cksum, sumd); 29792393Syz155240 } 29802393Syz155240 } /* nat_dir == NAT_INBOUND is impossible for icmp queries */ 29812393Syz155240 } 29822393Syz155240 return nat; 29832393Syz155240 } 29842393Syz155240 29852393Syz155240 29862393Syz155240 /* 29872393Syz155240 * NB: these lookups don't lock access to the list, it assumed that it has 29882393Syz155240 * already been done! 29892393Syz155240 */ 29902393Syz155240 29912393Syz155240 /* ------------------------------------------------------------------------ */ 29922393Syz155240 /* Function: nat_inlookup */ 29932393Syz155240 /* Returns: nat_t* - NULL == no match, */ 29942393Syz155240 /* else pointer to matching NAT entry */ 29952393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 29962393Syz155240 /* flags(I) - NAT flags for this packet */ 29972393Syz155240 /* p(I) - protocol for this packet */ 29982393Syz155240 /* src(I) - source IP address */ 29992393Syz155240 /* mapdst(I) - destination IP address */ 30002393Syz155240 /* */ 30012393Syz155240 /* Lookup a nat entry based on the mapped destination ip address/port and */ 30022393Syz155240 /* real source address/port. We use this lookup when receiving a packet, */ 30032393Syz155240 /* we're looking for a table entry, based on the destination address. */ 30042393Syz155240 /* */ 30052393Syz155240 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 30062393Syz155240 /* */ 30072393Syz155240 /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */ 30082393Syz155240 /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 30092393Syz155240 /* */ 30102393Syz155240 /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 30112393Syz155240 /* the packet is of said protocol */ 30122393Syz155240 /* ------------------------------------------------------------------------ */ 30132393Syz155240 nat_t *nat_inlookup(fin, flags, p, src, mapdst) 30142393Syz155240 fr_info_t *fin; 30152393Syz155240 u_int flags, p; 30162393Syz155240 struct in_addr src , mapdst; 30172393Syz155240 { 30182393Syz155240 u_short sport, dport; 30192393Syz155240 ipnat_t *ipn; 30202393Syz155240 u_int sflags; 30212393Syz155240 nat_t *nat; 30222393Syz155240 int nflags; 30232393Syz155240 u_32_t dst; 30242393Syz155240 void *ifp; 30252393Syz155240 u_int hv; 30262393Syz155240 30272393Syz155240 if (fin != NULL) 30282393Syz155240 ifp = fin->fin_ifp; 30292393Syz155240 else 30302393Syz155240 ifp = NULL; 30312393Syz155240 sport = 0; 30322393Syz155240 dport = 0; 30332393Syz155240 dst = mapdst.s_addr; 30342393Syz155240 sflags = flags & NAT_TCPUDPICMP; 30352393Syz155240 30362393Syz155240 switch (p) 30372393Syz155240 { 30382393Syz155240 case IPPROTO_TCP : 30392393Syz155240 case IPPROTO_UDP : 30402393Syz155240 sport = htons(fin->fin_data[0]); 30412393Syz155240 dport = htons(fin->fin_data[1]); 30422393Syz155240 break; 30432393Syz155240 case IPPROTO_ICMP : 30442393Syz155240 if (flags & IPN_ICMPERR) 30452393Syz155240 sport = fin->fin_data[1]; 30462393Syz155240 else 30472393Syz155240 dport = fin->fin_data[1]; 30482393Syz155240 break; 30492393Syz155240 default : 30502393Syz155240 break; 30512393Syz155240 } 30522393Syz155240 30532393Syz155240 30542393Syz155240 if ((flags & SI_WILDP) != 0) 30552393Syz155240 goto find_in_wild_ports; 30562393Syz155240 30572393Syz155240 hv = NAT_HASH_FN(dst, dport, 0xffffffff); 30582393Syz155240 hv = NAT_HASH_FN(src.s_addr, hv + sport, ipf_nattable_sz); 30592393Syz155240 nat = nat_table[1][hv]; 30602393Syz155240 for (; nat; nat = nat->nat_hnext[1]) { 3061*2508Syz155240 if (nat->nat_ifps[0] != NULL) { 3062*2508Syz155240 if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 3063*2508Syz155240 continue; 3064*2508Syz155240 } else if (ifp != NULL) 3065*2508Syz155240 nat->nat_ifps[0] = ifp; 3066*2508Syz155240 30672393Syz155240 nflags = nat->nat_flags; 30682393Syz155240 30692393Syz155240 if (nat->nat_oip.s_addr == src.s_addr && 30702393Syz155240 nat->nat_outip.s_addr == dst && 30712393Syz155240 (((p == 0) && 30722393Syz155240 (sflags == (nat->nat_flags & IPN_TCPUDPICMP))) 30732393Syz155240 || (p == nat->nat_p))) { 30742393Syz155240 switch (p) 30752393Syz155240 { 30762393Syz155240 #if 0 30772393Syz155240 case IPPROTO_GRE : 30782393Syz155240 if (nat->nat_call[1] != fin->fin_data[0]) 30792393Syz155240 continue; 30802393Syz155240 break; 30812393Syz155240 #endif 30822393Syz155240 case IPPROTO_ICMP : 30832393Syz155240 if ((flags & IPN_ICMPERR) != 0) { 30842393Syz155240 if (nat->nat_outport != sport) 30852393Syz155240 continue; 30862393Syz155240 } else { 30872393Syz155240 if (nat->nat_outport != dport) 30882393Syz155240 continue; 30892393Syz155240 } 30902393Syz155240 break; 30912393Syz155240 case IPPROTO_TCP : 30922393Syz155240 case IPPROTO_UDP : 30932393Syz155240 if (nat->nat_oport != sport) 30942393Syz155240 continue; 30952393Syz155240 if (nat->nat_outport != dport) 30962393Syz155240 continue; 30972393Syz155240 break; 30982393Syz155240 default : 30992393Syz155240 break; 31002393Syz155240 } 31012393Syz155240 31022393Syz155240 ipn = nat->nat_ptr; 31032393Syz155240 if ((ipn != NULL) && (nat->nat_aps != NULL)) 31042393Syz155240 if (appr_match(fin, nat) != 0) 31052393Syz155240 continue; 31062393Syz155240 return nat; 31072393Syz155240 } 31082393Syz155240 } 31092393Syz155240 31102393Syz155240 /* 31112393Syz155240 * So if we didn't find it but there are wildcard members in the hash 31122393Syz155240 * table, go back and look for them. We do this search and update here 31132393Syz155240 * because it is modifying the NAT table and we want to do this only 31142393Syz155240 * for the first packet that matches. The exception, of course, is 31152393Syz155240 * for "dummy" (FI_IGNORE) lookups. 31162393Syz155240 */ 31172393Syz155240 find_in_wild_ports: 31182393Syz155240 if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) 31192393Syz155240 return NULL; 31202393Syz155240 if (nat_stats.ns_wilds == 0) 31212393Syz155240 return NULL; 31222393Syz155240 31232393Syz155240 RWLOCK_EXIT(&ipf_nat); 31242393Syz155240 31252393Syz155240 hv = NAT_HASH_FN(dst, 0, 0xffffffff); 31262393Syz155240 hv = NAT_HASH_FN(src.s_addr, hv, ipf_nattable_sz); 31272393Syz155240 31282393Syz155240 WRITE_ENTER(&ipf_nat); 31292393Syz155240 31302393Syz155240 nat = nat_table[1][hv]; 31312393Syz155240 for (; nat; nat = nat->nat_hnext[1]) { 3132*2508Syz155240 if (nat->nat_ifps[0] != NULL) { 3133*2508Syz155240 if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 3134*2508Syz155240 continue; 3135*2508Syz155240 } else if (ifp != NULL) 3136*2508Syz155240 nat->nat_ifps[0] = ifp; 31372393Syz155240 31382393Syz155240 if (nat->nat_p != fin->fin_p) 31392393Syz155240 continue; 31402393Syz155240 if (nat->nat_oip.s_addr != src.s_addr || 31412393Syz155240 nat->nat_outip.s_addr != dst) 31422393Syz155240 continue; 31432393Syz155240 31442393Syz155240 nflags = nat->nat_flags; 31452393Syz155240 if (!(nflags & (NAT_TCPUDP|SI_WILDP))) 31462393Syz155240 continue; 31472393Syz155240 31482393Syz155240 if (nat_wildok(nat, (int)sport, (int)dport, nflags, 31492393Syz155240 NAT_INBOUND) == 1) { 31502393Syz155240 if ((fin->fin_flx & FI_IGNORE) != 0) 31512393Syz155240 break; 31522393Syz155240 if ((nflags & SI_CLONE) != 0) { 31532393Syz155240 nat = fr_natclone(fin, nat); 31542393Syz155240 if (nat == NULL) 31552393Syz155240 break; 31562393Syz155240 } else { 31572393Syz155240 MUTEX_ENTER(&ipf_nat_new); 31582393Syz155240 nat_stats.ns_wilds--; 31592393Syz155240 MUTEX_EXIT(&ipf_nat_new); 31602393Syz155240 } 31612393Syz155240 nat->nat_oport = sport; 31622393Syz155240 nat->nat_outport = dport; 31632393Syz155240 nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 31642393Syz155240 nat_tabmove(nat); 31652393Syz155240 break; 31662393Syz155240 } 31672393Syz155240 } 31682393Syz155240 31692393Syz155240 MUTEX_DOWNGRADE(&ipf_nat); 31702393Syz155240 31712393Syz155240 return nat; 31722393Syz155240 } 31732393Syz155240 31742393Syz155240 31752393Syz155240 /* ------------------------------------------------------------------------ */ 31762393Syz155240 /* Function: nat_tabmove */ 31772393Syz155240 /* Returns: Nil */ 31782393Syz155240 /* Parameters: nat(I) - pointer to NAT structure */ 31792393Syz155240 /* Write Lock: ipf_nat */ 31802393Syz155240 /* */ 31812393Syz155240 /* This function is only called for TCP/UDP NAT table entries where the */ 31822393Syz155240 /* original was placed in the table without hashing on the ports and we now */ 31832393Syz155240 /* want to include hashing on port numbers. */ 31842393Syz155240 /* ------------------------------------------------------------------------ */ 31852393Syz155240 static void nat_tabmove(nat) 31862393Syz155240 nat_t *nat; 31872393Syz155240 { 31882393Syz155240 nat_t **natp; 31892393Syz155240 u_int hv; 31902393Syz155240 31912393Syz155240 if (nat->nat_flags & SI_CLONE) 31922393Syz155240 return; 31932393Syz155240 31942393Syz155240 /* 31952393Syz155240 * Remove the NAT entry from the old location 31962393Syz155240 */ 31972393Syz155240 if (nat->nat_hnext[0]) 31982393Syz155240 nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 31992393Syz155240 *nat->nat_phnext[0] = nat->nat_hnext[0]; 32002393Syz155240 nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--; 32012393Syz155240 32022393Syz155240 if (nat->nat_hnext[1]) 32032393Syz155240 nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 32042393Syz155240 *nat->nat_phnext[1] = nat->nat_hnext[1]; 32052393Syz155240 nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--; 32062393Syz155240 32072393Syz155240 /* 32082393Syz155240 * Add into the NAT table in the new position 32092393Syz155240 */ 32102393Syz155240 hv = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 0xffffffff); 32112393Syz155240 hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport, 32122393Syz155240 ipf_nattable_sz); 32132393Syz155240 nat->nat_hv[0] = hv; 32142393Syz155240 natp = &nat_table[0][hv]; 32152393Syz155240 if (*natp) 32162393Syz155240 (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 32172393Syz155240 nat->nat_phnext[0] = natp; 32182393Syz155240 nat->nat_hnext[0] = *natp; 32192393Syz155240 *natp = nat; 32202393Syz155240 nat_stats.ns_bucketlen[0][hv]++; 32212393Syz155240 32222393Syz155240 hv = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, 0xffffffff); 32232393Syz155240 hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport, 32242393Syz155240 ipf_nattable_sz); 32252393Syz155240 nat->nat_hv[1] = hv; 32262393Syz155240 natp = &nat_table[1][hv]; 32272393Syz155240 if (*natp) 32282393Syz155240 (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 32292393Syz155240 nat->nat_phnext[1] = natp; 32302393Syz155240 nat->nat_hnext[1] = *natp; 32312393Syz155240 *natp = nat; 32322393Syz155240 nat_stats.ns_bucketlen[1][hv]++; 32332393Syz155240 } 32342393Syz155240 32352393Syz155240 32362393Syz155240 /* ------------------------------------------------------------------------ */ 32372393Syz155240 /* Function: nat_outlookup */ 32382393Syz155240 /* Returns: nat_t* - NULL == no match, */ 32392393Syz155240 /* else pointer to matching NAT entry */ 32402393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 32412393Syz155240 /* flags(I) - NAT flags for this packet */ 32422393Syz155240 /* p(I) - protocol for this packet */ 32432393Syz155240 /* src(I) - source IP address */ 32442393Syz155240 /* dst(I) - destination IP address */ 32452393Syz155240 /* rw(I) - 1 == write lock on ipf_nat held, 0 == read lock. */ 32462393Syz155240 /* */ 32472393Syz155240 /* Lookup a nat entry based on the source 'real' ip address/port and */ 32482393Syz155240 /* destination address/port. We use this lookup when sending a packet out, */ 32492393Syz155240 /* we're looking for a table entry, based on the source address. */ 32502393Syz155240 /* */ 32512393Syz155240 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 32522393Syz155240 /* */ 32532393Syz155240 /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */ 32542393Syz155240 /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 32552393Syz155240 /* */ 32562393Syz155240 /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 32572393Syz155240 /* the packet is of said protocol */ 32582393Syz155240 /* ------------------------------------------------------------------------ */ 32592393Syz155240 nat_t *nat_outlookup(fin, flags, p, src, dst) 32602393Syz155240 fr_info_t *fin; 32612393Syz155240 u_int flags, p; 32622393Syz155240 struct in_addr src , dst; 32632393Syz155240 { 32642393Syz155240 u_short sport, dport; 32652393Syz155240 u_int sflags; 32662393Syz155240 ipnat_t *ipn; 32672393Syz155240 u_32_t srcip; 32682393Syz155240 nat_t *nat; 32692393Syz155240 int nflags; 32702393Syz155240 void *ifp; 32712393Syz155240 u_int hv; 3272*2508Syz155240 frentry_t *fr; 3273*2508Syz155240 3274*2508Syz155240 fr = fin->fin_fr; 3275*2508Syz155240 3276*2508Syz155240 if ((fr != NULL) && !(fr->fr_flags & FR_DUP) && 3277*2508Syz155240 fr->fr_tif.fd_ifp && fr->fr_tif.fd_ifp != (void *)-1) 3278*2508Syz155240 ifp = fr->fr_tif.fd_ifp; 3279*2508Syz155240 else 3280*2508Syz155240 ifp = fin->fin_ifp; 3281*2508Syz155240 32822393Syz155240 srcip = src.s_addr; 32832393Syz155240 sflags = flags & IPN_TCPUDPICMP; 32842393Syz155240 sport = 0; 32852393Syz155240 dport = 0; 32862393Syz155240 32872393Syz155240 switch (p) 32882393Syz155240 { 32892393Syz155240 case IPPROTO_TCP : 32902393Syz155240 case IPPROTO_UDP : 32912393Syz155240 sport = htons(fin->fin_data[0]); 32922393Syz155240 dport = htons(fin->fin_data[1]); 32932393Syz155240 break; 32942393Syz155240 case IPPROTO_ICMP : 32952393Syz155240 if (flags & IPN_ICMPERR) 32962393Syz155240 sport = fin->fin_data[1]; 32972393Syz155240 else 32982393Syz155240 dport = fin->fin_data[1]; 32992393Syz155240 break; 33002393Syz155240 default : 33012393Syz155240 break; 33022393Syz155240 } 33032393Syz155240 33042393Syz155240 if ((flags & SI_WILDP) != 0) 33052393Syz155240 goto find_out_wild_ports; 33062393Syz155240 33072393Syz155240 hv = NAT_HASH_FN(srcip, sport, 0xffffffff); 33082393Syz155240 hv = NAT_HASH_FN(dst.s_addr, hv + dport, ipf_nattable_sz); 33092393Syz155240 nat = nat_table[0][hv]; 33102393Syz155240 for (; nat; nat = nat->nat_hnext[0]) { 3311*2508Syz155240 if (nat->nat_ifps[1] != NULL) { 3312*2508Syz155240 if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 3313*2508Syz155240 continue; 3314*2508Syz155240 } else if (ifp != NULL) 3315*2508Syz155240 nat->nat_ifps[1] = ifp; 3316*2508Syz155240 33172393Syz155240 nflags = nat->nat_flags; 3318*2508Syz155240 33192393Syz155240 if (nat->nat_inip.s_addr == srcip && 33202393Syz155240 nat->nat_oip.s_addr == dst.s_addr && 33212393Syz155240 (((p == 0) && (sflags == (nflags & NAT_TCPUDPICMP))) 33222393Syz155240 || (p == nat->nat_p))) { 33232393Syz155240 switch (p) 33242393Syz155240 { 33252393Syz155240 #if 0 33262393Syz155240 case IPPROTO_GRE : 33272393Syz155240 if (nat->nat_call[1] != fin->fin_data[0]) 33282393Syz155240 continue; 33292393Syz155240 break; 33302393Syz155240 #endif 33312393Syz155240 case IPPROTO_TCP : 33322393Syz155240 case IPPROTO_UDP : 33332393Syz155240 if (nat->nat_oport != dport) 33342393Syz155240 continue; 33352393Syz155240 if (nat->nat_inport != sport) 33362393Syz155240 continue; 33372393Syz155240 break; 33382393Syz155240 default : 33392393Syz155240 break; 33402393Syz155240 } 33412393Syz155240 33422393Syz155240 ipn = nat->nat_ptr; 33432393Syz155240 if ((ipn != NULL) && (nat->nat_aps != NULL)) 33442393Syz155240 if (appr_match(fin, nat) != 0) 33452393Syz155240 continue; 33462393Syz155240 return nat; 33472393Syz155240 } 33482393Syz155240 } 33492393Syz155240 33502393Syz155240 /* 33512393Syz155240 * So if we didn't find it but there are wildcard members in the hash 33522393Syz155240 * table, go back and look for them. We do this search and update here 33532393Syz155240 * because it is modifying the NAT table and we want to do this only 33542393Syz155240 * for the first packet that matches. The exception, of course, is 33552393Syz155240 * for "dummy" (FI_IGNORE) lookups. 33562393Syz155240 */ 33572393Syz155240 find_out_wild_ports: 33582393Syz155240 if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) 33592393Syz155240 return NULL; 33602393Syz155240 if (nat_stats.ns_wilds == 0) 33612393Syz155240 return NULL; 33622393Syz155240 33632393Syz155240 RWLOCK_EXIT(&ipf_nat); 33642393Syz155240 33652393Syz155240 hv = NAT_HASH_FN(srcip, 0, 0xffffffff); 33662393Syz155240 hv = NAT_HASH_FN(dst.s_addr, hv, ipf_nattable_sz); 33672393Syz155240 33682393Syz155240 WRITE_ENTER(&ipf_nat); 33692393Syz155240 33702393Syz155240 nat = nat_table[0][hv]; 33712393Syz155240 for (; nat; nat = nat->nat_hnext[0]) { 3372*2508Syz155240 if (nat->nat_ifps[1] != NULL) { 3373*2508Syz155240 if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 3374*2508Syz155240 continue; 3375*2508Syz155240 } else if (ifp != NULL) 3376*2508Syz155240 nat->nat_ifps[1] = ifp; 33772393Syz155240 33782393Syz155240 if (nat->nat_p != fin->fin_p) 33792393Syz155240 continue; 33802393Syz155240 if ((nat->nat_inip.s_addr != srcip) || 33812393Syz155240 (nat->nat_oip.s_addr != dst.s_addr)) 33822393Syz155240 continue; 33832393Syz155240 33842393Syz155240 nflags = nat->nat_flags; 33852393Syz155240 if (!(nflags & (NAT_TCPUDP|SI_WILDP))) 33862393Syz155240 continue; 33872393Syz155240 33882393Syz155240 if (nat_wildok(nat, (int)sport, (int)dport, nflags, 33892393Syz155240 NAT_OUTBOUND) == 1) { 33902393Syz155240 if ((fin->fin_flx & FI_IGNORE) != 0) 33912393Syz155240 break; 33922393Syz155240 if ((nflags & SI_CLONE) != 0) { 33932393Syz155240 nat = fr_natclone(fin, nat); 33942393Syz155240 if (nat == NULL) 33952393Syz155240 break; 33962393Syz155240 } else { 33972393Syz155240 MUTEX_ENTER(&ipf_nat_new); 33982393Syz155240 nat_stats.ns_wilds--; 33992393Syz155240 MUTEX_EXIT(&ipf_nat_new); 34002393Syz155240 } 34012393Syz155240 nat->nat_inport = sport; 34022393Syz155240 nat->nat_oport = dport; 34032393Syz155240 if (nat->nat_outport == 0) 34042393Syz155240 nat->nat_outport = sport; 34052393Syz155240 nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 34062393Syz155240 nat_tabmove(nat); 34072393Syz155240 break; 34082393Syz155240 } 34092393Syz155240 } 34102393Syz155240 34112393Syz155240 MUTEX_DOWNGRADE(&ipf_nat); 34122393Syz155240 34132393Syz155240 return nat; 34142393Syz155240 } 34152393Syz155240 34162393Syz155240 34172393Syz155240 /* ------------------------------------------------------------------------ */ 34182393Syz155240 /* Function: nat_lookupredir */ 34192393Syz155240 /* Returns: nat_t* - NULL == no match, */ 34202393Syz155240 /* else pointer to matching NAT entry */ 34212393Syz155240 /* Parameters: np(I) - pointer to description of packet to find NAT table */ 34222393Syz155240 /* entry for. */ 34232393Syz155240 /* */ 34242393Syz155240 /* Lookup the NAT tables to search for a matching redirect */ 34252393Syz155240 /* ------------------------------------------------------------------------ */ 34262393Syz155240 nat_t *nat_lookupredir(np) 34272393Syz155240 natlookup_t *np; 34282393Syz155240 { 34292393Syz155240 fr_info_t fi; 34302393Syz155240 nat_t *nat; 34312393Syz155240 34322393Syz155240 bzero((char *)&fi, sizeof(fi)); 34332393Syz155240 if (np->nl_flags & IPN_IN) { 34342393Syz155240 fi.fin_data[0] = ntohs(np->nl_realport); 34352393Syz155240 fi.fin_data[1] = ntohs(np->nl_outport); 34362393Syz155240 } else { 34372393Syz155240 fi.fin_data[0] = ntohs(np->nl_inport); 34382393Syz155240 fi.fin_data[1] = ntohs(np->nl_outport); 34392393Syz155240 } 34402393Syz155240 if (np->nl_flags & IPN_TCP) 34412393Syz155240 fi.fin_p = IPPROTO_TCP; 34422393Syz155240 else if (np->nl_flags & IPN_UDP) 34432393Syz155240 fi.fin_p = IPPROTO_UDP; 34442393Syz155240 else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) 34452393Syz155240 fi.fin_p = IPPROTO_ICMP; 34462393Syz155240 34472393Syz155240 /* 34482393Syz155240 * We can do two sorts of lookups: 34492393Syz155240 * - IPN_IN: we have the `real' and `out' address, look for `in'. 34502393Syz155240 * - default: we have the `in' and `out' address, look for `real'. 34512393Syz155240 */ 34522393Syz155240 if (np->nl_flags & IPN_IN) { 34532393Syz155240 if ((nat = nat_inlookup(&fi, np->nl_flags, fi.fin_p, 34542393Syz155240 np->nl_realip, np->nl_outip))) { 34552393Syz155240 np->nl_inip = nat->nat_inip; 34562393Syz155240 np->nl_inport = nat->nat_inport; 34572393Syz155240 } 34582393Syz155240 } else { 34592393Syz155240 /* 34602393Syz155240 * If nl_inip is non null, this is a lookup based on the real 34612393Syz155240 * ip address. Else, we use the fake. 34622393Syz155240 */ 34632393Syz155240 if ((nat = nat_outlookup(&fi, np->nl_flags, fi.fin_p, 34642393Syz155240 np->nl_inip, np->nl_outip))) { 34652393Syz155240 34662393Syz155240 if ((np->nl_flags & IPN_FINDFORWARD) != 0) { 34672393Syz155240 fr_info_t fin; 34682393Syz155240 bzero((char *)&fin, sizeof(fin)); 34692393Syz155240 fin.fin_p = nat->nat_p; 34702393Syz155240 fin.fin_data[0] = ntohs(nat->nat_outport); 34712393Syz155240 fin.fin_data[1] = ntohs(nat->nat_oport); 34722393Syz155240 if (nat_inlookup(&fin, np->nl_flags, fin.fin_p, 34732393Syz155240 nat->nat_outip, 34742393Syz155240 nat->nat_oip) != NULL) { 34752393Syz155240 np->nl_flags &= ~IPN_FINDFORWARD; 34762393Syz155240 } 34772393Syz155240 } 34782393Syz155240 34792393Syz155240 np->nl_realip = nat->nat_outip; 34802393Syz155240 np->nl_realport = nat->nat_outport; 34812393Syz155240 } 34822393Syz155240 } 34832393Syz155240 34842393Syz155240 return nat; 34852393Syz155240 } 34862393Syz155240 34872393Syz155240 34882393Syz155240 /* ------------------------------------------------------------------------ */ 34892393Syz155240 /* Function: nat_match */ 34902393Syz155240 /* Returns: int - 0 == no match, 1 == match */ 34912393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 34922393Syz155240 /* np(I) - pointer to NAT rule */ 34932393Syz155240 /* */ 34942393Syz155240 /* Pull the matching of a packet against a NAT rule out of that complex */ 34952393Syz155240 /* loop inside fr_checknatin() and lay it out properly in its own function. */ 34962393Syz155240 /* ------------------------------------------------------------------------ */ 34972393Syz155240 static int nat_match(fin, np) 34982393Syz155240 fr_info_t *fin; 34992393Syz155240 ipnat_t *np; 35002393Syz155240 { 35012393Syz155240 frtuc_t *ft; 35022393Syz155240 35032393Syz155240 if (fin->fin_v != 4) 35042393Syz155240 return 0; 35052393Syz155240 35062393Syz155240 if (np->in_p && fin->fin_p != np->in_p) 35072393Syz155240 return 0; 35082393Syz155240 35092393Syz155240 if (fin->fin_out) { 35102393Syz155240 if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK))) 35112393Syz155240 return 0; 35122393Syz155240 if (((fin->fin_fi.fi_saddr & np->in_inmsk) != np->in_inip) 35132393Syz155240 ^ ((np->in_flags & IPN_NOTSRC) != 0)) 35142393Syz155240 return 0; 35152393Syz155240 if (((fin->fin_fi.fi_daddr & np->in_srcmsk) != np->in_srcip) 35162393Syz155240 ^ ((np->in_flags & IPN_NOTDST) != 0)) 35172393Syz155240 return 0; 35182393Syz155240 } else { 35192393Syz155240 if (!(np->in_redir & NAT_REDIRECT)) 35202393Syz155240 return 0; 35212393Syz155240 if (((fin->fin_fi.fi_saddr & np->in_srcmsk) != np->in_srcip) 35222393Syz155240 ^ ((np->in_flags & IPN_NOTSRC) != 0)) 35232393Syz155240 return 0; 35242393Syz155240 if (((fin->fin_fi.fi_daddr & np->in_outmsk) != np->in_outip) 35252393Syz155240 ^ ((np->in_flags & IPN_NOTDST) != 0)) 35262393Syz155240 return 0; 35272393Syz155240 } 35282393Syz155240 35292393Syz155240 ft = &np->in_tuc; 35302393Syz155240 if (!(fin->fin_flx & FI_TCPUDP) || 35312393Syz155240 (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { 35322393Syz155240 if (ft->ftu_scmp || ft->ftu_dcmp) 35332393Syz155240 return 0; 35342393Syz155240 return 1; 35352393Syz155240 } 35362393Syz155240 35372393Syz155240 return fr_tcpudpchk(fin, ft); 35382393Syz155240 } 35392393Syz155240 35402393Syz155240 35412393Syz155240 /* ------------------------------------------------------------------------ */ 35422393Syz155240 /* Function: nat_update */ 35432393Syz155240 /* Returns: Nil */ 35442393Syz155240 /* Parameters: nat(I) - pointer to NAT structure */ 35452393Syz155240 /* np(I) - pointer to NAT rule */ 35462393Syz155240 /* */ 35472393Syz155240 /* Updates the lifetime of a NAT table entry for non-TCP packets. Must be */ 35482393Syz155240 /* called with fin_rev updated - i.e. after calling nat_proto(). */ 35492393Syz155240 /* ------------------------------------------------------------------------ */ 35502393Syz155240 void nat_update(fin, nat, np) 35512393Syz155240 fr_info_t *fin; 35522393Syz155240 nat_t *nat; 35532393Syz155240 ipnat_t *np; 35542393Syz155240 { 35552393Syz155240 ipftq_t *ifq, *ifq2; 35562393Syz155240 ipftqent_t *tqe; 35572393Syz155240 35582393Syz155240 MUTEX_ENTER(&nat->nat_lock); 35592393Syz155240 tqe = &nat->nat_tqe; 35602393Syz155240 ifq = tqe->tqe_ifq; 35612393Syz155240 35622393Syz155240 /* 35632393Syz155240 * We allow over-riding of NAT timeouts from NAT rules, even for 35642393Syz155240 * TCP, however, if it is TCP and there is no rule timeout set, 35652393Syz155240 * then do not update the timeout here. 35662393Syz155240 */ 35672393Syz155240 if (np != NULL) 35682393Syz155240 ifq2 = np->in_tqehead[fin->fin_rev]; 35692393Syz155240 else 35702393Syz155240 ifq2 = NULL; 35712393Syz155240 35722393Syz155240 if (nat->nat_p == IPPROTO_TCP && ifq2 == NULL) { 35732393Syz155240 (void) fr_tcp_age(&nat->nat_tqe, fin, nat_tqb, 0); 35742393Syz155240 } else { 35752393Syz155240 if (ifq2 == NULL) { 35762393Syz155240 if (nat->nat_p == IPPROTO_UDP) 35772393Syz155240 ifq2 = &nat_udptq; 35782393Syz155240 else if (nat->nat_p == IPPROTO_ICMP) 35792393Syz155240 ifq2 = &nat_icmptq; 35802393Syz155240 else 35812393Syz155240 ifq2 = &nat_iptq; 35822393Syz155240 } 35832393Syz155240 35842393Syz155240 fr_movequeue(tqe, ifq, ifq2); 35852393Syz155240 } 35862393Syz155240 MUTEX_EXIT(&nat->nat_lock); 35872393Syz155240 } 35882393Syz155240 35892393Syz155240 35902393Syz155240 /* ------------------------------------------------------------------------ */ 35912393Syz155240 /* Function: fr_checknatout */ 35922393Syz155240 /* Returns: int - -1 == packet failed NAT checks so block it, */ 35932393Syz155240 /* 0 == no packet translation occurred, */ 35942393Syz155240 /* 1 == packet was successfully translated. */ 35952393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 35962393Syz155240 /* passp(I) - pointer to filtering result flags */ 35972393Syz155240 /* */ 35982393Syz155240 /* Check to see if an outcoming packet should be changed. ICMP packets are */ 35992393Syz155240 /* first checked to see if they match an existing entry (if an error), */ 36002393Syz155240 /* otherwise a search of the current NAT table is made. If neither results */ 36012393Syz155240 /* in a match then a search for a matching NAT rule is made. Create a new */ 36022393Syz155240 /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 36032393Syz155240 /* packet header(s) as required. */ 36042393Syz155240 /* ------------------------------------------------------------------------ */ 36052393Syz155240 int fr_checknatout(fin, passp) 36062393Syz155240 fr_info_t *fin; 36072393Syz155240 u_32_t *passp; 36082393Syz155240 { 36092393Syz155240 struct ifnet *ifp, *sifp; 36102393Syz155240 icmphdr_t *icmp = NULL; 36112393Syz155240 tcphdr_t *tcp = NULL; 36122393Syz155240 int rval, natfailed; 36132393Syz155240 ipnat_t *np = NULL; 36142393Syz155240 u_int nflags = 0; 36152393Syz155240 u_32_t ipa, iph; 36162393Syz155240 int natadd = 1; 36172393Syz155240 frentry_t *fr; 36182393Syz155240 nat_t *nat; 36192393Syz155240 36202393Syz155240 if (nat_stats.ns_rules == 0 || fr_nat_lock != 0) 36212393Syz155240 return 0; 36222393Syz155240 36232393Syz155240 natfailed = 0; 36242393Syz155240 fr = fin->fin_fr; 36252393Syz155240 sifp = fin->fin_ifp; 36262393Syz155240 if ((fr != NULL) && !(fr->fr_flags & FR_DUP) && 36272393Syz155240 fr->fr_tif.fd_ifp && fr->fr_tif.fd_ifp != (void *)-1) 36282393Syz155240 fin->fin_ifp = fr->fr_tif.fd_ifp; 36292393Syz155240 ifp = fin->fin_ifp; 36302393Syz155240 36312393Syz155240 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 36322393Syz155240 switch (fin->fin_p) 36332393Syz155240 { 36342393Syz155240 case IPPROTO_TCP : 36352393Syz155240 nflags = IPN_TCP; 36362393Syz155240 break; 36372393Syz155240 case IPPROTO_UDP : 36382393Syz155240 nflags = IPN_UDP; 36392393Syz155240 break; 36402393Syz155240 case IPPROTO_ICMP : 36412393Syz155240 icmp = fin->fin_dp; 36422393Syz155240 36432393Syz155240 /* 36442393Syz155240 * This is an incoming packet, so the destination is 36452393Syz155240 * the icmp_id and the source port equals 0 36462393Syz155240 */ 36472393Syz155240 if (nat_icmpquerytype4(icmp->icmp_type)) 36482393Syz155240 nflags = IPN_ICMPQUERY; 36492393Syz155240 break; 36502393Syz155240 default : 36512393Syz155240 break; 36522393Syz155240 } 36532393Syz155240 36542393Syz155240 if ((nflags & IPN_TCPUDP)) 36552393Syz155240 tcp = fin->fin_dp; 36562393Syz155240 } 36572393Syz155240 36582393Syz155240 ipa = fin->fin_saddr; 36592393Syz155240 36602393Syz155240 READ_ENTER(&ipf_nat); 36612393Syz155240 36622393Syz155240 if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && 36632393Syz155240 (nat = nat_icmperror(fin, &nflags, NAT_OUTBOUND))) 36642393Syz155240 /*EMPTY*/; 36652393Syz155240 else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin))) 36662393Syz155240 natadd = 0; 36672393Syz155240 else if ((nat = nat_outlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, 36682393Syz155240 fin->fin_src, fin->fin_dst))) { 36692393Syz155240 nflags = nat->nat_flags; 36702393Syz155240 } else { 36712393Syz155240 u_32_t hv, msk, nmsk; 36722393Syz155240 36732393Syz155240 /* 36742393Syz155240 * If there is no current entry in the nat table for this IP#, 36752393Syz155240 * create one for it (if there is a matching rule). 36762393Syz155240 */ 36772393Syz155240 RWLOCK_EXIT(&ipf_nat); 36782393Syz155240 msk = 0xffffffff; 36792393Syz155240 nmsk = nat_masks; 36802393Syz155240 WRITE_ENTER(&ipf_nat); 36812393Syz155240 maskloop: 36822393Syz155240 iph = ipa & htonl(msk); 36832393Syz155240 hv = NAT_HASH_FN(iph, 0, ipf_natrules_sz); 36842393Syz155240 for (np = nat_rules[hv]; np; np = np->in_mnext) 36852393Syz155240 { 3686*2508Syz155240 if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) 36872393Syz155240 continue; 36882393Syz155240 if (np->in_v != fin->fin_v) 36892393Syz155240 continue; 36902393Syz155240 if (np->in_p && (np->in_p != fin->fin_p)) 36912393Syz155240 continue; 36922393Syz155240 if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) 36932393Syz155240 continue; 36942393Syz155240 if (np->in_flags & IPN_FILTER) { 36952393Syz155240 if (!nat_match(fin, np)) 36962393Syz155240 continue; 36972393Syz155240 } else if ((ipa & np->in_inmsk) != np->in_inip) 36982393Syz155240 continue; 36992393Syz155240 37002393Syz155240 if ((fr != NULL) && 37012393Syz155240 !fr_matchtag(&np->in_tag, &fr->fr_nattag)) 37022393Syz155240 continue; 37032393Syz155240 37042393Syz155240 if (*np->in_plabel != '\0') { 37052393Syz155240 if (((np->in_flags & IPN_FILTER) == 0) && 37062393Syz155240 (np->in_dport != tcp->th_dport)) 37072393Syz155240 continue; 37082393Syz155240 if (appr_ok(fin, tcp, np) == 0) 37092393Syz155240 continue; 37102393Syz155240 } 37112393Syz155240 37122393Syz155240 if ((nat = nat_new(fin, np, NULL, nflags, 37132393Syz155240 NAT_OUTBOUND))) { 37142393Syz155240 np->in_hits++; 37152393Syz155240 break; 37162393Syz155240 } else 37172393Syz155240 natfailed = -1; 37182393Syz155240 } 37192393Syz155240 if ((np == NULL) && (nmsk != 0)) { 37202393Syz155240 while (nmsk) { 37212393Syz155240 msk <<= 1; 37222393Syz155240 if (nmsk & 0x80000000) 37232393Syz155240 break; 37242393Syz155240 nmsk <<= 1; 37252393Syz155240 } 37262393Syz155240 if (nmsk != 0) { 37272393Syz155240 nmsk <<= 1; 37282393Syz155240 goto maskloop; 37292393Syz155240 } 37302393Syz155240 } 37312393Syz155240 MUTEX_DOWNGRADE(&ipf_nat); 37322393Syz155240 } 37332393Syz155240 37342393Syz155240 if (nat != NULL) { 37352393Syz155240 rval = fr_natout(fin, nat, natadd, nflags); 37362393Syz155240 if (rval == 1) { 37372393Syz155240 MUTEX_ENTER(&nat->nat_lock); 37382393Syz155240 nat->nat_ref++; 37392393Syz155240 MUTEX_EXIT(&nat->nat_lock); 37402393Syz155240 fin->fin_nat = nat; 37412393Syz155240 } 37422393Syz155240 } else 37432393Syz155240 rval = natfailed; 37442393Syz155240 RWLOCK_EXIT(&ipf_nat); 37452393Syz155240 37462393Syz155240 if (rval == -1) { 37472393Syz155240 if (passp != NULL) 37482393Syz155240 *passp = FR_BLOCK; 37492393Syz155240 fin->fin_flx |= FI_BADNAT; 37502393Syz155240 } 37512393Syz155240 fin->fin_ifp = sifp; 37522393Syz155240 return rval; 37532393Syz155240 } 37542393Syz155240 37552393Syz155240 /* ------------------------------------------------------------------------ */ 37562393Syz155240 /* Function: fr_natout */ 37572393Syz155240 /* Returns: int - -1 == packet failed NAT checks so block it, */ 37582393Syz155240 /* 1 == packet was successfully translated. */ 37592393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 37602393Syz155240 /* nat(I) - pointer to NAT structure */ 37612393Syz155240 /* natadd(I) - flag indicating if it is safe to add frag cache */ 37622393Syz155240 /* nflags(I) - NAT flags set for this packet */ 37632393Syz155240 /* */ 37642393Syz155240 /* Translate a packet coming "out" on an interface. */ 37652393Syz155240 /* ------------------------------------------------------------------------ */ 37662393Syz155240 int fr_natout(fin, nat, natadd, nflags) 37672393Syz155240 fr_info_t *fin; 37682393Syz155240 nat_t *nat; 37692393Syz155240 int natadd; 37702393Syz155240 u_32_t nflags; 37712393Syz155240 { 37722393Syz155240 icmphdr_t *icmp; 37732393Syz155240 u_short *csump; 37742393Syz155240 tcphdr_t *tcp; 37752393Syz155240 ipnat_t *np; 37762393Syz155240 int i; 37772393Syz155240 37782393Syz155240 tcp = NULL; 37792393Syz155240 icmp = NULL; 37802393Syz155240 csump = NULL; 37812393Syz155240 np = nat->nat_ptr; 37822393Syz155240 37832393Syz155240 if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL)) 37842393Syz155240 (void) fr_nat_newfrag(fin, 0, nat); 37852393Syz155240 37862393Syz155240 MUTEX_ENTER(&nat->nat_lock); 37872393Syz155240 nat->nat_bytes[1] += fin->fin_plen; 37882393Syz155240 nat->nat_pkts[1]++; 37892393Syz155240 MUTEX_EXIT(&nat->nat_lock); 37902393Syz155240 37912393Syz155240 /* 37922393Syz155240 * Fix up checksums, not by recalculating them, but 37932393Syz155240 * simply computing adjustments. 37942393Syz155240 * This is only done for STREAMS based IP implementations where the 37952393Syz155240 * checksum has already been calculated by IP. In all other cases, 37962393Syz155240 * IPFilter is called before the checksum needs calculating so there 37972393Syz155240 * is no call to modify whatever is in the header now. 37982393Syz155240 */ 37992393Syz155240 if (fin->fin_v == 4) { 38002393Syz155240 if (nflags == IPN_ICMPERR) { 38012393Syz155240 u_32_t s1, s2, sumd; 38022393Syz155240 38032393Syz155240 s1 = LONG_SUM(ntohl(fin->fin_saddr)); 38042393Syz155240 s2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)); 38052393Syz155240 CALC_SUMD(s1, s2, sumd); 38062393Syz155240 fix_outcksum(fin, &fin->fin_ip->ip_sum, sumd); 38072393Syz155240 } 38082393Syz155240 #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 38092393Syz155240 defined(linux) || defined(BRIDGE_IPF) 38102393Syz155240 else { 38112393Syz155240 /* 38122393Syz155240 * Strictly speaking, this isn't necessary on BSD 38132393Syz155240 * kernels because they do checksum calculation after 38142393Syz155240 * this code has run BUT if ipfilter is being used 38152393Syz155240 * to do NAT as a bridge, that code doesn't exist. 38162393Syz155240 */ 38172393Syz155240 if (nat->nat_dir == NAT_OUTBOUND) 38182393Syz155240 fix_outcksum(fin, &fin->fin_ip->ip_sum, 38192393Syz155240 nat->nat_ipsumd); 38202393Syz155240 else 38212393Syz155240 fix_incksum(fin, &fin->fin_ip->ip_sum, 38222393Syz155240 nat->nat_ipsumd); 38232393Syz155240 } 38242393Syz155240 #endif 38252393Syz155240 } 38262393Syz155240 38272393Syz155240 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 38282393Syz155240 if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) { 38292393Syz155240 tcp = fin->fin_dp; 38302393Syz155240 38312393Syz155240 tcp->th_sport = nat->nat_outport; 38322393Syz155240 fin->fin_data[0] = ntohs(nat->nat_outport); 38332393Syz155240 } 38342393Syz155240 38352393Syz155240 if ((nat->nat_outport != 0) && (nflags & IPN_ICMPQUERY)) { 38362393Syz155240 icmp = fin->fin_dp; 38372393Syz155240 icmp->icmp_id = nat->nat_outport; 38382393Syz155240 } 38392393Syz155240 38402393Syz155240 csump = nat_proto(fin, nat, nflags); 38412393Syz155240 } 38422393Syz155240 38432393Syz155240 fin->fin_ip->ip_src = nat->nat_outip; 38442393Syz155240 38452393Syz155240 nat_update(fin, nat, np); 38462393Syz155240 38472393Syz155240 /* 38482393Syz155240 * The above comments do not hold for layer 4 (or higher) checksums... 38492393Syz155240 */ 38502393Syz155240 if (csump != NULL) { 38512393Syz155240 if (nat->nat_dir == NAT_OUTBOUND) 38522393Syz155240 fix_outcksum(fin, csump, nat->nat_sumd[1]); 38532393Syz155240 else 38542393Syz155240 fix_incksum(fin, csump, nat->nat_sumd[1]); 38552393Syz155240 } 38562393Syz155240 #ifdef IPFILTER_SYNC 38572393Syz155240 ipfsync_update(SMC_NAT, fin, nat->nat_sync); 38582393Syz155240 #endif 38592393Syz155240 /* ------------------------------------------------------------- */ 38602393Syz155240 /* A few quick notes: */ 38612393Syz155240 /* Following are test conditions prior to calling the */ 38622393Syz155240 /* appr_check routine. */ 38632393Syz155240 /* */ 38642393Syz155240 /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 38652393Syz155240 /* with a redirect rule, we attempt to match the packet's */ 38662393Syz155240 /* source port against in_dport, otherwise we'd compare the */ 38672393Syz155240 /* packet's destination. */ 38682393Syz155240 /* ------------------------------------------------------------- */ 38692393Syz155240 if ((np != NULL) && (np->in_apr != NULL)) { 38702393Syz155240 i = appr_check(fin, nat); 38712393Syz155240 if (i == 0) 38722393Syz155240 i = 1; 38732393Syz155240 } else 38742393Syz155240 i = 1; 38752393Syz155240 ATOMIC_INCL(nat_stats.ns_mapped[1]); 38762393Syz155240 fin->fin_flx |= FI_NATED; 38772393Syz155240 return i; 38782393Syz155240 } 38792393Syz155240 38802393Syz155240 38812393Syz155240 /* ------------------------------------------------------------------------ */ 38822393Syz155240 /* Function: fr_checknatin */ 38832393Syz155240 /* Returns: int - -1 == packet failed NAT checks so block it, */ 38842393Syz155240 /* 0 == no packet translation occurred, */ 38852393Syz155240 /* 1 == packet was successfully translated. */ 38862393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 38872393Syz155240 /* passp(I) - pointer to filtering result flags */ 38882393Syz155240 /* */ 38892393Syz155240 /* Check to see if an incoming packet should be changed. ICMP packets are */ 38902393Syz155240 /* first checked to see if they match an existing entry (if an error), */ 38912393Syz155240 /* otherwise a search of the current NAT table is made. If neither results */ 38922393Syz155240 /* in a match then a search for a matching NAT rule is made. Create a new */ 38932393Syz155240 /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 38942393Syz155240 /* packet header(s) as required. */ 38952393Syz155240 /* ------------------------------------------------------------------------ */ 38962393Syz155240 int fr_checknatin(fin, passp) 38972393Syz155240 fr_info_t *fin; 38982393Syz155240 u_32_t *passp; 38992393Syz155240 { 39002393Syz155240 u_int nflags, natadd; 39012393Syz155240 int rval, natfailed; 39022393Syz155240 struct ifnet *ifp; 39032393Syz155240 struct in_addr in; 39042393Syz155240 icmphdr_t *icmp; 39052393Syz155240 tcphdr_t *tcp; 39062393Syz155240 u_short dport; 39072393Syz155240 ipnat_t *np; 39082393Syz155240 nat_t *nat; 39092393Syz155240 u_32_t iph; 39102393Syz155240 39112393Syz155240 if (nat_stats.ns_rules == 0 || fr_nat_lock != 0) 39122393Syz155240 return 0; 39132393Syz155240 39142393Syz155240 tcp = NULL; 39152393Syz155240 icmp = NULL; 39162393Syz155240 dport = 0; 39172393Syz155240 natadd = 1; 39182393Syz155240 nflags = 0; 39192393Syz155240 natfailed = 0; 39202393Syz155240 ifp = fin->fin_ifp; 39212393Syz155240 39222393Syz155240 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 39232393Syz155240 switch (fin->fin_p) 39242393Syz155240 { 39252393Syz155240 case IPPROTO_TCP : 39262393Syz155240 nflags = IPN_TCP; 39272393Syz155240 break; 39282393Syz155240 case IPPROTO_UDP : 39292393Syz155240 nflags = IPN_UDP; 39302393Syz155240 break; 39312393Syz155240 case IPPROTO_ICMP : 39322393Syz155240 icmp = fin->fin_dp; 39332393Syz155240 39342393Syz155240 /* 39352393Syz155240 * This is an incoming packet, so the destination is 39362393Syz155240 * the icmp_id and the source port equals 0 39372393Syz155240 */ 39382393Syz155240 if (nat_icmpquerytype4(icmp->icmp_type)) { 39392393Syz155240 nflags = IPN_ICMPQUERY; 39402393Syz155240 dport = icmp->icmp_id; 39412393Syz155240 } break; 39422393Syz155240 default : 39432393Syz155240 break; 39442393Syz155240 } 39452393Syz155240 39462393Syz155240 if ((nflags & IPN_TCPUDP)) { 39472393Syz155240 tcp = fin->fin_dp; 39482393Syz155240 dport = tcp->th_dport; 39492393Syz155240 } 39502393Syz155240 } 39512393Syz155240 39522393Syz155240 in = fin->fin_dst; 39532393Syz155240 39542393Syz155240 READ_ENTER(&ipf_nat); 39552393Syz155240 39562393Syz155240 if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && 39572393Syz155240 (nat = nat_icmperror(fin, &nflags, NAT_INBOUND))) 39582393Syz155240 /*EMPTY*/; 39592393Syz155240 else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin))) 39602393Syz155240 natadd = 0; 39612393Syz155240 else if ((nat = nat_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, 39622393Syz155240 fin->fin_src, in))) { 39632393Syz155240 nflags = nat->nat_flags; 39642393Syz155240 } else { 39652393Syz155240 u_32_t hv, msk, rmsk; 39662393Syz155240 39672393Syz155240 RWLOCK_EXIT(&ipf_nat); 39682393Syz155240 rmsk = rdr_masks; 39692393Syz155240 msk = 0xffffffff; 39702393Syz155240 WRITE_ENTER(&ipf_nat); 39712393Syz155240 /* 39722393Syz155240 * If there is no current entry in the nat table for this IP#, 39732393Syz155240 * create one for it (if there is a matching rule). 39742393Syz155240 */ 39752393Syz155240 maskloop: 39762393Syz155240 iph = in.s_addr & htonl(msk); 39772393Syz155240 hv = NAT_HASH_FN(iph, 0, ipf_rdrrules_sz); 39782393Syz155240 for (np = rdr_rules[hv]; np; np = np->in_rnext) { 39792393Syz155240 if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) 39802393Syz155240 continue; 39812393Syz155240 if (np->in_v != fin->fin_v) 39822393Syz155240 continue; 39832393Syz155240 if (np->in_p && (np->in_p != fin->fin_p)) 39842393Syz155240 continue; 39852393Syz155240 if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) 39862393Syz155240 continue; 39872393Syz155240 if (np->in_flags & IPN_FILTER) { 39882393Syz155240 if (!nat_match(fin, np)) 39892393Syz155240 continue; 39902393Syz155240 } else { 39912393Syz155240 if ((in.s_addr & np->in_outmsk) != np->in_outip) 39922393Syz155240 continue; 39932393Syz155240 if (np->in_pmin && 39942393Syz155240 ((ntohs(np->in_pmax) < ntohs(dport)) || 39952393Syz155240 (ntohs(dport) < ntohs(np->in_pmin)))) 39962393Syz155240 continue; 39972393Syz155240 } 39982393Syz155240 39992393Syz155240 if (*np->in_plabel != '\0') { 40002393Syz155240 if (!appr_ok(fin, tcp, np)) { 40012393Syz155240 continue; 40022393Syz155240 } 40032393Syz155240 } 40042393Syz155240 40052393Syz155240 nat = nat_new(fin, np, NULL, nflags, NAT_INBOUND); 40062393Syz155240 if (nat != NULL) { 40072393Syz155240 np->in_hits++; 40082393Syz155240 break; 40092393Syz155240 } else 40102393Syz155240 natfailed = -1; 40112393Syz155240 } 40122393Syz155240 40132393Syz155240 if ((np == NULL) && (rmsk != 0)) { 40142393Syz155240 while (rmsk) { 40152393Syz155240 msk <<= 1; 40162393Syz155240 if (rmsk & 0x80000000) 40172393Syz155240 break; 40182393Syz155240 rmsk <<= 1; 40192393Syz155240 } 40202393Syz155240 if (rmsk != 0) { 40212393Syz155240 rmsk <<= 1; 40222393Syz155240 goto maskloop; 40232393Syz155240 } 40242393Syz155240 } 40252393Syz155240 MUTEX_DOWNGRADE(&ipf_nat); 40262393Syz155240 } 40272393Syz155240 if (nat != NULL) { 40282393Syz155240 rval = fr_natin(fin, nat, natadd, nflags); 40292393Syz155240 if (rval == 1) { 40302393Syz155240 MUTEX_ENTER(&nat->nat_lock); 40312393Syz155240 nat->nat_ref++; 40322393Syz155240 MUTEX_EXIT(&nat->nat_lock); 40332393Syz155240 fin->fin_nat = nat; 40342393Syz155240 fin->fin_state = nat->nat_state; 40352393Syz155240 } 40362393Syz155240 } else 40372393Syz155240 rval = natfailed; 40382393Syz155240 RWLOCK_EXIT(&ipf_nat); 40392393Syz155240 40402393Syz155240 if (rval == -1) { 40412393Syz155240 if (passp != NULL) 40422393Syz155240 *passp = FR_BLOCK; 40432393Syz155240 fin->fin_flx |= FI_BADNAT; 40442393Syz155240 } 40452393Syz155240 return rval; 40462393Syz155240 } 40472393Syz155240 40482393Syz155240 40492393Syz155240 /* ------------------------------------------------------------------------ */ 40502393Syz155240 /* Function: fr_natin */ 40512393Syz155240 /* Returns: int - -1 == packet failed NAT checks so block it, */ 40522393Syz155240 /* 1 == packet was successfully translated. */ 40532393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 40542393Syz155240 /* nat(I) - pointer to NAT structure */ 40552393Syz155240 /* natadd(I) - flag indicating if it is safe to add frag cache */ 40562393Syz155240 /* nflags(I) - NAT flags set for this packet */ 40572393Syz155240 /* Locks Held: ipf_nat (READ) */ 40582393Syz155240 /* */ 40592393Syz155240 /* Translate a packet coming "in" on an interface. */ 40602393Syz155240 /* ------------------------------------------------------------------------ */ 40612393Syz155240 int fr_natin(fin, nat, natadd, nflags) 40622393Syz155240 fr_info_t *fin; 40632393Syz155240 nat_t *nat; 40642393Syz155240 int natadd; 40652393Syz155240 u_32_t nflags; 40662393Syz155240 { 40672393Syz155240 icmphdr_t *icmp; 40682393Syz155240 u_short *csump; 40692393Syz155240 tcphdr_t *tcp; 40702393Syz155240 ipnat_t *np; 40712393Syz155240 int i; 40722393Syz155240 40732393Syz155240 tcp = NULL; 40742393Syz155240 csump = NULL; 40752393Syz155240 np = nat->nat_ptr; 40762393Syz155240 fin->fin_fr = nat->nat_fr; 40772393Syz155240 40782393Syz155240 if (np != NULL) { 40792393Syz155240 if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) 40802393Syz155240 (void) fr_nat_newfrag(fin, 0, nat); 40812393Syz155240 40822393Syz155240 /* ------------------------------------------------------------- */ 40832393Syz155240 /* A few quick notes: */ 40842393Syz155240 /* Following are test conditions prior to calling the */ 40852393Syz155240 /* appr_check routine. */ 40862393Syz155240 /* */ 40872393Syz155240 /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 40882393Syz155240 /* with a map rule, we attempt to match the packet's */ 40892393Syz155240 /* source port against in_dport, otherwise we'd compare the */ 40902393Syz155240 /* packet's destination. */ 40912393Syz155240 /* ------------------------------------------------------------- */ 40922393Syz155240 if (np->in_apr != NULL) { 40932393Syz155240 i = appr_check(fin, nat); 40942393Syz155240 if (i == -1) { 40952393Syz155240 return -1; 40962393Syz155240 } 40972393Syz155240 } 40982393Syz155240 } 40992393Syz155240 41002393Syz155240 #ifdef IPFILTER_SYNC 41012393Syz155240 ipfsync_update(SMC_NAT, fin, nat->nat_sync); 41022393Syz155240 #endif 41032393Syz155240 41042393Syz155240 MUTEX_ENTER(&nat->nat_lock); 41052393Syz155240 nat->nat_bytes[0] += fin->fin_plen; 41062393Syz155240 nat->nat_pkts[0]++; 41072393Syz155240 MUTEX_EXIT(&nat->nat_lock); 41082393Syz155240 41092393Syz155240 fin->fin_ip->ip_dst = nat->nat_inip; 41102393Syz155240 fin->fin_fi.fi_daddr = nat->nat_inip.s_addr; 41112393Syz155240 if (nflags & IPN_TCPUDP) 41122393Syz155240 tcp = fin->fin_dp; 41132393Syz155240 41142393Syz155240 /* 41152393Syz155240 * Fix up checksums, not by recalculating them, but 41162393Syz155240 * simply computing adjustments. 41172393Syz155240 * Why only do this for some platforms on inbound packets ? 41182393Syz155240 * Because for those that it is done, IP processing is yet to happen 41192393Syz155240 * and so the IPv4 header checksum has not yet been evaluated. 41202393Syz155240 * Perhaps it should always be done for the benefit of things like 41212393Syz155240 * fast forwarding (so that it doesn't need to be recomputed) but with 41222393Syz155240 * header checksum offloading, perhaps it is a moot point. 41232393Syz155240 */ 41242393Syz155240 #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 41252393Syz155240 defined(__osf__) || defined(linux) 41262393Syz155240 if (nat->nat_dir == NAT_OUTBOUND) 41272393Syz155240 fix_incksum(fin, &fin->fin_ip->ip_sum, nat->nat_ipsumd); 41282393Syz155240 else 41292393Syz155240 fix_outcksum(fin, &fin->fin_ip->ip_sum, nat->nat_ipsumd); 41302393Syz155240 #endif 41312393Syz155240 41322393Syz155240 if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 41332393Syz155240 if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) { 41342393Syz155240 tcp->th_dport = nat->nat_inport; 41352393Syz155240 fin->fin_data[1] = ntohs(nat->nat_inport); 41362393Syz155240 } 41372393Syz155240 41382393Syz155240 41392393Syz155240 if ((nat->nat_inport != 0) && (nflags & IPN_ICMPQUERY)) { 41402393Syz155240 icmp = fin->fin_dp; 41412393Syz155240 41422393Syz155240 icmp->icmp_id = nat->nat_inport; 41432393Syz155240 } 41442393Syz155240 41452393Syz155240 csump = nat_proto(fin, nat, nflags); 41462393Syz155240 } 41472393Syz155240 41482393Syz155240 nat_update(fin, nat, np); 41492393Syz155240 41502393Syz155240 /* 41512393Syz155240 * The above comments do not hold for layer 4 (or higher) checksums... 41522393Syz155240 */ 41532393Syz155240 if (csump != NULL) { 41542393Syz155240 if (nat->nat_dir == NAT_OUTBOUND) 41552393Syz155240 fix_incksum(fin, csump, nat->nat_sumd[0]); 41562393Syz155240 else 41572393Syz155240 fix_outcksum(fin, csump, nat->nat_sumd[0]); 41582393Syz155240 } 41592393Syz155240 ATOMIC_INCL(nat_stats.ns_mapped[0]); 41602393Syz155240 fin->fin_flx |= FI_NATED; 41612393Syz155240 if (np != NULL && np->in_tag.ipt_num[0] != 0) 41622393Syz155240 fin->fin_nattag = &np->in_tag; 41632393Syz155240 return 1; 41642393Syz155240 } 41652393Syz155240 41662393Syz155240 41672393Syz155240 /* ------------------------------------------------------------------------ */ 41682393Syz155240 /* Function: nat_proto */ 41692393Syz155240 /* Returns: u_short* - pointer to transport header checksum to update, */ 41702393Syz155240 /* NULL if the transport protocol is not recognised */ 41712393Syz155240 /* as needing a checksum update. */ 41722393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 41732393Syz155240 /* nat(I) - pointer to NAT structure */ 41742393Syz155240 /* nflags(I) - NAT flags set for this packet */ 41752393Syz155240 /* */ 41762393Syz155240 /* Return the pointer to the checksum field for each protocol so understood.*/ 41772393Syz155240 /* If support for making other changes to a protocol header is required, */ 41782393Syz155240 /* that is not strictly 'address' translation, such as clamping the MSS in */ 41792393Syz155240 /* TCP down to a specific value, then do it from here. */ 41802393Syz155240 /* ------------------------------------------------------------------------ */ 41812393Syz155240 u_short *nat_proto(fin, nat, nflags) 41822393Syz155240 fr_info_t *fin; 41832393Syz155240 nat_t *nat; 41842393Syz155240 u_int nflags; 41852393Syz155240 { 41862393Syz155240 icmphdr_t *icmp; 41872393Syz155240 u_short *csump; 41882393Syz155240 tcphdr_t *tcp; 41892393Syz155240 udphdr_t *udp; 41902393Syz155240 41912393Syz155240 csump = NULL; 41922393Syz155240 if (fin->fin_out == 0) { 41932393Syz155240 fin->fin_rev = (nat->nat_dir == NAT_OUTBOUND); 41942393Syz155240 } else { 41952393Syz155240 fin->fin_rev = (nat->nat_dir == NAT_INBOUND); 41962393Syz155240 } 41972393Syz155240 41982393Syz155240 switch (fin->fin_p) 41992393Syz155240 { 42002393Syz155240 case IPPROTO_TCP : 42012393Syz155240 tcp = fin->fin_dp; 42022393Syz155240 42032393Syz155240 csump = &tcp->th_sum; 42042393Syz155240 42052393Syz155240 /* 42062393Syz155240 * Do a MSS CLAMPING on a SYN packet, 42072393Syz155240 * only deal IPv4 for now. 42082393Syz155240 */ 42092393Syz155240 if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0) 42102393Syz155240 nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump); 42112393Syz155240 42122393Syz155240 break; 42132393Syz155240 42142393Syz155240 case IPPROTO_UDP : 42152393Syz155240 udp = fin->fin_dp; 42162393Syz155240 42172393Syz155240 if (udp->uh_sum) 42182393Syz155240 csump = &udp->uh_sum; 42192393Syz155240 break; 42202393Syz155240 42212393Syz155240 case IPPROTO_ICMP : 42222393Syz155240 icmp = fin->fin_dp; 42232393Syz155240 42242393Syz155240 if ((nflags & IPN_ICMPQUERY) != 0) { 42252393Syz155240 if (icmp->icmp_cksum != 0) 42262393Syz155240 csump = &icmp->icmp_cksum; 42272393Syz155240 } 42282393Syz155240 break; 42292393Syz155240 } 42302393Syz155240 return csump; 42312393Syz155240 } 42322393Syz155240 42332393Syz155240 42342393Syz155240 /* ------------------------------------------------------------------------ */ 42352393Syz155240 /* Function: fr_natunload */ 42362393Syz155240 /* Returns: Nil */ 42372393Syz155240 /* Parameters: Nil */ 42382393Syz155240 /* */ 42392393Syz155240 /* Free all memory used by NAT structures allocated at runtime. */ 42402393Syz155240 /* ------------------------------------------------------------------------ */ 42412393Syz155240 void fr_natunload() 42422393Syz155240 { 42432393Syz155240 ipftq_t *ifq, *ifqnext; 42442393Syz155240 42452393Syz155240 (void) nat_clearlist(); 42462393Syz155240 (void) nat_flushtable(); 42472393Syz155240 42482393Syz155240 /* 42492393Syz155240 * Proxy timeout queues are not cleaned here because although they 42502393Syz155240 * exist on the NAT list, appr_unload is called after fr_natunload 42512393Syz155240 * and the proxies actually are responsible for them being created. 42522393Syz155240 * Should the proxy timeouts have their own list? There's no real 42532393Syz155240 * justification as this is the only complication. 42542393Syz155240 */ 42552393Syz155240 for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) { 42562393Syz155240 ifqnext = ifq->ifq_next; 42572393Syz155240 if (((ifq->ifq_flags & IFQF_PROXY) == 0) && 42582393Syz155240 (fr_deletetimeoutqueue(ifq) == 0)) 42592393Syz155240 fr_freetimeoutqueue(ifq); 42602393Syz155240 } 42612393Syz155240 42622393Syz155240 if (nat_table[0] != NULL) { 42632393Syz155240 KFREES(nat_table[0], sizeof(nat_t *) * ipf_nattable_sz); 42642393Syz155240 nat_table[0] = NULL; 42652393Syz155240 } 42662393Syz155240 if (nat_table[1] != NULL) { 42672393Syz155240 KFREES(nat_table[1], sizeof(nat_t *) * ipf_nattable_sz); 42682393Syz155240 nat_table[1] = NULL; 42692393Syz155240 } 42702393Syz155240 if (nat_rules != NULL) { 42712393Syz155240 KFREES(nat_rules, sizeof(ipnat_t *) * ipf_natrules_sz); 42722393Syz155240 nat_rules = NULL; 42732393Syz155240 } 42742393Syz155240 if (rdr_rules != NULL) { 42752393Syz155240 KFREES(rdr_rules, sizeof(ipnat_t *) * ipf_rdrrules_sz); 42762393Syz155240 rdr_rules = NULL; 42772393Syz155240 } 42782393Syz155240 if (maptable != NULL) { 42792393Syz155240 KFREES(maptable, sizeof(hostmap_t *) * ipf_hostmap_sz); 42802393Syz155240 maptable = NULL; 42812393Syz155240 } 42822393Syz155240 if (nat_stats.ns_bucketlen[0] != NULL) { 42832393Syz155240 KFREES(nat_stats.ns_bucketlen[0], 42842393Syz155240 sizeof(u_long *) * ipf_nattable_sz); 42852393Syz155240 nat_stats.ns_bucketlen[0] = NULL; 42862393Syz155240 } 42872393Syz155240 if (nat_stats.ns_bucketlen[1] != NULL) { 42882393Syz155240 KFREES(nat_stats.ns_bucketlen[1], 42892393Syz155240 sizeof(u_long *) * ipf_nattable_sz); 42902393Syz155240 nat_stats.ns_bucketlen[1] = NULL; 42912393Syz155240 } 42922393Syz155240 42932393Syz155240 if (fr_nat_maxbucket_reset == 1) 42942393Syz155240 fr_nat_maxbucket = 0; 42952393Syz155240 42962393Syz155240 if (fr_nat_init == 1) { 42972393Syz155240 fr_nat_init = 0; 42982393Syz155240 fr_sttab_destroy(nat_tqb); 42992393Syz155240 43002393Syz155240 RW_DESTROY(&ipf_natfrag); 43012393Syz155240 RW_DESTROY(&ipf_nat); 43022393Syz155240 43032393Syz155240 MUTEX_DESTROY(&ipf_nat_new); 43042393Syz155240 MUTEX_DESTROY(&ipf_natio); 43052393Syz155240 43062393Syz155240 MUTEX_DESTROY(&nat_udptq.ifq_lock); 43072393Syz155240 MUTEX_DESTROY(&nat_icmptq.ifq_lock); 43082393Syz155240 MUTEX_DESTROY(&nat_iptq.ifq_lock); 43092393Syz155240 } 43102393Syz155240 } 43112393Syz155240 43122393Syz155240 43132393Syz155240 /* ------------------------------------------------------------------------ */ 43142393Syz155240 /* Function: fr_natexpire */ 43152393Syz155240 /* Returns: Nil */ 43162393Syz155240 /* Parameters: Nil */ 43172393Syz155240 /* */ 43182393Syz155240 /* Check all of the timeout queues for entries at the top which need to be */ 43192393Syz155240 /* expired. */ 43202393Syz155240 /* ------------------------------------------------------------------------ */ 43212393Syz155240 void fr_natexpire() 43222393Syz155240 { 43232393Syz155240 ipftq_t *ifq, *ifqnext; 43242393Syz155240 ipftqent_t *tqe, *tqn; 43252393Syz155240 int i; 43262393Syz155240 SPL_INT(s); 43272393Syz155240 43282393Syz155240 SPL_NET(s); 43292393Syz155240 WRITE_ENTER(&ipf_nat); 43302393Syz155240 for (ifq = nat_tqb, i = 0; ifq != NULL; ifq = ifq->ifq_next) { 43312393Syz155240 for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { 43322393Syz155240 if (tqe->tqe_die > fr_ticks) 43332393Syz155240 break; 43342393Syz155240 tqn = tqe->tqe_next; 43352393Syz155240 nat_delete(tqe->tqe_parent, NL_EXPIRE); 43362393Syz155240 } 43372393Syz155240 } 43382393Syz155240 43392393Syz155240 for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) { 43402393Syz155240 ifqnext = ifq->ifq_next; 43412393Syz155240 43422393Syz155240 for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { 43432393Syz155240 if (tqe->tqe_die > fr_ticks) 43442393Syz155240 break; 43452393Syz155240 tqn = tqe->tqe_next; 43462393Syz155240 nat_delete(tqe->tqe_parent, NL_EXPIRE); 43472393Syz155240 } 43482393Syz155240 } 43492393Syz155240 43502393Syz155240 for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) { 43512393Syz155240 ifqnext = ifq->ifq_next; 43522393Syz155240 43532393Syz155240 if (((ifq->ifq_flags & IFQF_DELETE) != 0) && 43542393Syz155240 (ifq->ifq_ref == 0)) { 43552393Syz155240 fr_freetimeoutqueue(ifq); 43562393Syz155240 } 43572393Syz155240 } 43582393Syz155240 43592393Syz155240 RWLOCK_EXIT(&ipf_nat); 43602393Syz155240 SPL_X(s); 43612393Syz155240 } 43622393Syz155240 43632393Syz155240 43642393Syz155240 /* ------------------------------------------------------------------------ */ 43652393Syz155240 /* Function: fr_natsync */ 43662393Syz155240 /* Returns: Nil */ 43672393Syz155240 /* Parameters: ifp(I) - pointer to network interface */ 43682393Syz155240 /* */ 43692393Syz155240 /* Walk through all of the currently active NAT sessions, looking for those */ 43702393Syz155240 /* which need to have their translated address updated. */ 43712393Syz155240 /* ------------------------------------------------------------------------ */ 43722393Syz155240 void fr_natsync(ifp) 43732393Syz155240 void *ifp; 43742393Syz155240 { 43752393Syz155240 u_32_t sum1, sum2, sumd; 43762393Syz155240 struct in_addr in; 43772393Syz155240 ipnat_t *n; 43782393Syz155240 nat_t *nat; 43792393Syz155240 void *ifp2; 43802393Syz155240 SPL_INT(s); 43812393Syz155240 43822393Syz155240 if (fr_running <= 0) 43832393Syz155240 return; 43842393Syz155240 43852393Syz155240 /* 43862393Syz155240 * Change IP addresses for NAT sessions for any protocol except TCP 43872393Syz155240 * since it will break the TCP connection anyway. The only rules 43882393Syz155240 * which will get changed are those which are "map ... -> 0/32", 43892393Syz155240 * where the rule specifies the address is taken from the interface. 43902393Syz155240 */ 43912393Syz155240 SPL_NET(s); 43922393Syz155240 WRITE_ENTER(&ipf_nat); 43932393Syz155240 43942393Syz155240 if (fr_running <= 0) { 43952393Syz155240 RWLOCK_EXIT(&ipf_nat); 43962393Syz155240 return; 43972393Syz155240 } 43982393Syz155240 43992393Syz155240 for (nat = nat_instances; nat; nat = nat->nat_next) { 44002393Syz155240 if ((nat->nat_flags & IPN_TCP) != 0) 44012393Syz155240 continue; 44022393Syz155240 n = nat->nat_ptr; 44032393Syz155240 if ((n == NULL) || 44042393Syz155240 (n->in_outip != 0) || (n->in_outmsk != 0xffffffff)) 44052393Syz155240 continue; 44062393Syz155240 if (((ifp == NULL) || (ifp == nat->nat_ifps[0]) || 44072393Syz155240 (ifp == nat->nat_ifps[1]))) { 44082393Syz155240 nat->nat_ifps[0] = GETIFP(nat->nat_ifnames[0], 4); 44092393Syz155240 if (nat->nat_ifnames[1][0] != '\0') { 44102393Syz155240 nat->nat_ifps[1] = GETIFP(nat->nat_ifnames[1], 44112393Syz155240 4); 44122393Syz155240 } else 44132393Syz155240 nat->nat_ifps[1] = nat->nat_ifps[0]; 44142393Syz155240 ifp2 = nat->nat_ifps[0]; 44152393Syz155240 if (ifp2 == NULL) 44162393Syz155240 continue; 44172393Syz155240 44182393Syz155240 /* 44192393Syz155240 * Change the map-to address to be the same as the 44202393Syz155240 * new one. 44212393Syz155240 */ 44222393Syz155240 sum1 = nat->nat_outip.s_addr; 44232393Syz155240 if (fr_ifpaddr(4, FRI_NORMAL, ifp2, &in, NULL) != -1) 44242393Syz155240 nat->nat_outip = in; 44252393Syz155240 sum2 = nat->nat_outip.s_addr; 44262393Syz155240 44272393Syz155240 if (sum1 == sum2) 44282393Syz155240 continue; 44292393Syz155240 /* 44302393Syz155240 * Readjust the checksum adjustment to take into 44312393Syz155240 * account the new IP#. 44322393Syz155240 */ 44332393Syz155240 CALC_SUMD(sum1, sum2, sumd); 44342393Syz155240 /* XXX - dont change for TCP when solaris does 44352393Syz155240 * hardware checksumming. 44362393Syz155240 */ 44372393Syz155240 sumd += nat->nat_sumd[0]; 44382393Syz155240 nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 44392393Syz155240 nat->nat_sumd[1] = nat->nat_sumd[0]; 44402393Syz155240 } 44412393Syz155240 } 44422393Syz155240 44432393Syz155240 for (n = nat_list; (n != NULL); n = n->in_next) { 44442393Syz155240 if ((ifp == NULL) || (n->in_ifps[0] == ifp)) 44452393Syz155240 n->in_ifps[0] = fr_resolvenic(n->in_ifnames[0], 4); 44462393Syz155240 if ((ifp == NULL) || (n->in_ifps[1] == ifp)) 44472393Syz155240 n->in_ifps[1] = fr_resolvenic(n->in_ifnames[1], 4); 44482393Syz155240 } 44492393Syz155240 RWLOCK_EXIT(&ipf_nat); 44502393Syz155240 SPL_X(s); 44512393Syz155240 } 44522393Syz155240 44532393Syz155240 44542393Syz155240 /* ------------------------------------------------------------------------ */ 44552393Syz155240 /* Function: nat_icmpquerytype4 */ 44562393Syz155240 /* Returns: int - 1 == success, 0 == failure */ 44572393Syz155240 /* Parameters: icmptype(I) - ICMP type number */ 44582393Syz155240 /* */ 44592393Syz155240 /* Tests to see if the ICMP type number passed is a query/response type or */ 44602393Syz155240 /* not. */ 44612393Syz155240 /* ------------------------------------------------------------------------ */ 44622393Syz155240 static INLINE int nat_icmpquerytype4(icmptype) 44632393Syz155240 int icmptype; 44642393Syz155240 { 44652393Syz155240 44662393Syz155240 /* 44672393Syz155240 * For the ICMP query NAT code, it is essential that both the query 44682393Syz155240 * and the reply match on the NAT rule. Because the NAT structure 44692393Syz155240 * does not keep track of the icmptype, and a single NAT structure 44702393Syz155240 * is used for all icmp types with the same src, dest and id, we 44712393Syz155240 * simply define the replies as queries as well. The funny thing is, 44722393Syz155240 * altough it seems silly to call a reply a query, this is exactly 44732393Syz155240 * as it is defined in the IPv4 specification 44742393Syz155240 */ 44752393Syz155240 44762393Syz155240 switch (icmptype) 44772393Syz155240 { 44782393Syz155240 44792393Syz155240 case ICMP_ECHOREPLY: 44802393Syz155240 case ICMP_ECHO: 44812393Syz155240 /* route aedvertisement/solliciation is currently unsupported: */ 44822393Syz155240 /* it would require rewriting the ICMP data section */ 44832393Syz155240 case ICMP_TSTAMP: 44842393Syz155240 case ICMP_TSTAMPREPLY: 44852393Syz155240 case ICMP_IREQ: 44862393Syz155240 case ICMP_IREQREPLY: 44872393Syz155240 case ICMP_MASKREQ: 44882393Syz155240 case ICMP_MASKREPLY: 44892393Syz155240 return 1; 44902393Syz155240 default: 44912393Syz155240 return 0; 44922393Syz155240 } 44932393Syz155240 } 44942393Syz155240 44952393Syz155240 44962393Syz155240 /* ------------------------------------------------------------------------ */ 44972393Syz155240 /* Function: nat_log */ 44982393Syz155240 /* Returns: Nil */ 44992393Syz155240 /* Parameters: nat(I) - pointer to NAT structure */ 45002393Syz155240 /* type(I) - type of log entry to create */ 45012393Syz155240 /* */ 45022393Syz155240 /* Creates a NAT log entry. */ 45032393Syz155240 /* ------------------------------------------------------------------------ */ 45042393Syz155240 void nat_log(nat, type) 45052393Syz155240 struct nat *nat; 45062393Syz155240 u_int type; 45072393Syz155240 { 45082393Syz155240 #ifdef IPFILTER_LOG 45092393Syz155240 # ifndef LARGE_NAT 45102393Syz155240 struct ipnat *np; 45112393Syz155240 int rulen; 45122393Syz155240 # endif 45132393Syz155240 struct natlog natl; 45142393Syz155240 void *items[1]; 45152393Syz155240 size_t sizes[1]; 45162393Syz155240 int types[1]; 45172393Syz155240 45182393Syz155240 natl.nl_inip = nat->nat_inip; 45192393Syz155240 natl.nl_outip = nat->nat_outip; 45202393Syz155240 natl.nl_origip = nat->nat_oip; 45212393Syz155240 natl.nl_bytes[0] = nat->nat_bytes[0]; 45222393Syz155240 natl.nl_bytes[1] = nat->nat_bytes[1]; 45232393Syz155240 natl.nl_pkts[0] = nat->nat_pkts[0]; 45242393Syz155240 natl.nl_pkts[1] = nat->nat_pkts[1]; 45252393Syz155240 natl.nl_origport = nat->nat_oport; 45262393Syz155240 natl.nl_inport = nat->nat_inport; 45272393Syz155240 natl.nl_outport = nat->nat_outport; 45282393Syz155240 natl.nl_p = nat->nat_p; 45292393Syz155240 natl.nl_type = type; 45302393Syz155240 natl.nl_rule = -1; 45312393Syz155240 # ifndef LARGE_NAT 45322393Syz155240 if (nat->nat_ptr != NULL) { 45332393Syz155240 for (rulen = 0, np = nat_list; np; np = np->in_next, rulen++) 45342393Syz155240 if (np == nat->nat_ptr) { 45352393Syz155240 natl.nl_rule = rulen; 45362393Syz155240 break; 45372393Syz155240 } 45382393Syz155240 } 45392393Syz155240 # endif 45402393Syz155240 items[0] = &natl; 45412393Syz155240 sizes[0] = sizeof(natl); 45422393Syz155240 types[0] = 0; 45432393Syz155240 45442393Syz155240 (void) ipllog(IPL_LOGNAT, NULL, items, sizes, types, 1); 45452393Syz155240 #endif 45462393Syz155240 } 45472393Syz155240 45482393Syz155240 45492393Syz155240 #if defined(__OpenBSD__) 45502393Syz155240 /* ------------------------------------------------------------------------ */ 45512393Syz155240 /* Function: nat_ifdetach */ 45522393Syz155240 /* Returns: Nil */ 45532393Syz155240 /* Parameters: ifp(I) - pointer to network interface */ 45542393Syz155240 /* */ 45552393Syz155240 /* Compatibility interface for OpenBSD to trigger the correct updating of */ 45562393Syz155240 /* interface references within IPFilter. */ 45572393Syz155240 /* ------------------------------------------------------------------------ */ 45582393Syz155240 void nat_ifdetach(ifp) 45592393Syz155240 void *ifp; 45602393Syz155240 { 45612393Syz155240 frsync(ifp); 45622393Syz155240 return; 45632393Syz155240 } 45642393Syz155240 #endif 45652393Syz155240 45662393Syz155240 45672393Syz155240 /* ------------------------------------------------------------------------ */ 45682393Syz155240 /* Function: fr_natderef */ 45692393Syz155240 /* Returns: Nil */ 45702393Syz155240 /* Parameters: isp(I) - pointer to pointer to NAT table entry */ 45712393Syz155240 /* */ 45722393Syz155240 /* Decrement the reference counter for this NAT table entry and free it if */ 45732393Syz155240 /* there are no more things using it. */ 45742393Syz155240 /* ------------------------------------------------------------------------ */ 45752393Syz155240 void fr_natderef(natp) 45762393Syz155240 nat_t **natp; 45772393Syz155240 { 45782393Syz155240 nat_t *nat; 45792393Syz155240 45802393Syz155240 nat = *natp; 45812393Syz155240 *natp = NULL; 45822393Syz155240 WRITE_ENTER(&ipf_nat); 45832393Syz155240 nat->nat_ref--; 45842393Syz155240 if (nat->nat_ref == 0) 45852393Syz155240 nat_delete(nat, NL_EXPIRE); 45862393Syz155240 RWLOCK_EXIT(&ipf_nat); 45872393Syz155240 } 45882393Syz155240 45892393Syz155240 45902393Syz155240 /* ------------------------------------------------------------------------ */ 45912393Syz155240 /* Function: fr_natclone */ 45922393Syz155240 /* Returns: ipstate_t* - NULL == cloning failed, */ 45932393Syz155240 /* else pointer to new state structure */ 45942393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 45952393Syz155240 /* is(I) - pointer to master state structure */ 45962393Syz155240 /* Write Lock: ipf_nat */ 45972393Syz155240 /* */ 45982393Syz155240 /* Create a "duplcate" state table entry from the master. */ 45992393Syz155240 /* ------------------------------------------------------------------------ */ 46002393Syz155240 static nat_t *fr_natclone(fin, nat) 46012393Syz155240 fr_info_t *fin; 46022393Syz155240 nat_t *nat; 46032393Syz155240 { 46042393Syz155240 frentry_t *fr; 46052393Syz155240 nat_t *clone; 46062393Syz155240 ipnat_t *np; 46072393Syz155240 46082393Syz155240 KMALLOC(clone, nat_t *); 46092393Syz155240 if (clone == NULL) 46102393Syz155240 return NULL; 46112393Syz155240 bcopy((char *)nat, (char *)clone, sizeof(*clone)); 46122393Syz155240 46132393Syz155240 MUTEX_NUKE(&clone->nat_lock); 46142393Syz155240 46152393Syz155240 clone->nat_aps = NULL; 46162393Syz155240 /* 46172393Syz155240 * Initialize all these so that nat_delete() doesn't cause a crash. 46182393Syz155240 */ 46192393Syz155240 clone->nat_tqe.tqe_pnext = NULL; 46202393Syz155240 clone->nat_tqe.tqe_next = NULL; 46212393Syz155240 clone->nat_tqe.tqe_ifq = NULL; 46222393Syz155240 clone->nat_tqe.tqe_parent = clone; 46232393Syz155240 46242393Syz155240 clone->nat_flags &= ~SI_CLONE; 46252393Syz155240 clone->nat_flags |= SI_CLONED; 46262393Syz155240 46272393Syz155240 if (clone->nat_hm) 46282393Syz155240 clone->nat_hm->hm_ref++; 46292393Syz155240 46302393Syz155240 if (nat_insert(clone, fin->fin_rev) == -1) { 46312393Syz155240 KFREE(clone); 46322393Syz155240 return NULL; 46332393Syz155240 } 46342393Syz155240 np = clone->nat_ptr; 46352393Syz155240 if (np != NULL) { 46362393Syz155240 if (nat_logging) 46372393Syz155240 nat_log(clone, (u_int)np->in_redir); 46382393Syz155240 np->in_use++; 46392393Syz155240 } 46402393Syz155240 fr = clone->nat_fr; 46412393Syz155240 if (fr != NULL) { 46422393Syz155240 MUTEX_ENTER(&fr->fr_lock); 46432393Syz155240 fr->fr_ref++; 46442393Syz155240 MUTEX_EXIT(&fr->fr_lock); 46452393Syz155240 } 46462393Syz155240 46472393Syz155240 /* 46482393Syz155240 * Because the clone is created outside the normal loop of things and 46492393Syz155240 * TCP has special needs in terms of state, initialise the timeout 46502393Syz155240 * state of the new NAT from here. 46512393Syz155240 */ 46522393Syz155240 if (clone->nat_p == IPPROTO_TCP) { 46532393Syz155240 (void) fr_tcp_age(&clone->nat_tqe, fin, nat_tqb, 46542393Syz155240 clone->nat_flags); 46552393Syz155240 } 46562393Syz155240 #ifdef IPFILTER_SYNC 46572393Syz155240 clone->nat_sync = ipfsync_new(SMC_NAT, fin, clone); 46582393Syz155240 #endif 46592393Syz155240 if (nat_logging) 46602393Syz155240 nat_log(clone, NL_CLONE); 46612393Syz155240 return clone; 46622393Syz155240 } 46632393Syz155240 46642393Syz155240 46652393Syz155240 /* ------------------------------------------------------------------------ */ 46662393Syz155240 /* Function: nat_wildok */ 46672393Syz155240 /* Returns: int - 1 == packet's ports match wildcards */ 46682393Syz155240 /* 0 == packet's ports don't match wildcards */ 46692393Syz155240 /* Parameters: nat(I) - NAT entry */ 46702393Syz155240 /* sport(I) - source port */ 46712393Syz155240 /* dport(I) - destination port */ 46722393Syz155240 /* flags(I) - wildcard flags */ 46732393Syz155240 /* dir(I) - packet direction */ 46742393Syz155240 /* */ 46752393Syz155240 /* Use NAT entry and packet direction to determine which combination of */ 46762393Syz155240 /* wildcard flags should be used. */ 46772393Syz155240 /* ------------------------------------------------------------------------ */ 46782393Syz155240 static INLINE int nat_wildok(nat, sport, dport, flags, dir) 46792393Syz155240 nat_t *nat; 46802393Syz155240 int sport; 46812393Syz155240 int dport; 46822393Syz155240 int flags; 46832393Syz155240 int dir; 46842393Syz155240 { 46852393Syz155240 /* 46862393Syz155240 * When called by dir is set to 46872393Syz155240 * nat_inlookup NAT_INBOUND (0) 46882393Syz155240 * nat_outlookup NAT_OUTBOUND (1) 46892393Syz155240 * 46902393Syz155240 * We simply combine the packet's direction in dir with the original 46912393Syz155240 * "intended" direction of that NAT entry in nat->nat_dir to decide 46922393Syz155240 * which combination of wildcard flags to allow. 46932393Syz155240 */ 46942393Syz155240 46952393Syz155240 switch ((dir << 1) | nat->nat_dir) 46962393Syz155240 { 46972393Syz155240 case 3: /* outbound packet / outbound entry */ 46982393Syz155240 if (((nat->nat_inport == sport) || 46992393Syz155240 (flags & SI_W_SPORT)) && 47002393Syz155240 ((nat->nat_oport == dport) || 47012393Syz155240 (flags & SI_W_DPORT))) 47022393Syz155240 return 1; 47032393Syz155240 break; 47042393Syz155240 case 2: /* outbound packet / inbound entry */ 47052393Syz155240 if (((nat->nat_outport == sport) || 47062393Syz155240 (flags & SI_W_DPORT)) && 47072393Syz155240 ((nat->nat_oport == dport) || 47082393Syz155240 (flags & SI_W_SPORT))) 47092393Syz155240 return 1; 47102393Syz155240 break; 47112393Syz155240 case 1: /* inbound packet / outbound entry */ 47122393Syz155240 if (((nat->nat_oport == sport) || 47132393Syz155240 (flags & SI_W_DPORT)) && 47142393Syz155240 ((nat->nat_outport == dport) || 47152393Syz155240 (flags & SI_W_SPORT))) 47162393Syz155240 return 1; 47172393Syz155240 break; 47182393Syz155240 case 0: /* inbound packet / inbound entry */ 47192393Syz155240 if (((nat->nat_oport == sport) || 47202393Syz155240 (flags & SI_W_SPORT)) && 47212393Syz155240 ((nat->nat_outport == dport) || 47222393Syz155240 (flags & SI_W_DPORT))) 47232393Syz155240 return 1; 47242393Syz155240 break; 47252393Syz155240 default: 47262393Syz155240 break; 47272393Syz155240 } 47282393Syz155240 47292393Syz155240 return(0); 47302393Syz155240 } 47312393Syz155240 47322393Syz155240 47332393Syz155240 /* ------------------------------------------------------------------------ */ 47342393Syz155240 /* Function: nat_mssclamp */ 47352393Syz155240 /* Returns: Nil */ 47362393Syz155240 /* Parameters: tcp(I) - pointer to TCP header */ 47372393Syz155240 /* maxmss(I) - value to clamp the TCP MSS to */ 47382393Syz155240 /* fin(I) - pointer to packet information */ 47392393Syz155240 /* csump(I) - pointer to TCP checksum */ 47402393Syz155240 /* */ 47412393Syz155240 /* Check for MSS option and clamp it if necessary. If found and changed, */ 47422393Syz155240 /* then the TCP header checksum will be updated to reflect the change in */ 47432393Syz155240 /* the MSS. */ 47442393Syz155240 /* ------------------------------------------------------------------------ */ 47452393Syz155240 static void nat_mssclamp(tcp, maxmss, fin, csump) 47462393Syz155240 tcphdr_t *tcp; 47472393Syz155240 u_32_t maxmss; 47482393Syz155240 fr_info_t *fin; 47492393Syz155240 u_short *csump; 47502393Syz155240 { 47512393Syz155240 u_char *cp, *ep, opt; 47522393Syz155240 int hlen, advance; 47532393Syz155240 u_32_t mss, sumd; 47542393Syz155240 47552393Syz155240 hlen = TCP_OFF(tcp) << 2; 47562393Syz155240 if (hlen > sizeof(*tcp)) { 47572393Syz155240 cp = (u_char *)tcp + sizeof(*tcp); 47582393Syz155240 ep = (u_char *)tcp + hlen; 47592393Syz155240 47602393Syz155240 while (cp < ep) { 47612393Syz155240 opt = cp[0]; 47622393Syz155240 if (opt == TCPOPT_EOL) 47632393Syz155240 break; 47642393Syz155240 else if (opt == TCPOPT_NOP) { 47652393Syz155240 cp++; 47662393Syz155240 continue; 47672393Syz155240 } 47682393Syz155240 47692393Syz155240 if (cp + 1 >= ep) 47702393Syz155240 break; 47712393Syz155240 advance = cp[1]; 47722393Syz155240 if ((cp + advance > ep) || (advance <= 0)) 47732393Syz155240 break; 47742393Syz155240 switch (opt) 47752393Syz155240 { 47762393Syz155240 case TCPOPT_MAXSEG: 47772393Syz155240 if (advance != 4) 47782393Syz155240 break; 47792393Syz155240 mss = cp[2] * 256 + cp[3]; 47802393Syz155240 if (mss > maxmss) { 47812393Syz155240 cp[2] = maxmss / 256; 47822393Syz155240 cp[3] = maxmss & 0xff; 47832393Syz155240 CALC_SUMD(mss, maxmss, sumd); 47842393Syz155240 fix_outcksum(fin, csump, sumd); 47852393Syz155240 } 47862393Syz155240 break; 47872393Syz155240 default: 47882393Syz155240 /* ignore unknown options */ 47892393Syz155240 break; 47902393Syz155240 } 47912393Syz155240 47922393Syz155240 cp += advance; 47932393Syz155240 } 47942393Syz155240 } 47952393Syz155240 } 47962393Syz155240 47972393Syz155240 47982393Syz155240 /* ------------------------------------------------------------------------ */ 47992393Syz155240 /* Function: fr_setnatqueue */ 48002393Syz155240 /* Returns: Nil */ 48012393Syz155240 /* Parameters: nat(I)- pointer to NAT structure */ 48022393Syz155240 /* rev(I) - forward(0) or reverse(1) direction */ 48032393Syz155240 /* Locks: ipf_nat (read or write) */ 48042393Syz155240 /* */ 48052393Syz155240 /* Put the NAT entry on its default queue entry, using rev as a helped in */ 48062393Syz155240 /* determining which queue it should be placed on. */ 48072393Syz155240 /* ------------------------------------------------------------------------ */ 48082393Syz155240 void fr_setnatqueue(nat, rev) 48092393Syz155240 nat_t *nat; 48102393Syz155240 int rev; 48112393Syz155240 { 48122393Syz155240 ipftq_t *oifq, *nifq; 48132393Syz155240 48142393Syz155240 if (nat->nat_ptr != NULL) 48152393Syz155240 nifq = nat->nat_ptr->in_tqehead[rev]; 48162393Syz155240 else 48172393Syz155240 nifq = NULL; 48182393Syz155240 48192393Syz155240 if (nifq == NULL) { 48202393Syz155240 switch (nat->nat_p) 48212393Syz155240 { 48222393Syz155240 case IPPROTO_UDP : 48232393Syz155240 nifq = &nat_udptq; 48242393Syz155240 break; 48252393Syz155240 case IPPROTO_ICMP : 48262393Syz155240 nifq = &nat_icmptq; 48272393Syz155240 break; 48282393Syz155240 case IPPROTO_TCP : 48292393Syz155240 nifq = nat_tqb + nat->nat_tqe.tqe_state[rev]; 48302393Syz155240 break; 48312393Syz155240 default : 48322393Syz155240 nifq = &nat_iptq; 48332393Syz155240 break; 48342393Syz155240 } 48352393Syz155240 } 48362393Syz155240 48372393Syz155240 oifq = nat->nat_tqe.tqe_ifq; 48382393Syz155240 /* 48392393Syz155240 * If it's currently on a timeout queue, move it from one queue to 48402393Syz155240 * another, else put it on the end of the newly determined queue. 48412393Syz155240 */ 48422393Syz155240 if (oifq != NULL) 48432393Syz155240 fr_movequeue(&nat->nat_tqe, oifq, nifq); 48442393Syz155240 else 48452393Syz155240 fr_queueappend(&nat->nat_tqe, nifq, nat); 48462393Syz155240 return; 48472393Syz155240 } 4848