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