ip_nat.c revision 67614
153642Sguido/* 260852Sdarrenr * Copyright (C) 1995-2000 by Darren Reed. 353642Sguido * 453642Sguido * Redistribution and use in source and binary forms are permitted 553642Sguido * provided that this notice is preserved and due credit is given 653642Sguido * to the original author and the contributors. 753642Sguido * 853642Sguido * Added redirect stuff and a LOT of bug fixes. (mcn@EnGarde.com) 953642Sguido */ 1053642Sguido#if !defined(lint) 1153642Sguidostatic const char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed"; 1263523Sdarrenr/*static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.37.2.16 2000/07/18 13:57:40 darrenr Exp $";*/ 1357126Sguidostatic const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_nat.c 67614 2000-10-26 12:33:42Z darrenr $"; 1453642Sguido#endif 1553642Sguido 1653642Sguido#if defined(__FreeBSD__) && defined(KERNEL) && !defined(_KERNEL) 1753642Sguido#define _KERNEL 1853642Sguido#endif 1953642Sguido 2053642Sguido#include <sys/errno.h> 2153642Sguido#include <sys/types.h> 2253642Sguido#include <sys/param.h> 2353642Sguido#include <sys/time.h> 2453642Sguido#include <sys/file.h> 2553642Sguido#if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \ 2653642Sguido defined(_KERNEL) 2753642Sguido# include "opt_ipfilter_log.h" 2853642Sguido#endif 2953642Sguido#if !defined(_KERNEL) && !defined(KERNEL) 3053642Sguido# include <stdio.h> 3153642Sguido# include <string.h> 3253642Sguido# include <stdlib.h> 3353642Sguido#endif 3460852Sdarrenr#if (defined(KERNEL) || defined(_KERNEL)) && (__FreeBSD_version >= 220000) 3553642Sguido# include <sys/filio.h> 3653642Sguido# include <sys/fcntl.h> 3753642Sguido#else 3853642Sguido# include <sys/ioctl.h> 3953642Sguido#endif 4053642Sguido#include <sys/fcntl.h> 4153642Sguido#include <sys/uio.h> 4253642Sguido#ifndef linux 4353642Sguido# include <sys/protosw.h> 4453642Sguido#endif 4553642Sguido#include <sys/socket.h> 4653642Sguido#if defined(_KERNEL) && !defined(linux) 4753642Sguido# include <sys/systm.h> 4853642Sguido#endif 4953642Sguido#if !defined(__SVR4) && !defined(__svr4__) 5053642Sguido# ifndef linux 5153642Sguido# include <sys/mbuf.h> 5253642Sguido# endif 5353642Sguido#else 5453642Sguido# include <sys/filio.h> 5553642Sguido# include <sys/byteorder.h> 5653642Sguido# ifdef _KERNEL 5753642Sguido# include <sys/dditypes.h> 5853642Sguido# endif 5953642Sguido# include <sys/stream.h> 6053642Sguido# include <sys/kmem.h> 6153642Sguido#endif 6253642Sguido#if __FreeBSD_version >= 300000 6353642Sguido# include <sys/queue.h> 6453642Sguido#endif 6553642Sguido#include <net/if.h> 6653642Sguido#if __FreeBSD_version >= 300000 6753642Sguido# include <net/if_var.h> 6853642Sguido# if defined(_KERNEL) && !defined(IPFILTER_LKM) 6953642Sguido# include "opt_ipfilter.h" 7053642Sguido# endif 7153642Sguido#endif 7253642Sguido#ifdef sun 7353642Sguido# include <net/af.h> 7453642Sguido#endif 7553642Sguido#include <net/route.h> 7653642Sguido#include <netinet/in.h> 7753642Sguido#include <netinet/in_systm.h> 7853642Sguido#include <netinet/ip.h> 7953642Sguido 8053642Sguido#ifdef __sgi 8153642Sguido# ifdef IFF_DRVRLOCK /* IRIX6 */ 8253642Sguido#include <sys/hashing.h> 8353642Sguido#include <netinet/in_var.h> 8453642Sguido# endif 8553642Sguido#endif 8653642Sguido 8753642Sguido#ifdef RFC1825 8853642Sguido# include <vpn/md5.h> 8953642Sguido# include <vpn/ipsec.h> 9053642Sguidoextern struct ifnet vpnif; 9153642Sguido#endif 9253642Sguido 9353642Sguido#ifndef linux 9453642Sguido# include <netinet/ip_var.h> 9553642Sguido#endif 9653642Sguido#include <netinet/tcp.h> 9753642Sguido#include <netinet/udp.h> 9853642Sguido#include <netinet/ip_icmp.h> 9953642Sguido#include "netinet/ip_compat.h" 10053642Sguido#include <netinet/tcpip.h> 10153642Sguido#include "netinet/ip_fil.h" 10253642Sguido#include "netinet/ip_proxy.h" 10353642Sguido#include "netinet/ip_nat.h" 10453642Sguido#include "netinet/ip_frag.h" 10553642Sguido#include "netinet/ip_state.h" 10653642Sguido#if (__FreeBSD_version >= 300000) 10753642Sguido# include <sys/malloc.h> 10853642Sguido#endif 10953642Sguido#ifndef MIN 11053642Sguido# define MIN(a,b) (((a)<(b))?(a):(b)) 11153642Sguido#endif 11253642Sguido#undef SOCKADDR_IN 11353642Sguido#define SOCKADDR_IN struct sockaddr_in 11453642Sguido 11553642Sguidonat_t **nat_table[2] = { NULL, NULL }, 11653642Sguido *nat_instances = NULL; 11753642Sguidoipnat_t *nat_list = NULL; 11853642Sguidou_int ipf_nattable_sz = NAT_TABLE_SZ; 11953642Sguidou_int ipf_natrules_sz = NAT_SIZE; 12053642Sguidou_int ipf_rdrrules_sz = RDR_SIZE; 12160852Sdarrenru_int ipf_hostmap_sz = HOSTMAP_SIZE; 12267614Sdarrenrint nat_wilds = 0; 12353642Sguidou_32_t nat_masks = 0; 12453642Sguidou_32_t rdr_masks = 0; 12553642Sguidoipnat_t **nat_rules = NULL; 12653642Sguidoipnat_t **rdr_rules = NULL; 12760852Sdarrenrhostmap_t **maptable = NULL; 12853642Sguido 12953642Sguidou_long fr_defnatage = DEF_NAT_AGE, 13053642Sguido fr_defnaticmpage = 6; /* 3 seconds */ 13164580Sdarrenrnatstat_t nat_stats; 13260852Sdarrenrint fr_nat_lock = 0; 13353642Sguido#if (SOLARIS || defined(__sgi)) && defined(_KERNEL) 13460852Sdarrenrextern kmutex_t ipf_rw, ipf_hostmap; 13553642Sguidoextern KRWLOCK_T ipf_nat; 13653642Sguido#endif 13753642Sguido 13853642Sguidostatic int nat_flushtable __P((void)); 13953642Sguidostatic int nat_clearlist __P((void)); 14060852Sdarrenrstatic void nat_addnat __P((struct ipnat *)); 14160852Sdarrenrstatic void nat_addrdr __P((struct ipnat *)); 14260857Sdarrenrstatic void nat_delete __P((struct nat *)); 14353642Sguidostatic void nat_delrdr __P((struct ipnat *)); 14453642Sguidostatic void nat_delnat __P((struct ipnat *)); 14560852Sdarrenrstatic int fr_natgetent __P((caddr_t)); 14660852Sdarrenrstatic int fr_natgetsz __P((caddr_t)); 14760852Sdarrenrstatic int fr_natputent __P((caddr_t)); 14867614Sdarrenrstatic void nat_tabmove __P((nat_t *, u_int)); 14960852Sdarrenrstatic int nat_match __P((fr_info_t *, ipnat_t *, ip_t *)); 15060852Sdarrenrstatic hostmap_t *nat_hostmap __P((ipnat_t *, struct in_addr, 15160852Sdarrenr struct in_addr)); 15260852Sdarrenrstatic void nat_hostmapdel __P((struct hostmap *)); 15353642Sguido 15453642Sguido 15553642Sguidoint nat_init() 15653642Sguido{ 15753642Sguido KMALLOCS(nat_table[0], nat_t **, sizeof(nat_t *) * ipf_nattable_sz); 15853642Sguido if (nat_table[0] != NULL) 15953642Sguido bzero((char *)nat_table[0], ipf_nattable_sz * sizeof(nat_t *)); 16053642Sguido else 16153642Sguido return -1; 16253642Sguido 16353642Sguido KMALLOCS(nat_table[1], nat_t **, sizeof(nat_t *) * ipf_nattable_sz); 16453642Sguido if (nat_table[1] != NULL) 16553642Sguido bzero((char *)nat_table[1], ipf_nattable_sz * sizeof(nat_t *)); 16653642Sguido else 16753642Sguido return -1; 16853642Sguido 16953642Sguido KMALLOCS(nat_rules, ipnat_t **, sizeof(ipnat_t *) * ipf_natrules_sz); 17053642Sguido if (nat_rules != NULL) 17153642Sguido bzero((char *)nat_rules, ipf_natrules_sz * sizeof(ipnat_t *)); 17253642Sguido else 17353642Sguido return -1; 17453642Sguido 17553642Sguido KMALLOCS(rdr_rules, ipnat_t **, sizeof(ipnat_t *) * ipf_rdrrules_sz); 17653642Sguido if (rdr_rules != NULL) 17753642Sguido bzero((char *)rdr_rules, ipf_rdrrules_sz * sizeof(ipnat_t *)); 17853642Sguido else 17953642Sguido return -1; 18060852Sdarrenr 18160852Sdarrenr KMALLOCS(maptable, hostmap_t **, sizeof(hostmap_t *) * ipf_hostmap_sz); 18260852Sdarrenr if (maptable != NULL) 18360852Sdarrenr bzero((char *)maptable, sizeof(hostmap_t *) * ipf_hostmap_sz); 18460852Sdarrenr else 18560852Sdarrenr return -1; 18653642Sguido return 0; 18753642Sguido} 18853642Sguido 18953642Sguido 19060852Sdarrenrstatic void nat_addrdr(n) 19153642Sguidoipnat_t *n; 19253642Sguido{ 19360852Sdarrenr ipnat_t **np; 19460852Sdarrenr u_32_t j; 19553642Sguido u_int hv; 19660852Sdarrenr int k; 19753642Sguido 19860852Sdarrenr k = countbits(n->in_outmsk); 19960852Sdarrenr if ((k >= 0) && (k != 32)) 20060852Sdarrenr rdr_masks |= 1 << k; 20160852Sdarrenr j = (n->in_outip & n->in_outmsk); 20260852Sdarrenr hv = NAT_HASH_FN(j, 0, ipf_rdrrules_sz); 20360852Sdarrenr np = rdr_rules + hv; 20460852Sdarrenr while (*np != NULL) 20560852Sdarrenr np = &(*np)->in_rnext; 20660852Sdarrenr n->in_rnext = NULL; 20760852Sdarrenr n->in_prnext = np; 20860852Sdarrenr *np = n; 20953642Sguido} 21053642Sguido 21153642Sguido 21260852Sdarrenrstatic void nat_addnat(n) 21360852Sdarrenripnat_t *n; 21460852Sdarrenr{ 21560852Sdarrenr ipnat_t **np; 21660852Sdarrenr u_32_t j; 21760852Sdarrenr u_int hv; 21860852Sdarrenr int k; 21960852Sdarrenr 22060852Sdarrenr k = countbits(n->in_inmsk); 22160852Sdarrenr if ((k >= 0) && (k != 32)) 22260852Sdarrenr nat_masks |= 1 << k; 22360852Sdarrenr j = (n->in_inip & n->in_inmsk); 22460852Sdarrenr hv = NAT_HASH_FN(j, 0, ipf_natrules_sz); 22560852Sdarrenr np = nat_rules + hv; 22660852Sdarrenr while (*np != NULL) 22760852Sdarrenr np = &(*np)->in_mnext; 22860852Sdarrenr n->in_mnext = NULL; 22960852Sdarrenr n->in_pmnext = np; 23060852Sdarrenr *np = n; 23160852Sdarrenr} 23260852Sdarrenr 23360852Sdarrenr 23460852Sdarrenrstatic void nat_delrdr(n) 23560852Sdarrenripnat_t *n; 23660852Sdarrenr{ 23760852Sdarrenr if (n->in_rnext) 23860852Sdarrenr n->in_rnext->in_prnext = n->in_prnext; 23960852Sdarrenr *n->in_prnext = n->in_rnext; 24060852Sdarrenr} 24160852Sdarrenr 24260852Sdarrenr 24353642Sguidostatic void nat_delnat(n) 24453642Sguidoipnat_t *n; 24553642Sguido{ 24660852Sdarrenr if (n->in_mnext) 24760852Sdarrenr n->in_mnext->in_pmnext = n->in_pmnext; 24860852Sdarrenr *n->in_pmnext = n->in_mnext; 24960852Sdarrenr} 25060852Sdarrenr 25160852Sdarrenr 25260852Sdarrenr/* 25360852Sdarrenr * check if an ip address has already been allocated for a given mapping that 25460852Sdarrenr * is not doing port based translation. 25560852Sdarrenr */ 25660852Sdarrenrstatic struct hostmap *nat_hostmap(np, real, map) 25760852Sdarrenripnat_t *np; 25860852Sdarrenrstruct in_addr real; 25960852Sdarrenrstruct in_addr map; 26060852Sdarrenr{ 26160852Sdarrenr hostmap_t *hm; 26253642Sguido u_int hv; 26353642Sguido 26460852Sdarrenr MUTEX_ENTER(&ipf_hostmap); 26560852Sdarrenr hv = real.s_addr % HOSTMAP_SIZE; 26660852Sdarrenr for (hm = maptable[hv]; hm; hm = hm->hm_next) 26760852Sdarrenr if ((hm->hm_realip.s_addr == real.s_addr) && 26860852Sdarrenr (np == hm->hm_ipnat)) { 26960852Sdarrenr hm->hm_ref++; 27060852Sdarrenr MUTEX_EXIT(&ipf_hostmap); 27160852Sdarrenr return hm; 27260852Sdarrenr } 27360852Sdarrenr 27460852Sdarrenr KMALLOC(hm, hostmap_t *); 27560852Sdarrenr if (hm) { 27660852Sdarrenr hm->hm_next = maptable[hv]; 27760852Sdarrenr hm->hm_pnext = maptable + hv; 27860852Sdarrenr if (maptable[hv]) 27960852Sdarrenr maptable[hv]->hm_pnext = &hm->hm_next; 28060852Sdarrenr maptable[hv] = hm; 28160852Sdarrenr hm->hm_ipnat = np; 28260852Sdarrenr hm->hm_realip = real; 28360852Sdarrenr hm->hm_mapip = map; 28460852Sdarrenr hm->hm_ref = 1; 28560852Sdarrenr } 28660852Sdarrenr MUTEX_EXIT(&ipf_hostmap); 28760852Sdarrenr return hm; 28853642Sguido} 28953642Sguido 29053642Sguido 29160852Sdarrenrstatic void nat_hostmapdel(hm) 29260852Sdarrenrstruct hostmap *hm; 29360852Sdarrenr{ 29460852Sdarrenr MUTEX_ENTER(&ipf_hostmap); 29560852Sdarrenr ATOMIC_DEC32(hm->hm_ref); 29660852Sdarrenr if (hm->hm_ref == 0) { 29760852Sdarrenr if (hm->hm_next) 29860852Sdarrenr hm->hm_next->hm_pnext = hm->hm_pnext; 29960852Sdarrenr *hm->hm_pnext = hm->hm_next; 30060852Sdarrenr KFREE(hm); 30160852Sdarrenr } 30260852Sdarrenr MUTEX_EXIT(&ipf_hostmap); 30360852Sdarrenr} 30460852Sdarrenr 30560852Sdarrenr 30667614Sdarrenrvoid fix_outcksum(sp, n) 30753642Sguidou_short *sp; 30853642Sguidou_32_t n; 30953642Sguido{ 31053642Sguido register u_short sumshort; 31153642Sguido register u_32_t sum1; 31253642Sguido 31353642Sguido if (!n) 31453642Sguido return; 31555929Sguido#if SOLARIS2 >= 6 31655929Sguido else if (n & NAT_HW_CKSUM) { 31755929Sguido *sp = n & 0xffff; 31855929Sguido return; 31955929Sguido } 32055929Sguido#endif 32153642Sguido sum1 = (~ntohs(*sp)) & 0xffff; 32253642Sguido sum1 += (n); 32353642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 32453642Sguido /* Again */ 32553642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 32653642Sguido sumshort = ~(u_short)sum1; 32753642Sguido *(sp) = htons(sumshort); 32853642Sguido} 32953642Sguido 33053642Sguido 33167614Sdarrenrvoid fix_incksum(sp, n) 33253642Sguidou_short *sp; 33353642Sguidou_32_t n; 33453642Sguido{ 33553642Sguido register u_short sumshort; 33653642Sguido register u_32_t sum1; 33753642Sguido 33853642Sguido if (!n) 33953642Sguido return; 34055929Sguido#if SOLARIS2 >= 6 34155929Sguido else if (n & NAT_HW_CKSUM) { 34255929Sguido *sp = n & 0xffff; 34355929Sguido return; 34455929Sguido } 34555929Sguido#endif 34653642Sguido#ifdef sparc 34753642Sguido sum1 = (~(*sp)) & 0xffff; 34853642Sguido#else 34953642Sguido sum1 = (~ntohs(*sp)) & 0xffff; 35053642Sguido#endif 35153642Sguido sum1 += ~(n) & 0xffff; 35253642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 35353642Sguido /* Again */ 35453642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 35553642Sguido sumshort = ~(u_short)sum1; 35653642Sguido *(sp) = htons(sumshort); 35753642Sguido} 35853642Sguido 35953642Sguido 36053642Sguido/* 36167614Sdarrenr * fix_datacksum is used *only* for the adjustments of checksums in the data 36267614Sdarrenr * section of an IP packet. 36367614Sdarrenr * 36467614Sdarrenr * The only situation in which you need to do this is when NAT'ing an 36567614Sdarrenr * ICMP error message. Such a message, contains in its body the IP header 36667614Sdarrenr * of the original IP packet, that causes the error. 36767614Sdarrenr * 36867614Sdarrenr * You can't use fix_incksum or fix_outcksum in that case, because for the 36967614Sdarrenr * kernel the data section of the ICMP error is just data, and no special 37067614Sdarrenr * processing like hardware cksum or ntohs processing have been done by the 37167614Sdarrenr * kernel on the data section. 37267614Sdarrenr */ 37367614Sdarrenrvoid fix_datacksum(sp, n) 37467614Sdarrenru_short *sp; 37567614Sdarrenru_32_t n; 37667614Sdarrenr{ 37767614Sdarrenr register u_short sumshort; 37867614Sdarrenr register u_32_t sum1; 37967614Sdarrenr 38067614Sdarrenr if (!n) 38167614Sdarrenr return; 38267614Sdarrenr 38367614Sdarrenr sum1 = (~ntohs(*sp)) & 0xffff; 38467614Sdarrenr sum1 += (n); 38567614Sdarrenr sum1 = (sum1 >> 16) + (sum1 & 0xffff); 38667614Sdarrenr /* Again */ 38767614Sdarrenr sum1 = (sum1 >> 16) + (sum1 & 0xffff); 38867614Sdarrenr sumshort = ~(u_short)sum1; 38967614Sdarrenr *(sp) = htons(sumshort); 39067614Sdarrenr} 39167614Sdarrenr 39267614Sdarrenr/* 39353642Sguido * How the NAT is organised and works. 39453642Sguido * 39553642Sguido * Inside (interface y) NAT Outside (interface x) 39653642Sguido * -------------------- -+- ------------------------------------- 39753642Sguido * Packet going | out, processsed by ip_natout() for x 39853642Sguido * ------------> | ------------> 39953642Sguido * src=10.1.1.1 | src=192.1.1.1 40053642Sguido * | 40153642Sguido * | in, processed by ip_natin() for x 40253642Sguido * <------------ | <------------ 40353642Sguido * dst=10.1.1.1 | dst=192.1.1.1 40453642Sguido * -------------------- -+- ------------------------------------- 40553642Sguido * ip_natout() - changes ip_src and if required, sport 40653642Sguido * - creates a new mapping, if required. 40753642Sguido * ip_natin() - changes ip_dst and if required, dport 40853642Sguido * 40953642Sguido * In the NAT table, internal source is recorded as "in" and externally 41053642Sguido * seen as "out". 41153642Sguido */ 41253642Sguido 41353642Sguido/* 41453642Sguido * Handle ioctls which manipulate the NAT. 41553642Sguido */ 41653642Sguidoint nat_ioctl(data, cmd, mode) 41760852Sdarrenr#if defined(__NetBSD__) || defined(__OpenBSD__) || (__FreeBSD_version >= 300003) 41853642Sguidou_long cmd; 41953642Sguido#else 42053642Sguidoint cmd; 42153642Sguido#endif 42253642Sguidocaddr_t data; 42353642Sguidoint mode; 42453642Sguido{ 42553642Sguido register ipnat_t *nat, *nt, *n = NULL, **np = NULL; 42660852Sdarrenr int error = 0, ret, arg; 42753642Sguido ipnat_t natd; 42853642Sguido u_32_t i, j; 42953642Sguido 43053642Sguido#if (BSD >= 199306) && defined(_KERNEL) 43153642Sguido if ((securelevel >= 2) && (mode & FWRITE)) 43253642Sguido return EPERM; 43353642Sguido#endif 43453642Sguido 43553642Sguido nat = NULL; /* XXX gcc -Wuninitialized */ 43653642Sguido KMALLOC(nt, ipnat_t *); 43753642Sguido if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) 43860852Sdarrenr error = IRCOPYPTR(data, (char *)&natd, sizeof(natd)); 43964580Sdarrenr else if (cmd == SIOCIPFFL) { /* SIOCFLNAT & SIOCCNATL */ 44060852Sdarrenr error = IRCOPY(data, (char *)&arg, sizeof(arg)); 44164580Sdarrenr if (error) 44264580Sdarrenr error = EFAULT; 44364580Sdarrenr } 44453642Sguido 44560852Sdarrenr if (error) 44660852Sdarrenr goto done; 44760852Sdarrenr 44853642Sguido /* 44953642Sguido * For add/delete, look to see if the NAT entry is already present 45053642Sguido */ 45153642Sguido WRITE_ENTER(&ipf_nat); 45253642Sguido if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) { 45353642Sguido nat = &natd; 45453642Sguido nat->in_flags &= IPN_USERFLAGS; 45553642Sguido if ((nat->in_redir & NAT_MAPBLK) == 0) { 45660852Sdarrenr if ((nat->in_flags & IPN_SPLIT) == 0) 45760852Sdarrenr nat->in_inip &= nat->in_inmsk; 45860852Sdarrenr if ((nat->in_flags & IPN_IPRANGE) == 0) 45953642Sguido nat->in_outip &= nat->in_outmsk; 46053642Sguido } 46153642Sguido for (np = &nat_list; (n = *np); np = &n->in_next) 46253642Sguido if (!bcmp((char *)&nat->in_flags, (char *)&n->in_flags, 46353642Sguido IPN_CMPSIZ)) 46453642Sguido break; 46553642Sguido } 46653642Sguido 46753642Sguido switch (cmd) 46853642Sguido { 46955929Sguido#ifdef IPFILTER_LOG 47055929Sguido case SIOCIPFFB : 47160852Sdarrenr { 47260852Sdarrenr int tmp; 47360852Sdarrenr 47455929Sguido if (!(mode & FWRITE)) 47555929Sguido error = EPERM; 47660852Sdarrenr else { 47760852Sdarrenr tmp = ipflog_clear(IPL_LOGNAT); 47860852Sdarrenr IWCOPY((char *)&tmp, (char *)data, sizeof(tmp)); 47960852Sdarrenr } 48055929Sguido break; 48160852Sdarrenr } 48255929Sguido#endif 48353642Sguido case SIOCADNAT : 48453642Sguido if (!(mode & FWRITE)) { 48553642Sguido error = EPERM; 48653642Sguido break; 48753642Sguido } 48853642Sguido if (n) { 48953642Sguido error = EEXIST; 49053642Sguido break; 49153642Sguido } 49253642Sguido if (nt == NULL) { 49353642Sguido error = ENOMEM; 49453642Sguido break; 49553642Sguido } 49653642Sguido n = nt; 49753642Sguido nt = NULL; 49853642Sguido bcopy((char *)nat, (char *)n, sizeof(*n)); 49960852Sdarrenr n->in_ifp = (void *)GETUNIT(n->in_ifname, 4); 50053642Sguido if (!n->in_ifp) 50153642Sguido n->in_ifp = (void *)-1; 50253642Sguido if (n->in_plabel[0] != '\0') { 50353642Sguido n->in_apr = appr_match(n->in_p, n->in_plabel); 50453642Sguido if (!n->in_apr) { 50553642Sguido error = ENOENT; 50653642Sguido break; 50753642Sguido } 50853642Sguido } 50953642Sguido n->in_next = NULL; 51053642Sguido *np = n; 51153642Sguido 51263523Sdarrenr if (n->in_redir & NAT_REDIRECT) { 51363523Sdarrenr n->in_flags &= ~IPN_NOTDST; 51460852Sdarrenr nat_addrdr(n); 51563523Sdarrenr } 51663523Sdarrenr if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { 51763523Sdarrenr n->in_flags &= ~IPN_NOTSRC; 51860852Sdarrenr nat_addnat(n); 51963523Sdarrenr } 52053642Sguido 52153642Sguido n->in_use = 0; 52253642Sguido if (n->in_redir & NAT_MAPBLK) 52353642Sguido n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk); 52453642Sguido else if (n->in_flags & IPN_AUTOPORTMAP) 52553642Sguido n->in_space = USABLE_PORTS * ~ntohl(n->in_inmsk); 52660852Sdarrenr else if (n->in_flags & IPN_IPRANGE) 52753642Sguido n->in_space = ntohl(n->in_outmsk) - ntohl(n->in_outip); 52860852Sdarrenr else if (n->in_flags & IPN_SPLIT) 52960852Sdarrenr n->in_space = 2; 53053642Sguido else 53153642Sguido n->in_space = ~ntohl(n->in_outmsk); 53253642Sguido /* 53353642Sguido * Calculate the number of valid IP addresses in the output 53453642Sguido * mapping range. In all cases, the range is inclusive of 53553642Sguido * the start and ending IP addresses. 53653642Sguido * If to a CIDR address, lose 2: broadcast + network address 53764580Sdarrenr * (so subtract 1) 53853642Sguido * If to a range, add one. 53953642Sguido * If to a single IP address, set to 1. 54053642Sguido */ 54153642Sguido if (n->in_space) { 54260852Sdarrenr if ((n->in_flags & IPN_IPRANGE) != 0) 54353642Sguido n->in_space += 1; 54453642Sguido else 54553642Sguido n->in_space -= 1; 54653642Sguido } else 54753642Sguido n->in_space = 1; 54853642Sguido if ((n->in_outmsk != 0xffffffff) && (n->in_outmsk != 0) && 54960852Sdarrenr ((n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0)) 55053642Sguido n->in_nip = ntohl(n->in_outip) + 1; 55160852Sdarrenr else if ((n->in_flags & IPN_SPLIT) && 55260852Sdarrenr (n->in_redir & NAT_REDIRECT)) 55360852Sdarrenr n->in_nip = ntohl(n->in_inip); 55453642Sguido else 55553642Sguido n->in_nip = ntohl(n->in_outip); 55653642Sguido if (n->in_redir & NAT_MAP) { 55753642Sguido n->in_pnext = ntohs(n->in_pmin); 55853642Sguido /* 55953642Sguido * Multiply by the number of ports made available. 56053642Sguido */ 56153642Sguido if (ntohs(n->in_pmax) >= ntohs(n->in_pmin)) { 56253642Sguido n->in_space *= (ntohs(n->in_pmax) - 56353642Sguido ntohs(n->in_pmin) + 1); 56453642Sguido /* 56553642Sguido * Because two different sources can map to 56653642Sguido * different destinations but use the same 56753642Sguido * local IP#/port #. 56853642Sguido * If the result is smaller than in_space, then 56953642Sguido * we may have wrapped around 32bits. 57053642Sguido */ 57153642Sguido i = n->in_inmsk; 57253642Sguido if ((i != 0) && (i != 0xffffffff)) { 57353642Sguido j = n->in_space * (~ntohl(i) + 1); 57453642Sguido if (j >= n->in_space) 57553642Sguido n->in_space = j; 57653642Sguido else 57753642Sguido n->in_space = 0xffffffff; 57853642Sguido } 57953642Sguido } 58053642Sguido /* 58153642Sguido * If no protocol is specified, multiple by 256. 58253642Sguido */ 58353642Sguido if ((n->in_flags & IPN_TCPUDP) == 0) { 58453642Sguido j = n->in_space * 256; 58553642Sguido if (j >= n->in_space) 58653642Sguido n->in_space = j; 58753642Sguido else 58853642Sguido n->in_space = 0xffffffff; 58953642Sguido } 59053642Sguido } 59153642Sguido /* Otherwise, these fields are preset */ 59253642Sguido n = NULL; 59353642Sguido nat_stats.ns_rules++; 59453642Sguido break; 59553642Sguido case SIOCRMNAT : 59653642Sguido if (!(mode & FWRITE)) { 59753642Sguido error = EPERM; 59853642Sguido n = NULL; 59953642Sguido break; 60053642Sguido } 60153642Sguido if (!n) { 60253642Sguido error = ESRCH; 60353642Sguido break; 60453642Sguido } 60553642Sguido if (n->in_redir & NAT_REDIRECT) 60653642Sguido nat_delrdr(n); 60753642Sguido if (n->in_redir & (NAT_MAPBLK|NAT_MAP)) 60853642Sguido nat_delnat(n); 60953642Sguido if (nat_list == NULL) { 61053642Sguido nat_masks = 0; 61153642Sguido rdr_masks = 0; 61253642Sguido } 61353642Sguido *np = n->in_next; 61453642Sguido if (!n->in_use) { 61553642Sguido if (n->in_apr) 61653642Sguido appr_free(n->in_apr); 61753642Sguido KFREE(n); 61853642Sguido nat_stats.ns_rules--; 61953642Sguido } else { 62053642Sguido n->in_flags |= IPN_DELETE; 62153642Sguido n->in_next = NULL; 62253642Sguido } 62353642Sguido n = NULL; 62453642Sguido break; 62553642Sguido case SIOCGNATS : 62653642Sguido MUTEX_DOWNGRADE(&ipf_nat); 62753642Sguido nat_stats.ns_table[0] = nat_table[0]; 62853642Sguido nat_stats.ns_table[1] = nat_table[1]; 62953642Sguido nat_stats.ns_list = nat_list; 63053642Sguido nat_stats.ns_nattab_sz = ipf_nattable_sz; 63153642Sguido nat_stats.ns_rultab_sz = ipf_natrules_sz; 63253642Sguido nat_stats.ns_rdrtab_sz = ipf_rdrrules_sz; 63353642Sguido nat_stats.ns_instances = nat_instances; 63453642Sguido nat_stats.ns_apslist = ap_sess_list; 63560852Sdarrenr error = IWCOPYPTR((char *)&nat_stats, (char *)data, 63660852Sdarrenr sizeof(nat_stats)); 63753642Sguido break; 63853642Sguido case SIOCGNATL : 63953642Sguido { 64053642Sguido natlookup_t nl; 64153642Sguido 64253642Sguido MUTEX_DOWNGRADE(&ipf_nat); 64360852Sdarrenr error = IRCOPYPTR((char *)data, (char *)&nl, sizeof(nl)); 64460852Sdarrenr if (error) 64560852Sdarrenr break; 64653642Sguido 64753642Sguido if (nat_lookupredir(&nl)) { 64860852Sdarrenr error = IWCOPYPTR((char *)&nl, (char *)data, 64960852Sdarrenr sizeof(nl)); 65053642Sguido } else 65153642Sguido error = ESRCH; 65253642Sguido break; 65353642Sguido } 65460852Sdarrenr case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */ 65553642Sguido if (!(mode & FWRITE)) { 65653642Sguido error = EPERM; 65753642Sguido break; 65853642Sguido } 65960852Sdarrenr error = 0; 66060852Sdarrenr if (arg == 0) 66160852Sdarrenr ret = nat_flushtable(); 66260852Sdarrenr else if (arg == 1) 66360852Sdarrenr ret = nat_clearlist(); 66460852Sdarrenr else 66560852Sdarrenr error = EINVAL; 66653642Sguido MUTEX_DOWNGRADE(&ipf_nat); 66760852Sdarrenr if (!error) { 66860852Sdarrenr error = IWCOPY((caddr_t)&ret, data, sizeof(ret)); 66960852Sdarrenr if (error) 67060852Sdarrenr error = EFAULT; 67160852Sdarrenr } 67253642Sguido break; 67360852Sdarrenr case SIOCSTLCK : 67460852Sdarrenr error = IRCOPY(data, (caddr_t)&arg, sizeof(arg)); 67560852Sdarrenr if (!error) { 67660852Sdarrenr error = IWCOPY((caddr_t)&fr_nat_lock, data, 67760852Sdarrenr sizeof(fr_nat_lock)); 67860852Sdarrenr if (!error) 67960852Sdarrenr fr_nat_lock = arg; 68064580Sdarrenr } else 68164580Sdarrenr error = EFAULT; 68253642Sguido break; 68360852Sdarrenr case SIOCSTPUT : 68460852Sdarrenr if (fr_nat_lock) 68560852Sdarrenr error = fr_natputent(data); 68660852Sdarrenr else 68760852Sdarrenr error = EACCES; 68860852Sdarrenr break; 68960852Sdarrenr case SIOCSTGSZ : 69060852Sdarrenr if (fr_nat_lock) 69160852Sdarrenr error = fr_natgetsz(data); 69260852Sdarrenr else 69360852Sdarrenr error = EACCES; 69460852Sdarrenr break; 69560852Sdarrenr case SIOCSTGET : 69660852Sdarrenr if (fr_nat_lock) 69760852Sdarrenr error = fr_natgetent(data); 69860852Sdarrenr else 69960852Sdarrenr error = EACCES; 70060852Sdarrenr break; 70153642Sguido case FIONREAD : 70253642Sguido#ifdef IPFILTER_LOG 70353642Sguido MUTEX_DOWNGRADE(&ipf_nat); 70460852Sdarrenr error = IWCOPY((caddr_t)&iplused[IPL_LOGNAT], (caddr_t)data, 70560852Sdarrenr sizeof(iplused[IPL_LOGNAT])); 70664580Sdarrenr if (error) 70764580Sdarrenr error = EFAULT; 70853642Sguido#endif 70953642Sguido break; 71053642Sguido default : 71153642Sguido error = EINVAL; 71253642Sguido break; 71353642Sguido } 71453642Sguido RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ 71560852Sdarrenrdone: 71653642Sguido if (nt) 71753642Sguido KFREE(nt); 71853642Sguido return error; 71953642Sguido} 72053642Sguido 72153642Sguido 72260852Sdarrenrstatic int fr_natgetsz(data) 72360852Sdarrenrcaddr_t data; 72460852Sdarrenr{ 72560852Sdarrenr ap_session_t *aps; 72660852Sdarrenr nat_t *nat, *n; 72760852Sdarrenr int error = 0; 72860852Sdarrenr natget_t ng; 72960852Sdarrenr 73060852Sdarrenr error = IRCOPY(data, (caddr_t)&ng, sizeof(ng)); 73160852Sdarrenr if (error) 73260852Sdarrenr return EFAULT; 73360852Sdarrenr 73460852Sdarrenr nat = ng.ng_ptr; 73560852Sdarrenr if (!nat) { 73660852Sdarrenr nat = nat_instances; 73760852Sdarrenr ng.ng_sz = 0; 73860852Sdarrenr if (nat == NULL) { 73960852Sdarrenr error = IWCOPY((caddr_t)&ng, data, sizeof(ng)); 74060852Sdarrenr if (error) 74160852Sdarrenr error = EFAULT; 74260852Sdarrenr return error; 74360852Sdarrenr } 74460852Sdarrenr } else { 74560852Sdarrenr /* 74660852Sdarrenr * Make sure the pointer we're copying from exists in the 74760852Sdarrenr * current list of entries. Security precaution to prevent 74860852Sdarrenr * copying of random kernel data. 74960852Sdarrenr */ 75060852Sdarrenr for (n = nat_instances; n; n = n->nat_next) 75160852Sdarrenr if (n == nat) 75260852Sdarrenr break; 75360852Sdarrenr if (!n) 75460852Sdarrenr return ESRCH; 75560852Sdarrenr } 75660852Sdarrenr 75760852Sdarrenr ng.ng_sz = sizeof(nat_save_t); 75860852Sdarrenr aps = nat->nat_aps; 75960852Sdarrenr if ((aps != NULL) && (aps->aps_data != 0)) { 76060852Sdarrenr ng.ng_sz += sizeof(ap_session_t); 76160852Sdarrenr ng.ng_sz += aps->aps_psiz; 76260852Sdarrenr } 76360852Sdarrenr 76460852Sdarrenr error = IWCOPY((caddr_t)&ng, data, sizeof(ng)); 76560852Sdarrenr if (error) 76660852Sdarrenr error = EFAULT; 76760852Sdarrenr return error; 76860852Sdarrenr} 76960852Sdarrenr 77060852Sdarrenr 77160852Sdarrenrstatic int fr_natgetent(data) 77260852Sdarrenrcaddr_t data; 77360852Sdarrenr{ 77464580Sdarrenr nat_save_t ipn, *ipnp, *ipnn = NULL; 77560852Sdarrenr register nat_t *n, *nat; 77660852Sdarrenr ap_session_t *aps; 77760852Sdarrenr int error; 77860852Sdarrenr 77960852Sdarrenr error = IRCOPY(data, (caddr_t)&ipnp, sizeof(ipnp)); 78060852Sdarrenr if (error) 78160852Sdarrenr return EFAULT; 78260852Sdarrenr error = IRCOPY((caddr_t)ipnp, (caddr_t)&ipn, sizeof(ipn)); 78360852Sdarrenr if (error) 78460852Sdarrenr return EFAULT; 78560852Sdarrenr 78660852Sdarrenr nat = ipn.ipn_next; 78760852Sdarrenr if (!nat) { 78860852Sdarrenr nat = nat_instances; 78960852Sdarrenr if (nat == NULL) { 79060852Sdarrenr if (nat_instances == NULL) 79160852Sdarrenr return ENOENT; 79260852Sdarrenr return 0; 79360852Sdarrenr } 79460852Sdarrenr } else { 79560852Sdarrenr /* 79660852Sdarrenr * Make sure the pointer we're copying from exists in the 79760852Sdarrenr * current list of entries. Security precaution to prevent 79860852Sdarrenr * copying of random kernel data. 79960852Sdarrenr */ 80060852Sdarrenr for (n = nat_instances; n; n = n->nat_next) 80160852Sdarrenr if (n == nat) 80260852Sdarrenr break; 80360852Sdarrenr if (!n) 80460852Sdarrenr return ESRCH; 80560852Sdarrenr } 80660852Sdarrenr 80760852Sdarrenr ipn.ipn_next = nat->nat_next; 80860852Sdarrenr ipn.ipn_dsize = 0; 80960852Sdarrenr bcopy((char *)nat, (char *)&ipn.ipn_nat, sizeof(ipn.ipn_nat)); 81060852Sdarrenr ipn.ipn_nat.nat_data = NULL; 81160852Sdarrenr 81260852Sdarrenr if (nat->nat_ptr) { 81360852Sdarrenr bcopy((char *)nat->nat_ptr, (char *)&ipn.ipn_ipnat, 81460852Sdarrenr sizeof(ipn.ipn_ipnat)); 81560852Sdarrenr } 81660852Sdarrenr 81760852Sdarrenr if (nat->nat_fr) 81860852Sdarrenr bcopy((char *)nat->nat_fr, (char *)&ipn.ipn_rule, 81960852Sdarrenr sizeof(ipn.ipn_rule)); 82060852Sdarrenr 82160852Sdarrenr if ((aps = nat->nat_aps)) { 82260852Sdarrenr ipn.ipn_dsize = sizeof(*aps); 82360852Sdarrenr if (aps->aps_data) 82460852Sdarrenr ipn.ipn_dsize += aps->aps_psiz; 82560852Sdarrenr KMALLOCS(ipnn, nat_save_t *, sizeof(*ipnn) + ipn.ipn_dsize); 82660852Sdarrenr if (ipnn == NULL) 82764580Sdarrenr return ENOMEM; 82860852Sdarrenr bcopy((char *)&ipn, (char *)ipnn, sizeof(ipn)); 82960852Sdarrenr 83064580Sdarrenr bcopy((char *)aps, ipnn->ipn_data, sizeof(*aps)); 83160852Sdarrenr if (aps->aps_data) { 83264580Sdarrenr bcopy(aps->aps_data, ipnn->ipn_data + sizeof(*aps), 83360852Sdarrenr aps->aps_psiz); 83464580Sdarrenr ipnn->ipn_dsize += aps->aps_psiz; 83560852Sdarrenr } 83660852Sdarrenr error = IWCOPY((caddr_t)ipnn, ipnp, 83760852Sdarrenr sizeof(ipn) + ipn.ipn_dsize); 83860852Sdarrenr if (error) 83964580Sdarrenr error = EFAULT; 84060852Sdarrenr KFREES(ipnn, sizeof(*ipnn) + ipn.ipn_dsize); 84160852Sdarrenr } else { 84260852Sdarrenr error = IWCOPY((caddr_t)&ipn, ipnp, sizeof(ipn)); 84360852Sdarrenr if (error) 84464580Sdarrenr error = EFAULT; 84560852Sdarrenr } 84664580Sdarrenr return error; 84760852Sdarrenr} 84860852Sdarrenr 84960852Sdarrenr 85060852Sdarrenrstatic int fr_natputent(data) 85160852Sdarrenrcaddr_t data; 85260852Sdarrenr{ 85364580Sdarrenr nat_save_t ipn, *ipnp, *ipnn = NULL; 85460852Sdarrenr register nat_t *n, *nat; 85560852Sdarrenr ap_session_t *aps; 85660852Sdarrenr frentry_t *fr; 85760852Sdarrenr ipnat_t *in; 85860852Sdarrenr 85960852Sdarrenr int error; 86060852Sdarrenr 86160852Sdarrenr error = IRCOPY(data, (caddr_t)&ipnp, sizeof(ipnp)); 86260852Sdarrenr if (error) 86360852Sdarrenr return EFAULT; 86460852Sdarrenr error = IRCOPY((caddr_t)ipnp, (caddr_t)&ipn, sizeof(ipn)); 86560852Sdarrenr if (error) 86660852Sdarrenr return EFAULT; 86764580Sdarrenr nat = NULL; 86860852Sdarrenr if (ipn.ipn_dsize) { 86960852Sdarrenr KMALLOCS(ipnn, nat_save_t *, sizeof(ipn) + ipn.ipn_dsize); 87060852Sdarrenr if (ipnn == NULL) 87160852Sdarrenr return ENOMEM; 87260852Sdarrenr bcopy((char *)&ipn, (char *)ipnn, sizeof(ipn)); 87360852Sdarrenr error = IRCOPY((caddr_t)ipnp, (caddr_t)ipn.ipn_data, 87460852Sdarrenr ipn.ipn_dsize); 87564580Sdarrenr if (error) { 87664580Sdarrenr error = EFAULT; 87764580Sdarrenr goto junkput; 87864580Sdarrenr } 87960852Sdarrenr } else 88060852Sdarrenr ipnn = NULL; 88160852Sdarrenr 88260852Sdarrenr KMALLOC(nat, nat_t *); 88364580Sdarrenr if (nat == NULL) { 88464580Sdarrenr error = EFAULT; 88564580Sdarrenr goto junkput; 88664580Sdarrenr } 88760852Sdarrenr 88860852Sdarrenr bcopy((char *)&ipn.ipn_nat, (char *)nat, sizeof(*nat)); 88960852Sdarrenr /* 89060852Sdarrenr * Initialize all these so that nat_delete() doesn't cause a crash. 89160852Sdarrenr */ 89267614Sdarrenr nat->nat_phnext[0] = NULL; 89367614Sdarrenr nat->nat_phnext[1] = NULL; 89460852Sdarrenr fr = nat->nat_fr; 89560852Sdarrenr nat->nat_fr = NULL; 89660852Sdarrenr aps = nat->nat_aps; 89760852Sdarrenr nat->nat_aps = NULL; 89860852Sdarrenr in = nat->nat_ptr; 89960852Sdarrenr nat->nat_ptr = NULL; 90060852Sdarrenr nat->nat_data = NULL; 90160852Sdarrenr 90260852Sdarrenr /* 90360852Sdarrenr * Restore the rule associated with this nat session 90460852Sdarrenr */ 90560852Sdarrenr if (in) { 90660852Sdarrenr KMALLOC(in, ipnat_t *); 90760852Sdarrenr if (in == NULL) { 90860852Sdarrenr error = ENOMEM; 90960852Sdarrenr goto junkput; 91060852Sdarrenr } 91160852Sdarrenr nat->nat_ptr = in; 91260852Sdarrenr bcopy((char *)&ipn.ipn_ipnat, (char *)in, sizeof(*in)); 91360852Sdarrenr in->in_use = 1; 91460852Sdarrenr in->in_flags |= IPN_DELETE; 91560852Sdarrenr in->in_next = NULL; 91660852Sdarrenr in->in_rnext = NULL; 91760852Sdarrenr in->in_prnext = NULL; 91860852Sdarrenr in->in_mnext = NULL; 91960852Sdarrenr in->in_pmnext = NULL; 92060852Sdarrenr in->in_ifp = GETUNIT(in->in_ifname, 4); 92160852Sdarrenr if (in->in_plabel[0] != '\0') { 92260852Sdarrenr in->in_apr = appr_match(in->in_p, in->in_plabel); 92360852Sdarrenr } 92460852Sdarrenr } 92560852Sdarrenr 92660852Sdarrenr /* 92760852Sdarrenr * Restore ap_session_t structure. Include the private data allocated 92860852Sdarrenr * if it was there. 92960852Sdarrenr */ 93060852Sdarrenr if (aps) { 93160852Sdarrenr KMALLOC(aps, ap_session_t *); 93260852Sdarrenr if (aps == NULL) { 93360852Sdarrenr error = ENOMEM; 93460852Sdarrenr goto junkput; 93560852Sdarrenr } 93660852Sdarrenr nat->nat_aps = aps; 93760852Sdarrenr aps->aps_next = ap_sess_list; 93860852Sdarrenr ap_sess_list = aps; 93960852Sdarrenr bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps)); 94060852Sdarrenr if (in) 94160852Sdarrenr aps->aps_apr = in->in_apr; 94260852Sdarrenr if (aps->aps_psiz) { 94360852Sdarrenr KMALLOCS(aps->aps_data, void *, aps->aps_psiz); 94460852Sdarrenr if (aps->aps_data == NULL) { 94560852Sdarrenr error = ENOMEM; 94660852Sdarrenr goto junkput; 94760852Sdarrenr } 94860852Sdarrenr bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data, 94960852Sdarrenr aps->aps_psiz); 95060852Sdarrenr } else { 95160852Sdarrenr aps->aps_psiz = 0; 95260852Sdarrenr aps->aps_data = NULL; 95360852Sdarrenr } 95460852Sdarrenr } 95560852Sdarrenr 95660852Sdarrenr /* 95760852Sdarrenr * If there was a filtering rule associated with this entry then 95860852Sdarrenr * build up a new one. 95960852Sdarrenr */ 96060852Sdarrenr if (fr != NULL) { 96160852Sdarrenr if (nat->nat_flags & FI_NEWFR) { 96260852Sdarrenr KMALLOC(fr, frentry_t *); 96360852Sdarrenr nat->nat_fr = fr; 96460852Sdarrenr if (fr == NULL) { 96560852Sdarrenr error = ENOMEM; 96660852Sdarrenr goto junkput; 96760852Sdarrenr } 96860852Sdarrenr bcopy((char *)&ipn.ipn_fr, (char *)fr, sizeof(*fr)); 96960852Sdarrenr ipn.ipn_nat.nat_fr = fr; 97060852Sdarrenr error = IWCOPY((caddr_t)&ipn, ipnp, sizeof(ipn)); 97160852Sdarrenr if (error) { 97260852Sdarrenr error = EFAULT; 97360852Sdarrenr goto junkput; 97460852Sdarrenr } 97560852Sdarrenr } else { 97660852Sdarrenr for (n = nat_instances; n; n = n->nat_next) 97760852Sdarrenr if (n->nat_fr == fr) 97860852Sdarrenr break; 97960852Sdarrenr if (!n) { 98060852Sdarrenr error = ESRCH; 98160852Sdarrenr goto junkput; 98260852Sdarrenr } 98360852Sdarrenr } 98460852Sdarrenr } 98560852Sdarrenr 98660852Sdarrenr if (ipnn) 98760852Sdarrenr KFREES(ipnn, sizeof(ipn) + ipn.ipn_dsize); 98860852Sdarrenr nat_insert(nat); 98960852Sdarrenr return 0; 99060852Sdarrenrjunkput: 99160852Sdarrenr if (ipnn) 99260852Sdarrenr KFREES(ipnn, sizeof(ipn) + ipn.ipn_dsize); 99360852Sdarrenr if (nat) 99460852Sdarrenr nat_delete(nat); 99560852Sdarrenr return error; 99660852Sdarrenr} 99760852Sdarrenr 99860852Sdarrenr 99953642Sguido/* 100053642Sguido * Delete a nat entry from the various lists and table. 100153642Sguido */ 100253642Sguidostatic void nat_delete(natd) 100353642Sguidostruct nat *natd; 100453642Sguido{ 100553642Sguido struct ipnat *ipn; 100653642Sguido 100767614Sdarrenr if (natd->nat_flags & FI_WILDP) 100867614Sdarrenr nat_wilds--; 100967614Sdarrenr if (natd->nat_hnext[0]) 101067614Sdarrenr natd->nat_hnext[0]->nat_phnext[0] = natd->nat_phnext[0]; 101167614Sdarrenr *natd->nat_phnext[0] = natd->nat_hnext[0]; 101267614Sdarrenr if (natd->nat_hnext[1]) 101367614Sdarrenr natd->nat_hnext[1]->nat_phnext[1] = natd->nat_phnext[1]; 101467614Sdarrenr *natd->nat_phnext[1] = natd->nat_hnext[1]; 101553642Sguido 101653642Sguido if (natd->nat_fr != NULL) { 101760852Sdarrenr ATOMIC_DEC32(natd->nat_fr->fr_ref); 101853642Sguido } 101960852Sdarrenr 102060852Sdarrenr if (natd->nat_hm != NULL) 102160852Sdarrenr nat_hostmapdel(natd->nat_hm); 102260852Sdarrenr 102353642Sguido /* 102453642Sguido * If there is an active reference from the nat entry to its parent 102553642Sguido * rule, decrement the rule's reference count and free it too if no 102653642Sguido * longer being used. 102753642Sguido */ 102853642Sguido ipn = natd->nat_ptr; 102953642Sguido if (ipn != NULL) { 103053642Sguido ipn->in_space++; 103153642Sguido ipn->in_use--; 103253642Sguido if (!ipn->in_use && (ipn->in_flags & IPN_DELETE)) { 103353642Sguido if (ipn->in_apr) 103453642Sguido appr_free(ipn->in_apr); 103553642Sguido KFREE(ipn); 103653642Sguido nat_stats.ns_rules--; 103753642Sguido } 103853642Sguido } 103953642Sguido 104060852Sdarrenr MUTEX_DESTROY(&natd->nat_lock); 104153642Sguido /* 104253642Sguido * If there's a fragment table entry too for this nat entry, then 104353642Sguido * dereference that as well. 104453642Sguido */ 104553642Sguido ipfr_forget((void *)natd); 104653642Sguido aps_free(natd->nat_aps); 104753642Sguido nat_stats.ns_inuse--; 104853642Sguido KFREE(natd); 104953642Sguido} 105053642Sguido 105153642Sguido 105253642Sguido/* 105353642Sguido * nat_flushtable - clear the NAT table of all mapping entries. 105453642Sguido */ 105553642Sguidostatic int nat_flushtable() 105653642Sguido{ 105753642Sguido register nat_t *nat, **natp; 105853642Sguido register int j = 0; 105967614Sdarrenr 106053642Sguido /* 106153642Sguido * ALL NAT mappings deleted, so lets just make the deletions 106253642Sguido * quicker. 106353642Sguido */ 106453642Sguido if (nat_table[0] != NULL) 106553642Sguido bzero((char *)nat_table[0], 106653642Sguido sizeof(nat_table[0]) * ipf_nattable_sz); 106753642Sguido if (nat_table[1] != NULL) 106853642Sguido bzero((char *)nat_table[1], 106953642Sguido sizeof(nat_table[1]) * ipf_nattable_sz); 107053642Sguido 107153642Sguido for (natp = &nat_instances; (nat = *natp); ) { 107253642Sguido *natp = nat->nat_next; 107353642Sguido nat_delete(nat); 107453642Sguido j++; 107553642Sguido } 107653642Sguido nat_stats.ns_inuse = 0; 107753642Sguido return j; 107853642Sguido} 107953642Sguido 108053642Sguido 108153642Sguido/* 108253642Sguido * nat_clearlist - delete all rules in the active NAT mapping list. 108353642Sguido */ 108453642Sguidostatic int nat_clearlist() 108553642Sguido{ 108653642Sguido register ipnat_t *n, **np = &nat_list; 108753642Sguido int i = 0; 108853642Sguido 108953642Sguido if (nat_rules != NULL) 109053642Sguido bzero((char *)nat_rules, sizeof(*nat_rules) * ipf_natrules_sz); 109153642Sguido if (rdr_rules != NULL) 109253642Sguido bzero((char *)rdr_rules, sizeof(*rdr_rules) * ipf_rdrrules_sz); 109353642Sguido 109453642Sguido while ((n = *np)) { 109553642Sguido *np = n->in_next; 109653642Sguido if (!n->in_use) { 109753642Sguido if (n->in_apr) 109853642Sguido appr_free(n->in_apr); 109953642Sguido KFREE(n); 110053642Sguido nat_stats.ns_rules--; 110153642Sguido } else { 110253642Sguido n->in_flags |= IPN_DELETE; 110353642Sguido n->in_next = NULL; 110453642Sguido } 110553642Sguido i++; 110653642Sguido } 110753642Sguido nat_masks = 0; 110853642Sguido rdr_masks = 0; 110953642Sguido return i; 111053642Sguido} 111153642Sguido 111253642Sguido 111353642Sguido/* 111453642Sguido * Create a new NAT table entry. 111553642Sguido * NOTE: assumes write lock on ipf_nat has been obtained already. 111653642Sguido */ 111753642Sguidonat_t *nat_new(np, ip, fin, flags, direction) 111853642Sguidoipnat_t *np; 111953642Sguidoip_t *ip; 112053642Sguidofr_info_t *fin; 112153642Sguidou_int flags; 112253642Sguidoint direction; 112353642Sguido{ 112453642Sguido register u_32_t sum1, sum2, sumd, l; 112553642Sguido u_short port = 0, sport = 0, dport = 0, nport = 0; 112653642Sguido struct in_addr in, inb; 112753642Sguido tcphdr_t *tcp = NULL; 112860852Sdarrenr hostmap_t *hm = NULL; 112960852Sdarrenr nat_t *nat, *natl; 113053642Sguido u_short nflags; 113155929Sguido#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) 113255929Sguido qif_t *qf = fin->fin_qif; 113355929Sguido#endif 113453642Sguido 113553642Sguido nflags = flags & np->in_flags; 113653642Sguido if (flags & IPN_TCPUDP) { 113753642Sguido tcp = (tcphdr_t *)fin->fin_dp; 113853642Sguido sport = tcp->th_sport; 113953642Sguido dport = tcp->th_dport; 114053642Sguido } 114153642Sguido 114253642Sguido /* Give me a new nat */ 114353642Sguido KMALLOC(nat, nat_t *); 114460852Sdarrenr if (nat == NULL) { 114560852Sdarrenr nat_stats.ns_memfail++; 114653642Sguido return NULL; 114760852Sdarrenr } 114853642Sguido 114953642Sguido bzero((char *)nat, sizeof(*nat)); 115053642Sguido nat->nat_flags = flags; 115167614Sdarrenr if (flags & FI_WILDP) 115267614Sdarrenr nat_wilds++; 115353642Sguido /* 115453642Sguido * Search the current table for a match. 115553642Sguido */ 115653642Sguido if (direction == NAT_OUTBOUND) { 115753642Sguido /* 115853642Sguido * Values at which the search for a free resouce starts. 115953642Sguido */ 116053642Sguido u_32_t st_ip; 116153642Sguido u_short st_port; 116253642Sguido 116353642Sguido /* 116453642Sguido * If it's an outbound packet which doesn't match any existing 116553642Sguido * record, then create a new port 116653642Sguido */ 116753642Sguido l = 0; 116853642Sguido st_ip = np->in_nip; 116953642Sguido st_port = np->in_pnext; 117053642Sguido 117153642Sguido do { 117253642Sguido port = 0; 117360852Sdarrenr in.s_addr = htonl(np->in_nip); 117453642Sguido if (l == 0) { 117557096Sguido /* 117657096Sguido * Check to see if there is an existing NAT 117757096Sguido * setup for this IP address pair. 117857096Sguido */ 117960852Sdarrenr hm = nat_hostmap(np, ip->ip_src, in); 118060852Sdarrenr if (hm != NULL) 118160852Sdarrenr in.s_addr = hm->hm_mapip.s_addr; 118260852Sdarrenr } else if ((l == 1) && (hm != NULL)) { 118360852Sdarrenr nat_hostmapdel(hm); 118460852Sdarrenr hm = NULL; 118553642Sguido } 118660852Sdarrenr in.s_addr = ntohl(in.s_addr); 118753642Sguido 118860852Sdarrenr nat->nat_hm = hm; 118960852Sdarrenr 119053642Sguido if ((np->in_outmsk == 0xffffffff) && 119153642Sguido (np->in_pnext == 0)) { 119260852Sdarrenr if (l > 0) 119360852Sdarrenr goto badnat; 119453642Sguido } 119553642Sguido 119653642Sguido if (np->in_redir & NAT_MAPBLK) { 119753642Sguido if ((l >= np->in_ppip) || ((l > 0) && 119860852Sdarrenr !(flags & IPN_TCPUDP))) 119960852Sdarrenr goto badnat; 120053642Sguido /* 120153642Sguido * map-block - Calculate destination address. 120253642Sguido */ 120353642Sguido in.s_addr = ntohl(ip->ip_src.s_addr); 120453642Sguido in.s_addr &= ntohl(~np->in_inmsk); 120553642Sguido inb.s_addr = in.s_addr; 120653642Sguido in.s_addr /= np->in_ippip; 120753642Sguido in.s_addr &= ntohl(~np->in_outmsk); 120853642Sguido in.s_addr += ntohl(np->in_outip); 120953642Sguido /* 121053642Sguido * Calculate destination port. 121153642Sguido */ 121253642Sguido if ((flags & IPN_TCPUDP) && 121353642Sguido (np->in_ppip != 0)) { 121453642Sguido port = ntohs(sport) + l; 121553642Sguido port %= np->in_ppip; 121653642Sguido port += np->in_ppip * 121753642Sguido (inb.s_addr % np->in_ippip); 121853642Sguido port += MAPBLK_MINPORT; 121953642Sguido port = htons(port); 122053642Sguido } 122160852Sdarrenr } else if (!np->in_outip && 122253642Sguido (np->in_outmsk == 0xffffffff)) { 122353642Sguido /* 122453642Sguido * 0/32 - use the interface's IP address. 122553642Sguido */ 122653642Sguido if ((l > 0) || 122760852Sdarrenr fr_ifpaddr(4, fin->fin_ifp, &in) == -1) 122860852Sdarrenr goto badnat; 122955929Sguido in.s_addr = ntohl(in.s_addr); 123060852Sdarrenr } else if (!np->in_outip && !np->in_outmsk) { 123153642Sguido /* 123253642Sguido * 0/0 - use the original source address/port. 123353642Sguido */ 123460852Sdarrenr if (l > 0) 123560852Sdarrenr goto badnat; 123653642Sguido in.s_addr = ntohl(ip->ip_src.s_addr); 123753642Sguido } else if ((np->in_outmsk != 0xffffffff) && 123853642Sguido (np->in_pnext == 0) && 123960852Sdarrenr ((l > 0) || (hm == NULL))) 124053642Sguido np->in_nip++; 124153642Sguido natl = NULL; 124253642Sguido 124353642Sguido if ((nflags & IPN_TCPUDP) && 124453642Sguido ((np->in_redir & NAT_MAPBLK) == 0) && 124553642Sguido (np->in_flags & IPN_AUTOPORTMAP)) { 124653642Sguido if ((l > 0) && (l % np->in_ppip == 0)) { 124753642Sguido if (l > np->in_space) { 124860852Sdarrenr goto badnat; 124953642Sguido } else if ((l > np->in_ppip) && 125053642Sguido np->in_outmsk != 0xffffffff) 125153642Sguido np->in_nip++; 125253642Sguido } 125353642Sguido if (np->in_ppip != 0) { 125453642Sguido port = ntohs(sport); 125553642Sguido port += (l % np->in_ppip); 125653642Sguido port %= np->in_ppip; 125753642Sguido port += np->in_ppip * 125853642Sguido (ntohl(ip->ip_src.s_addr) % 125953642Sguido np->in_ippip); 126053642Sguido port += MAPBLK_MINPORT; 126153642Sguido port = htons(port); 126253642Sguido } 126353642Sguido } else if (((np->in_redir & NAT_MAPBLK) == 0) && 126453642Sguido (nflags & IPN_TCPUDP) && 126553642Sguido (np->in_pnext != 0)) { 126653642Sguido port = htons(np->in_pnext++); 126753642Sguido if (np->in_pnext > ntohs(np->in_pmax)) { 126853642Sguido np->in_pnext = ntohs(np->in_pmin); 126953642Sguido if (np->in_outmsk != 0xffffffff) 127053642Sguido np->in_nip++; 127153642Sguido } 127253642Sguido } 127353642Sguido 127460852Sdarrenr if (np->in_flags & IPN_IPRANGE) { 127560852Sdarrenr if (np->in_nip > ntohl(np->in_outmsk)) 127653642Sguido np->in_nip = ntohl(np->in_outip); 127753642Sguido } else { 127853642Sguido if ((np->in_outmsk != 0xffffffff) && 127953642Sguido ((np->in_nip + 1) & ntohl(np->in_outmsk)) > 128053642Sguido ntohl(np->in_outip)) 128153642Sguido np->in_nip = ntohl(np->in_outip) + 1; 128253642Sguido } 128353642Sguido 128453642Sguido if (!port && (flags & IPN_TCPUDP)) 128553642Sguido port = sport; 128653642Sguido 128753642Sguido /* 128853642Sguido * Here we do a lookup of the connection as seen from 128953642Sguido * the outside. If an IP# pair already exists, try 129053642Sguido * again. So if you have A->B becomes C->B, you can 129153642Sguido * also have D->E become C->E but not D->B causing 129253642Sguido * another C->B. Also take protocol and ports into 129353642Sguido * account when determining whether a pre-existing 129453642Sguido * NAT setup will cause an external conflict where 129553642Sguido * this is appropriate. 129653642Sguido */ 129753642Sguido inb.s_addr = htonl(in.s_addr); 129860852Sdarrenr natl = nat_inlookup(fin->fin_ifp, flags & ~FI_WILDP, 129953642Sguido (u_int)ip->ip_p, ip->ip_dst, inb, 130053642Sguido (port << 16) | dport); 130153642Sguido 130253642Sguido /* 130353642Sguido * Has the search wrapped around and come back to the 130453642Sguido * start ? 130553642Sguido */ 130653642Sguido if ((natl != NULL) && 130753642Sguido (np->in_pnext != 0) && (st_port == np->in_pnext) && 130860852Sdarrenr (np->in_nip != 0) && (st_ip == np->in_nip)) 130960852Sdarrenr goto badnat; 131053642Sguido l++; 131153642Sguido } while (natl != NULL); 131253642Sguido 131353642Sguido if (np->in_space > 0) 131453642Sguido np->in_space--; 131553642Sguido 131653642Sguido /* Setup the NAT table */ 131753642Sguido nat->nat_inip = ip->ip_src; 131853642Sguido nat->nat_outip.s_addr = htonl(in.s_addr); 131953642Sguido nat->nat_oip = ip->ip_dst; 132060852Sdarrenr if (nat->nat_hm == NULL) 132160852Sdarrenr nat->nat_hm = nat_hostmap(np, ip->ip_src, 132260852Sdarrenr nat->nat_outip); 132353642Sguido 132453642Sguido sum1 = LONG_SUM(ntohl(ip->ip_src.s_addr)) + ntohs(sport); 132553642Sguido sum2 = LONG_SUM(in.s_addr) + ntohs(port); 132653642Sguido 132753642Sguido if (flags & IPN_TCPUDP) { 132853642Sguido nat->nat_inport = sport; 132953642Sguido nat->nat_outport = port; /* sport */ 133053642Sguido nat->nat_oport = dport; 133153642Sguido } 133253642Sguido } else { 133353642Sguido /* 133453642Sguido * Otherwise, it's an inbound packet. Most likely, we don't 133553642Sguido * want to rewrite source ports and source addresses. Instead, 133653642Sguido * we want to rewrite to a fixed internal address and fixed 133753642Sguido * internal port. 133853642Sguido */ 133960852Sdarrenr if (np->in_flags & IPN_SPLIT) { 134060852Sdarrenr in.s_addr = np->in_nip; 134160852Sdarrenr if (np->in_inip == htonl(in.s_addr)) 134260852Sdarrenr np->in_nip = ntohl(np->in_inmsk); 134360852Sdarrenr else { 134460852Sdarrenr np->in_nip = ntohl(np->in_inip); 134560852Sdarrenr if (np->in_flags & IPN_ROUNDR) { 134660852Sdarrenr nat_delrdr(np); 134760852Sdarrenr nat_addrdr(np); 134860852Sdarrenr } 134960852Sdarrenr } 135060852Sdarrenr } else { 135160852Sdarrenr in.s_addr = ntohl(np->in_inip); 135260852Sdarrenr if (np->in_flags & IPN_ROUNDR) { 135360852Sdarrenr nat_delrdr(np); 135460852Sdarrenr nat_addrdr(np); 135560852Sdarrenr } 135660852Sdarrenr } 135760852Sdarrenr if (!np->in_pnext) 135853642Sguido nport = dport; 135960852Sdarrenr else { 136060852Sdarrenr /* 136160852Sdarrenr * Whilst not optimized for the case where 136260852Sdarrenr * pmin == pmax, the gain is not significant. 136360852Sdarrenr */ 136460852Sdarrenr nport = ntohs(dport) - ntohs(np->in_pmin) + 136560852Sdarrenr ntohs(np->in_pnext); 136660852Sdarrenr nport = htons(nport); 136760852Sdarrenr } 136853642Sguido 136953642Sguido /* 137053642Sguido * When the redirect-to address is set to 0.0.0.0, just 137153642Sguido * assume a blank `forwarding' of the packet. We don't 137253642Sguido * setup any translation for this either. 137353642Sguido */ 137460852Sdarrenr if (in.s_addr == 0) { 137560852Sdarrenr if (nport == dport) 137660852Sdarrenr goto badnat; 137760852Sdarrenr in.s_addr = ntohl(ip->ip_dst.s_addr); 137853642Sguido } 137953642Sguido 138053642Sguido nat->nat_inip.s_addr = htonl(in.s_addr); 138153642Sguido nat->nat_outip = ip->ip_dst; 138253642Sguido nat->nat_oip = ip->ip_src; 138353642Sguido 138453642Sguido sum1 = LONG_SUM(ntohl(ip->ip_dst.s_addr)) + ntohs(dport); 138553642Sguido sum2 = LONG_SUM(in.s_addr) + ntohs(nport); 138653642Sguido 138753642Sguido if (flags & IPN_TCPUDP) { 138853642Sguido nat->nat_inport = nport; 138953642Sguido nat->nat_outport = dport; 139053642Sguido nat->nat_oport = sport; 139153642Sguido } 139253642Sguido } 139353642Sguido 139453642Sguido CALC_SUMD(sum1, sum2, sumd); 139555929Sguido nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 139655929Sguido#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) 139755929Sguido if ((flags == IPN_TCP) && dohwcksum && 139855929Sguido (qf->qf_ill->ill_ick.ick_magic == ICK_M_CTL_MAGIC)) { 139955929Sguido if (direction == NAT_OUTBOUND) 140055929Sguido sum1 = LONG_SUM(ntohl(in.s_addr)); 140155929Sguido else 140255929Sguido sum1 = LONG_SUM(ntohl(ip->ip_src.s_addr)); 140355929Sguido sum1 += LONG_SUM(ntohl(ip->ip_dst.s_addr)); 140455929Sguido sum1 += 30; 140555929Sguido sum1 = (sum1 & 0xffff) + (sum1 >> 16); 140655929Sguido nat->nat_sumd[1] = NAT_HW_CKSUM|(sum1 & 0xffff); 140755929Sguido } else 140855929Sguido#endif 140955929Sguido nat->nat_sumd[1] = nat->nat_sumd[0]; 141053642Sguido 141153642Sguido if ((flags & IPN_TCPUDP) && ((sport != port) || (dport != nport))) { 141253642Sguido if (direction == NAT_OUTBOUND) 141353642Sguido sum1 = LONG_SUM(ntohl(ip->ip_src.s_addr)); 141453642Sguido else 141553642Sguido sum1 = LONG_SUM(ntohl(ip->ip_dst.s_addr)); 141653642Sguido 141753642Sguido sum2 = LONG_SUM(in.s_addr); 141853642Sguido 141953642Sguido CALC_SUMD(sum1, sum2, sumd); 142053642Sguido nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); 142153642Sguido } else 142255929Sguido nat->nat_ipsumd = nat->nat_sumd[0]; 142353642Sguido 142453642Sguido in.s_addr = htonl(in.s_addr); 142560852Sdarrenr 142660852Sdarrenr#ifdef _KERNEL 142760852Sdarrenr strncpy(nat->nat_ifname, IFNAME(fin->fin_ifp), IFNAMSIZ); 142860852Sdarrenr#endif 142960852Sdarrenr nat_insert(nat); 143060852Sdarrenr 143153642Sguido nat->nat_dir = direction; 143253642Sguido nat->nat_ifp = fin->fin_ifp; 143353642Sguido nat->nat_ptr = np; 143453642Sguido nat->nat_p = ip->ip_p; 143553642Sguido nat->nat_bytes = 0; 143653642Sguido nat->nat_pkts = 0; 143753642Sguido nat->nat_fr = fin->fin_fr; 143853642Sguido if (nat->nat_fr != NULL) { 143960852Sdarrenr ATOMIC_INC32(nat->nat_fr->fr_ref); 144053642Sguido } 144153642Sguido if (direction == NAT_OUTBOUND) { 144253642Sguido if (flags & IPN_TCPUDP) 144353642Sguido tcp->th_sport = port; 144453642Sguido } else { 144553642Sguido if (flags & IPN_TCPUDP) 144653642Sguido tcp->th_dport = nport; 144753642Sguido } 144853642Sguido np->in_use++; 144953642Sguido return nat; 145060852Sdarrenrbadnat: 145160852Sdarrenr nat_stats.ns_badnat++; 145260852Sdarrenr if ((hm = nat->nat_hm) != NULL) 145360852Sdarrenr nat_hostmapdel(hm); 145460852Sdarrenr KFREE(nat); 145560852Sdarrenr return NULL; 145653642Sguido} 145753642Sguido 145853642Sguido 145960852Sdarrenrvoid nat_insert(nat) 146060852Sdarrenrnat_t *nat; 146160852Sdarrenr{ 146260852Sdarrenr nat_t **natp; 146360852Sdarrenr u_int hv; 146460852Sdarrenr 146560852Sdarrenr MUTEX_INIT(&nat->nat_lock, "nat entry lock", NULL); 146660852Sdarrenr 146760852Sdarrenr nat->nat_age = fr_defnatage; 146860852Sdarrenr nat->nat_ifname[sizeof(nat->nat_ifname) - 1] = '\0'; 146960852Sdarrenr if (nat->nat_ifname[0] !='\0') { 147060852Sdarrenr nat->nat_ifp = GETUNIT(nat->nat_ifname, 4); 147160852Sdarrenr } 147260852Sdarrenr 147360852Sdarrenr nat->nat_next = nat_instances; 147460852Sdarrenr nat_instances = nat; 147567614Sdarrenr 147660852Sdarrenr hv = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 147760852Sdarrenr ipf_nattable_sz); 147860852Sdarrenr natp = &nat_table[0][hv]; 147967614Sdarrenr if (*natp) 148067614Sdarrenr (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 148167614Sdarrenr nat->nat_phnext[0] = natp; 148260852Sdarrenr nat->nat_hnext[0] = *natp; 148360852Sdarrenr *natp = nat; 148467614Sdarrenr 148560852Sdarrenr hv = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, 148660852Sdarrenr ipf_nattable_sz); 148760852Sdarrenr natp = &nat_table[1][hv]; 148867614Sdarrenr if (*natp) 148967614Sdarrenr (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 149067614Sdarrenr nat->nat_phnext[1] = natp; 149160852Sdarrenr nat->nat_hnext[1] = *natp; 149260852Sdarrenr *natp = nat; 149360852Sdarrenr 149460852Sdarrenr nat_stats.ns_added++; 149560852Sdarrenr nat_stats.ns_inuse++; 149660852Sdarrenr} 149760852Sdarrenr 149860852Sdarrenr 149960852Sdarrenrnat_t *nat_icmplookup(ip, fin, dir) 150053642Sguidoip_t *ip; 150153642Sguidofr_info_t *fin; 150260852Sdarrenrint dir; 150353642Sguido{ 150453642Sguido icmphdr_t *icmp; 150553642Sguido tcphdr_t *tcp = NULL; 150653642Sguido ip_t *oip; 150764580Sdarrenr int flags = 0, type, minlen; 150853642Sguido 150953642Sguido icmp = (icmphdr_t *)fin->fin_dp; 151053642Sguido /* 151153642Sguido * Does it at least have the return (basic) IP header ? 151253642Sguido * Only a basic IP header (no options) should be with an ICMP error 151353642Sguido * header. 151453642Sguido */ 151553642Sguido if ((ip->ip_hl != 5) || (ip->ip_len < ICMPERR_MINPKTLEN)) 151653642Sguido return NULL; 151753642Sguido type = icmp->icmp_type; 151853642Sguido /* 151953642Sguido * If it's not an error type, then return. 152053642Sguido */ 152153642Sguido if ((type != ICMP_UNREACH) && (type != ICMP_SOURCEQUENCH) && 152253642Sguido (type != ICMP_REDIRECT) && (type != ICMP_TIMXCEED) && 152353642Sguido (type != ICMP_PARAMPROB)) 152453642Sguido return NULL; 152553642Sguido 152653642Sguido oip = (ip_t *)((char *)fin->fin_dp + 8); 152764580Sdarrenr minlen = (oip->ip_hl << 2); 152864580Sdarrenr if (minlen < sizeof(ip_t)) 152953642Sguido return NULL; 153064580Sdarrenr if (ip->ip_len < ICMPERR_IPICMPHLEN + minlen) 153164580Sdarrenr return NULL; 153264580Sdarrenr /* 153364580Sdarrenr * Is the buffer big enough for all of it ? It's the size of the IP 153464580Sdarrenr * header claimed in the encapsulated part which is of concern. It 153564580Sdarrenr * may be too big to be in this buffer but not so big that it's 153664580Sdarrenr * outside the ICMP packet, leading to TCP deref's causing problems. 153764580Sdarrenr * This is possible because we don't know how big oip_hl is when we 153864580Sdarrenr * do the pullup early in fr_check() and thus can't gaurantee it is 153964580Sdarrenr * all here now. 154064580Sdarrenr */ 154164580Sdarrenr#ifdef _KERNEL 154264580Sdarrenr { 154364580Sdarrenr mb_t *m; 154464580Sdarrenr 154564580Sdarrenr# if SOLARIS 154664580Sdarrenr m = fin->fin_qfm; 154764580Sdarrenr if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr) 154864580Sdarrenr return NULL; 154964580Sdarrenr# else 155064580Sdarrenr m = *(mb_t **)fin->fin_mp; 155164580Sdarrenr if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > 155264580Sdarrenr (char *)ip + m->m_len) 155364580Sdarrenr return NULL; 155464580Sdarrenr# endif 155564580Sdarrenr } 155664580Sdarrenr#endif 155764580Sdarrenr 155853642Sguido if (oip->ip_p == IPPROTO_TCP) 155953642Sguido flags = IPN_TCP; 156053642Sguido else if (oip->ip_p == IPPROTO_UDP) 156153642Sguido flags = IPN_UDP; 156253642Sguido if (flags & IPN_TCPUDP) { 156364580Sdarrenr minlen += 8; /* + 64bits of data to get ports */ 156464580Sdarrenr if (ip->ip_len < ICMPERR_IPICMPHLEN + minlen) 156564580Sdarrenr return NULL; 156653642Sguido tcp = (tcphdr_t *)((char *)oip + (oip->ip_hl << 2)); 156760852Sdarrenr if (dir == NAT_INBOUND) 156860852Sdarrenr return nat_inlookup(fin->fin_ifp, flags, 156960852Sdarrenr (u_int)oip->ip_p, oip->ip_dst, oip->ip_src, 157060852Sdarrenr (tcp->th_sport << 16) | tcp->th_dport); 157160852Sdarrenr else 157260852Sdarrenr return nat_outlookup(fin->fin_ifp, flags, 157360852Sdarrenr (u_int)oip->ip_p, oip->ip_dst, oip->ip_src, 157460852Sdarrenr (tcp->th_sport << 16) | tcp->th_dport); 157553642Sguido } 157660852Sdarrenr if (dir == NAT_INBOUND) 157760852Sdarrenr return nat_inlookup(fin->fin_ifp, 0, (u_int)oip->ip_p, 157860852Sdarrenr oip->ip_dst, oip->ip_src, 0); 157960852Sdarrenr else 158060852Sdarrenr return nat_outlookup(fin->fin_ifp, 0, (u_int)oip->ip_p, 158160852Sdarrenr oip->ip_dst, oip->ip_src, 0); 158253642Sguido} 158353642Sguido 158453642Sguido 158553642Sguido/* 158653642Sguido * This should *ONLY* be used for incoming packets to make sure a NAT'd ICMP 158753642Sguido * packet gets correctly recognised. 158853642Sguido */ 158960852Sdarrenrnat_t *nat_icmp(ip, fin, nflags, dir) 159053642Sguidoip_t *ip; 159153642Sguidofr_info_t *fin; 159253642Sguidou_int *nflags; 159360852Sdarrenrint dir; 159453642Sguido{ 159553642Sguido u_32_t sum1, sum2, sumd; 159653642Sguido struct in_addr in; 159753642Sguido icmphdr_t *icmp; 159867614Sdarrenr udphdr_t *udp; 159953642Sguido nat_t *nat; 160053642Sguido ip_t *oip; 160153642Sguido int flags = 0; 160253642Sguido 160363523Sdarrenr if ((fin->fin_fi.fi_fl & FI_SHORT) || (ip->ip_off & IP_OFFMASK)) 160463523Sdarrenr return NULL; 160567614Sdarrenr /* 160667614Sdarrenr * nat_icmplookup() will return NULL for `defective' packets. 160767614Sdarrenr */ 160860852Sdarrenr if ((ip->ip_v != 4) || !(nat = nat_icmplookup(ip, fin, dir))) 160953642Sguido return NULL; 161053642Sguido *nflags = IPN_ICMPERR; 161153642Sguido icmp = (icmphdr_t *)fin->fin_dp; 161253642Sguido oip = (ip_t *)&icmp->icmp_ip; 161353642Sguido if (oip->ip_p == IPPROTO_TCP) 161453642Sguido flags = IPN_TCP; 161553642Sguido else if (oip->ip_p == IPPROTO_UDP) 161653642Sguido flags = IPN_UDP; 161767614Sdarrenr udp = (udphdr_t *)((((char *)oip) + (oip->ip_hl << 2))); 161853642Sguido /* 161953642Sguido * Need to adjust ICMP header to include the real IP#'s and 162053642Sguido * port #'s. Only apply a checksum change relative to the 162167614Sdarrenr * IP address change as it will be modified again in ip_natout 162253642Sguido * for both address and port. Two checksum changes are 162353642Sguido * necessary for the two header address changes. Be careful 162453642Sguido * to only modify the checksum once for the port # and twice 162553642Sguido * for the IP#. 162653642Sguido */ 162760852Sdarrenr 162867614Sdarrenr /* 162967614Sdarrenr * Step 1 163067614Sdarrenr * Fix the IP addresses in the offending IP packet. You also need 163167614Sdarrenr * to adjust the IP header checksum of that offending IP packet 163267614Sdarrenr * and the ICMP checksum of the ICMP error message itself. 163367614Sdarrenr * 163467614Sdarrenr * Unfortunately, for UDP and TCP, the IP addresses are also contained 163567614Sdarrenr * in the pseudo header that is used to compute the UDP resp. TCP 163667614Sdarrenr * checksum. So, we must compensate that as well. Even worse, the 163767614Sdarrenr * change in the UDP and TCP checksums require yet another 163867614Sdarrenr * adjustment of the ICMP checksum of the ICMP error message. 163967614Sdarrenr * 164067614Sdarrenr * For the moment we forget about TCP, because that checksum is not 164167614Sdarrenr * in the first 8 bytes, so it will not be available in most cases. 164267614Sdarrenr */ 164367614Sdarrenr 164453642Sguido if (nat->nat_dir == NAT_OUTBOUND) { 164553642Sguido sum1 = LONG_SUM(ntohl(oip->ip_src.s_addr)); 164653642Sguido in = nat->nat_inip; 164753642Sguido oip->ip_src = in; 164853642Sguido } else { 164953642Sguido sum1 = LONG_SUM(ntohl(oip->ip_dst.s_addr)); 165053642Sguido in = nat->nat_outip; 165153642Sguido oip->ip_dst = in; 165253642Sguido } 165353642Sguido 165453642Sguido sum2 = LONG_SUM(ntohl(in.s_addr)); 165553642Sguido 165653642Sguido CALC_SUMD(sum1, sum2, sumd); 165753642Sguido 165853642Sguido if (nat->nat_dir == NAT_OUTBOUND) { 165967614Sdarrenr /* 166067614Sdarrenr * Fix IP checksum of the offending IP packet to adjust for 166167614Sdarrenr * the change in the IP address. 166267614Sdarrenr * 166367614Sdarrenr * Normally, you would expect that the ICMP checksum of the 166467614Sdarrenr * ICMP error message needs to be adjusted as well for the 166567614Sdarrenr * IP address change in oip. 166667614Sdarrenr * However, this is a NOP, because the ICMP checksum is 166767614Sdarrenr * calculated over the complete ICMP packet, which includes the 166867614Sdarrenr * changed oip IP addresses and oip->ip_sum. However, these 166967614Sdarrenr * two changes cancel each other out (if the delta for 167067614Sdarrenr * the IP address is x, then the delta for ip_sum is minus x), 167167614Sdarrenr * so no change in the icmp_cksum is necessary. 167267614Sdarrenr * 167367614Sdarrenr * Be careful that nat_dir refers to the direction of the 167467614Sdarrenr * offending IP packet (oip), not to its ICMP response (icmp) 167567614Sdarrenr */ 167667614Sdarrenr fix_datacksum(&oip->ip_sum, sumd); 167753642Sguido 167867614Sdarrenr /* 167967614Sdarrenr * Fix UDP pseudo header checksum to compensate for the 168067614Sdarrenr * IP address change. 168167614Sdarrenr */ 168267614Sdarrenr if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) { 168367614Sdarrenr /* 168467614Sdarrenr * The UDP checksum is optional, only adjust it 168567614Sdarrenr * if it has been set. 168667614Sdarrenr */ 168767614Sdarrenr sum1 = ntohs(udp->uh_sum); 168867614Sdarrenr fix_datacksum(&udp->uh_sum, sumd); 168967614Sdarrenr sum2 = ntohs(udp->uh_sum); 169067614Sdarrenr 169167614Sdarrenr /* 169267614Sdarrenr * Fix ICMP checksum to compensate the UDP 169367614Sdarrenr * checksum adjustment. 169467614Sdarrenr */ 169567614Sdarrenr CALC_SUMD(sum1, sum2, sumd); 169667614Sdarrenr fix_outcksum(&icmp->icmp_cksum, sumd); 169767614Sdarrenr } 169867614Sdarrenr 169967614Sdarrenr#if 0 170067614Sdarrenr /* 170167614Sdarrenr * Fix TCP pseudo header checksum to compensate for the 170267614Sdarrenr * IP address change. Before we can do the change, we 170367614Sdarrenr * must make sure that oip is sufficient large to hold 170467614Sdarrenr * the TCP checksum (normally it does not!). 170567614Sdarrenr */ 170667614Sdarrenr if (oip->ip_p == IPPROTO_TCP) { 170767614Sdarrenr 170867614Sdarrenr } 170967614Sdarrenr#endif 171053642Sguido } else { 171167614Sdarrenr 171267614Sdarrenr /* 171367614Sdarrenr * Fix IP checksum of the offending IP packet to adjust for 171467614Sdarrenr * the change in the IP address. 171567614Sdarrenr * 171667614Sdarrenr * Normally, you would expect that the ICMP checksum of the 171767614Sdarrenr * ICMP error message needs to be adjusted as well for the 171867614Sdarrenr * IP address change in oip. 171967614Sdarrenr * However, this is a NOP, because the ICMP checksum is 172067614Sdarrenr * calculated over the complete ICMP packet, which includes the 172167614Sdarrenr * changed oip IP addresses and oip->ip_sum. However, these 172267614Sdarrenr * two changes cancel each other out (if the delta for 172367614Sdarrenr * the IP address is x, then the delta for ip_sum is minus x), 172467614Sdarrenr * so no change in the icmp_cksum is necessary. 172567614Sdarrenr * 172667614Sdarrenr * Be careful that nat_dir refers to the direction of the 172767614Sdarrenr * offending IP packet (oip), not to its ICMP response (icmp) 172867614Sdarrenr */ 172967614Sdarrenr fix_datacksum(&oip->ip_sum, sumd); 173067614Sdarrenr 173167614Sdarrenr/* XXX FV : without having looked at Solaris source code, it seems unlikely 173267614Sdarrenr * that SOLARIS would compensate this in the kernel (a body of an IP packet 173367614Sdarrenr * in the data section of an ICMP packet). I have the feeling that this should 173467614Sdarrenr * be unconditional, but I'm not in a position to check. 173567614Sdarrenr */ 173663523Sdarrenr#if !SOLARIS && !defined(__sgi) 173767614Sdarrenr /* 173867614Sdarrenr * Fix UDP pseudo header checksum to compensate for the 173967614Sdarrenr * IP address change. 174067614Sdarrenr */ 174167614Sdarrenr if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) { 174267614Sdarrenr /* 174367614Sdarrenr * The UDP checksum is optional, only adjust it 174467614Sdarrenr * if it has been set 174567614Sdarrenr */ 174667614Sdarrenr sum1 = ntohs(udp->uh_sum); 174767614Sdarrenr fix_datacksum(&udp->uh_sum, sumd); 174867614Sdarrenr sum2 = ntohs(udp->uh_sum); 174967614Sdarrenr 175067614Sdarrenr /* 175167614Sdarrenr * Fix ICMP checksum to compensate the UDP 175267614Sdarrenr * checksum adjustment. 175367614Sdarrenr */ 175467614Sdarrenr CALC_SUMD(sum1, sum2, sumd); 175567614Sdarrenr fix_incksum(&icmp->icmp_cksum, sumd); 175667614Sdarrenr } 175767614Sdarrenr 175867614Sdarrenr#if 0 175967614Sdarrenr /* 176067614Sdarrenr * Fix TCP pseudo header checksum to compensate for the 176167614Sdarrenr * IP address change. Before we can do the change, we 176267614Sdarrenr * must make sure that oip is sufficient large to hold 176367614Sdarrenr * the TCP checksum (normally it does not!). 176467614Sdarrenr */ 176567614Sdarrenr if (oip->ip_p == IPPROTO_TCP) { 176667614Sdarrenr 176767614Sdarrenr }; 176863523Sdarrenr#endif 176967614Sdarrenr 177067614Sdarrenr#endif 177153642Sguido } 177253642Sguido 177353642Sguido if ((flags & IPN_TCPUDP) != 0) { 177453642Sguido tcphdr_t *tcp; 177553642Sguido 177664580Sdarrenr /* 177764580Sdarrenr * XXX - what if this is bogus hl and we go off the end ? 177864580Sdarrenr * In this case, nat_icmpinlookup() will have returned NULL. 177964580Sdarrenr */ 178067614Sdarrenr tcp = (tcphdr_t *)udp; 178153642Sguido 178267614Sdarrenr /* 178367614Sdarrenr * Step 2 : 178467614Sdarrenr * For offending TCP/UDP IP packets, translate the ports as 178567614Sdarrenr * well, based on the NAT specification. Of course such 178667614Sdarrenr * a change must be reflected in the ICMP checksum as well. 178767614Sdarrenr * 178867614Sdarrenr * Advance notice : Now it becomes complicated :-) 178967614Sdarrenr * 179067614Sdarrenr * Since the port fields are part of the TCP/UDP checksum 179167614Sdarrenr * of the offending IP packet, you need to adjust that checksum 179267614Sdarrenr * as well... but, if you change, you must change the icmp 179367614Sdarrenr * checksum *again*, to reflect that change. 179467614Sdarrenr * 179567614Sdarrenr * To further complicate: the TCP checksum is not in the first 179667614Sdarrenr * 8 bytes of the offending ip packet, so it most likely is not 179767614Sdarrenr * available (we might have to fix that if the encounter a 179867614Sdarrenr * device that returns more than 8 data bytes on icmp error) 179967614Sdarrenr */ 180067614Sdarrenr 180153642Sguido if (nat->nat_dir == NAT_OUTBOUND) { 180253642Sguido if (tcp->th_sport != nat->nat_inport) { 180367614Sdarrenr /* 180467614Sdarrenr * Fix ICMP checksum to compensate port 180567614Sdarrenr * adjustment. 180667614Sdarrenr */ 180753642Sguido sum1 = ntohs(tcp->th_sport); 180853642Sguido sum2 = ntohs(nat->nat_inport); 180953642Sguido CALC_SUMD(sum1, sum2, sumd); 181053642Sguido tcp->th_sport = nat->nat_inport; 181167614Sdarrenr fix_outcksum(&icmp->icmp_cksum, sumd); 181267614Sdarrenr 181367614Sdarrenr /* 181467614Sdarrenr * Fix udp checksum to compensate port 181567614Sdarrenr * adjustment. NOTE : the offending IP packet 181667614Sdarrenr * flows the other direction compared to the 181767614Sdarrenr * ICMP message. 181867614Sdarrenr * 181967614Sdarrenr * The UDP checksum is optional, only adjust 182067614Sdarrenr * it if it has been set. 182167614Sdarrenr */ 182267614Sdarrenr if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) { 182367614Sdarrenr 182467614Sdarrenr sum1 = ntohs(udp->uh_sum); 182567614Sdarrenr fix_datacksum(&udp->uh_sum, sumd); 182667614Sdarrenr sum2 = ntohs(udp->uh_sum); 182767614Sdarrenr 182867614Sdarrenr /* 182967614Sdarrenr * Fix ICMP checksum to 183067614Sdarrenr * compensate UDP checksum 183167614Sdarrenr * adjustment. 183267614Sdarrenr */ 183367614Sdarrenr CALC_SUMD(sum1, sum2, sumd); 183467614Sdarrenr fix_outcksum(&icmp->icmp_cksum, sumd); 183567614Sdarrenr } 183653642Sguido } 183753642Sguido } else { 183867614Sdarrenr 183953642Sguido if (tcp->th_dport != nat->nat_outport) { 184067614Sdarrenr /* 184167614Sdarrenr * Fix ICMP checksum to compensate port 184267614Sdarrenr * adjustment. 184367614Sdarrenr */ 184453642Sguido sum1 = ntohs(tcp->th_dport); 184553642Sguido sum2 = ntohs(nat->nat_outport); 184653642Sguido CALC_SUMD(sum1, sum2, sumd); 184753642Sguido tcp->th_dport = nat->nat_outport; 184867614Sdarrenr fix_incksum(&icmp->icmp_cksum, sumd); 184967614Sdarrenr 185067614Sdarrenr /* 185167614Sdarrenr * Fix udp checksum to compensate port 185267614Sdarrenr * adjustment. NOTE : the offending IP 185367614Sdarrenr * packet flows the other direction compared 185467614Sdarrenr * to the ICMP message. 185567614Sdarrenr * 185667614Sdarrenr * The UDP checksum is optional, only adjust 185767614Sdarrenr * it if it has been set. 185867614Sdarrenr */ 185967614Sdarrenr if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) { 186067614Sdarrenr 186167614Sdarrenr sum1 = ntohs(udp->uh_sum); 186267614Sdarrenr fix_datacksum(&udp->uh_sum, sumd); 186367614Sdarrenr sum2 = ntohs(udp->uh_sum); 186467614Sdarrenr 186567614Sdarrenr /* 186667614Sdarrenr * Fix ICMP checksum to compensate 186767614Sdarrenr * UDP checksum adjustment. 186867614Sdarrenr */ 186967614Sdarrenr CALC_SUMD(sum1, sum2, sumd); 187067614Sdarrenr fix_incksum(&icmp->icmp_cksum, sumd); 187167614Sdarrenr } 187253642Sguido } 187353642Sguido } 187453642Sguido } 187553642Sguido nat->nat_age = fr_defnaticmpage; 187653642Sguido return nat; 187753642Sguido} 187853642Sguido 187953642Sguido 188053642Sguido/* 188153642Sguido * NB: these lookups don't lock access to the list, it assume it has already 188253642Sguido * been done! 188353642Sguido */ 188453642Sguido/* 188553642Sguido * Lookup a nat entry based on the mapped destination ip address/port and 188653642Sguido * real source address/port. We use this lookup when receiving a packet, 188753642Sguido * we're looking for a table entry, based on the destination address. 188853642Sguido * NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. 188953642Sguido */ 189053642Sguidonat_t *nat_inlookup(ifp, flags, p, src, mapdst, ports) 189153642Sguidovoid *ifp; 189253642Sguidoregister u_int flags, p; 189353642Sguidostruct in_addr src , mapdst; 189453642Sguidou_32_t ports; 189553642Sguido{ 189667614Sdarrenr register u_short sport, dport; 189753642Sguido register nat_t *nat; 189853642Sguido register int nflags; 189967614Sdarrenr register u_32_t dst; 190053642Sguido u_int hv; 190153642Sguido 190267614Sdarrenr dst = mapdst.s_addr; 190367614Sdarrenr dport = ports >> 16; 190453642Sguido sport = ports & 0xffff; 190553642Sguido flags &= IPN_TCPUDP; 190653642Sguido 190767614Sdarrenr hv = NAT_HASH_FN(dst, dport, ipf_nattable_sz); 190853642Sguido nat = nat_table[1][hv]; 190953642Sguido for (; nat; nat = nat->nat_hnext[1]) { 191053642Sguido nflags = nat->nat_flags; 191153642Sguido if ((!ifp || ifp == nat->nat_ifp) && 191253642Sguido nat->nat_oip.s_addr == src.s_addr && 191367614Sdarrenr nat->nat_outip.s_addr == dst && 191453642Sguido (((p == 0) && (flags == (nat->nat_flags & IPN_TCPUDP))) 191553642Sguido || (p == nat->nat_p)) && (!flags || 191653642Sguido (((nat->nat_oport == sport) || (nflags & FI_W_DPORT)) && 191767614Sdarrenr ((nat->nat_outport == dport) || (nflags & FI_W_SPORT))))) 191853642Sguido return nat; 191953642Sguido } 192067614Sdarrenr if (!nat_wilds || !(flags & IPN_TCPUDP)) 192167614Sdarrenr return NULL; 192267614Sdarrenr RWLOCK_EXIT(&ipf_nat); 192367614Sdarrenr hv = NAT_HASH_FN(dst, 0, ipf_nattable_sz); 192467614Sdarrenr WRITE_ENTER(&ipf_nat); 192567614Sdarrenr nat = nat_table[1][hv]; 192667614Sdarrenr for (; nat; nat = nat->nat_hnext[1]) { 192767614Sdarrenr nflags = nat->nat_flags; 192867614Sdarrenr if (ifp && ifp != nat->nat_ifp) 192967614Sdarrenr continue; 193067614Sdarrenr if (!(nflags & IPN_TCPUDP)) 193167614Sdarrenr continue; 193267614Sdarrenr if (!(nflags & FI_WILDP)) 193367614Sdarrenr continue; 193467614Sdarrenr if (nat->nat_oip.s_addr != src.s_addr || 193567614Sdarrenr nat->nat_outip.s_addr != dst) 193667614Sdarrenr continue; 193767614Sdarrenr if (((nat->nat_oport == sport) || (nflags & FI_W_DPORT)) && 193867614Sdarrenr ((nat->nat_outport == dport) || (nflags & FI_W_SPORT))) { 193967614Sdarrenr hv = NAT_HASH_FN(dst, dport, ipf_nattable_sz); 194067614Sdarrenr nat_tabmove(nat, hv); 194167614Sdarrenr break; 194267614Sdarrenr } 194367614Sdarrenr } 194467614Sdarrenr MUTEX_DOWNGRADE(&ipf_nat); 194567614Sdarrenr return nat; 194653642Sguido} 194753642Sguido 194853642Sguido 194967614Sdarrenrstatic void nat_tabmove(nat, hv) 195067614Sdarrenrnat_t *nat; 195167614Sdarrenru_int hv; 195267614Sdarrenr{ 195367614Sdarrenr nat_t **natp; 195467614Sdarrenr 195567614Sdarrenr /* 195667614Sdarrenr * Remove the NAT entry from the old location 195767614Sdarrenr */ 195867614Sdarrenr if (nat->nat_hnext[0]) 195967614Sdarrenr nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 196067614Sdarrenr *nat->nat_phnext[0] = nat->nat_hnext[0]; 196167614Sdarrenr 196267614Sdarrenr if (nat->nat_hnext[1]) 196367614Sdarrenr nat->nat_hnext[0]->nat_phnext[1] = nat->nat_phnext[1]; 196467614Sdarrenr *nat->nat_phnext[1] = nat->nat_hnext[1]; 196567614Sdarrenr 196667614Sdarrenr natp = &nat_table[0][hv]; 196767614Sdarrenr if (*natp) 196867614Sdarrenr (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 196967614Sdarrenr nat->nat_phnext[0] = natp; 197067614Sdarrenr nat->nat_hnext[0] = *natp; 197167614Sdarrenr *natp = nat; 197267614Sdarrenr 197367614Sdarrenr /* 197467614Sdarrenr * Add into the NAT table in the new position 197567614Sdarrenr */ 197667614Sdarrenr natp = &nat_table[1][hv]; 197767614Sdarrenr if (*natp) 197867614Sdarrenr (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 197967614Sdarrenr nat->nat_phnext[1] = natp; 198067614Sdarrenr nat->nat_hnext[1] = *natp; 198167614Sdarrenr *natp = nat; 198267614Sdarrenr} 198367614Sdarrenr 198467614Sdarrenr 198553642Sguido/* 198653642Sguido * Lookup a nat entry based on the source 'real' ip address/port and 198753642Sguido * destination address/port. We use this lookup when sending a packet out, 198853642Sguido * we're looking for a table entry, based on the source address. 198953642Sguido * NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. 199053642Sguido */ 199153642Sguidonat_t *nat_outlookup(ifp, flags, p, src, dst, ports) 199253642Sguidovoid *ifp; 199353642Sguidoregister u_int flags, p; 199453642Sguidostruct in_addr src , dst; 199553642Sguidou_32_t ports; 199653642Sguido{ 199753642Sguido register u_short sport, dport; 199853642Sguido register nat_t *nat; 199953642Sguido register int nflags; 200067614Sdarrenr u_32_t srcip; 200153642Sguido u_int hv; 200253642Sguido 200353642Sguido sport = ports & 0xffff; 200453642Sguido dport = ports >> 16; 200553642Sguido flags &= IPN_TCPUDP; 200667614Sdarrenr srcip = src.s_addr; 200753642Sguido 200867614Sdarrenr hv = NAT_HASH_FN(srcip, sport, ipf_nattable_sz); 200953642Sguido nat = nat_table[0][hv]; 201053642Sguido for (; nat; nat = nat->nat_hnext[0]) { 201153642Sguido nflags = nat->nat_flags; 201253642Sguido 201353642Sguido if ((!ifp || ifp == nat->nat_ifp) && 201467614Sdarrenr nat->nat_inip.s_addr == srcip && 201553642Sguido nat->nat_oip.s_addr == dst.s_addr && 201653642Sguido (((p == 0) && (flags == (nat->nat_flags & IPN_TCPUDP))) 201753642Sguido || (p == nat->nat_p)) && (!flags || 201853642Sguido ((nat->nat_inport == sport || nflags & FI_W_SPORT) && 201953642Sguido (nat->nat_oport == dport || nflags & FI_W_DPORT)))) 202053642Sguido return nat; 202153642Sguido } 202267614Sdarrenr if (!nat_wilds || !(flags & IPN_TCPUDP)) 202367614Sdarrenr return NULL; 202467614Sdarrenr RWLOCK_EXIT(&ipf_nat); 202567614Sdarrenr hv = NAT_HASH_FN(srcip, 0, ipf_nattable_sz); 202667614Sdarrenr WRITE_ENTER(&ipf_nat); 202767614Sdarrenr nat = nat_table[0][hv]; 202867614Sdarrenr for (; nat; nat = nat->nat_hnext[0]) { 202967614Sdarrenr nflags = nat->nat_flags; 203067614Sdarrenr if (ifp && ifp != nat->nat_ifp) 203167614Sdarrenr continue; 203267614Sdarrenr if (!(nflags & IPN_TCPUDP)) 203367614Sdarrenr continue; 203467614Sdarrenr if (!(nflags & FI_WILDP)) 203567614Sdarrenr continue; 203667614Sdarrenr if ((nat->nat_inip.s_addr != srcip) || 203767614Sdarrenr (nat->nat_oip.s_addr != dst.s_addr)) 203867614Sdarrenr continue; 203967614Sdarrenr if (((nat->nat_inport == sport) || (nflags & FI_W_DPORT)) && 204067614Sdarrenr ((nat->nat_oport == dport) || (nflags & FI_W_SPORT))) { 204167614Sdarrenr hv = NAT_HASH_FN(srcip, sport, ipf_nattable_sz); 204267614Sdarrenr nat_tabmove(nat, hv); 204367614Sdarrenr break; 204467614Sdarrenr } 204567614Sdarrenr } 204667614Sdarrenr MUTEX_DOWNGRADE(&ipf_nat); 204767614Sdarrenr return nat; 204853642Sguido} 204953642Sguido 205053642Sguido 205153642Sguido/* 205253642Sguido * Lookup the NAT tables to search for a matching redirect 205353642Sguido */ 205453642Sguidonat_t *nat_lookupredir(np) 205553642Sguidoregister natlookup_t *np; 205653642Sguido{ 205753642Sguido u_32_t ports; 205853642Sguido nat_t *nat; 205953642Sguido 206053642Sguido ports = (np->nl_outport << 16) | np->nl_inport; 206153642Sguido /* 206253642Sguido * If nl_inip is non null, this is a lookup based on the real 206353642Sguido * ip address. Else, we use the fake. 206453642Sguido */ 206553642Sguido if ((nat = nat_outlookup(NULL, np->nl_flags, 0, np->nl_inip, 206653642Sguido np->nl_outip, ports))) { 206753642Sguido np->nl_realip = nat->nat_outip; 206853642Sguido np->nl_realport = nat->nat_outport; 206953642Sguido } 207053642Sguido return nat; 207153642Sguido} 207253642Sguido 207353642Sguido 207460852Sdarrenrstatic int nat_match(fin, np, ip) 207560852Sdarrenrfr_info_t *fin; 207660852Sdarrenripnat_t *np; 207760852Sdarrenrip_t *ip; 207860852Sdarrenr{ 207960852Sdarrenr frtuc_t *ft; 208060852Sdarrenr 208160852Sdarrenr if (ip->ip_v != 4) 208260852Sdarrenr return 0; 208360852Sdarrenr 208460852Sdarrenr if (np->in_p && ip->ip_p != np->in_p) 208560852Sdarrenr return 0; 208660852Sdarrenr if (fin->fin_out) { 208763523Sdarrenr if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK))) 208860852Sdarrenr return 0; 208963523Sdarrenr if (((fin->fin_fi.fi_saddr & np->in_inmsk) != np->in_inip) 209063523Sdarrenr ^ ((np->in_flags & IPN_NOTSRC) != 0)) 209160852Sdarrenr return 0; 209263523Sdarrenr if (((fin->fin_fi.fi_daddr & np->in_srcmsk) != np->in_srcip) 209363523Sdarrenr ^ ((np->in_flags & IPN_NOTDST) != 0)) 209460852Sdarrenr return 0; 209560852Sdarrenr } else { 209663523Sdarrenr if (!(np->in_redir & NAT_REDIRECT)) 209760852Sdarrenr return 0; 209863523Sdarrenr if (((fin->fin_fi.fi_saddr & np->in_srcmsk) != np->in_srcip) 209963523Sdarrenr ^ ((np->in_flags & IPN_NOTSRC) != 0)) 210063523Sdarrenr return 0; 210163523Sdarrenr if (((fin->fin_fi.fi_daddr & np->in_outmsk) != np->in_outip) 210263523Sdarrenr ^ ((np->in_flags & IPN_NOTDST) != 0)) 210363523Sdarrenr return 0; 210460852Sdarrenr } 210560852Sdarrenr 210660852Sdarrenr ft = &np->in_tuc; 210763523Sdarrenr if (!(fin->fin_fi.fi_fl & FI_TCPUDP) || 210863523Sdarrenr (fin->fin_fi.fi_fl & FI_SHORT) || (ip->ip_off & IP_OFFMASK)) { 210960852Sdarrenr if (ft->ftu_scmp || ft->ftu_dcmp) 211060852Sdarrenr return 0; 211160852Sdarrenr return 1; 211260852Sdarrenr } 211360852Sdarrenr 211460852Sdarrenr return fr_tcpudpchk(ft, fin); 211560852Sdarrenr} 211660852Sdarrenr 211760852Sdarrenr 211853642Sguido/* 211953642Sguido * Packets going out on the external interface go through this. 212053642Sguido * Here, the source address requires alteration, if anything. 212153642Sguido */ 212253642Sguidoint ip_natout(ip, fin) 212353642Sguidoip_t *ip; 212453642Sguidofr_info_t *fin; 212553642Sguido{ 212653642Sguido register ipnat_t *np = NULL; 212753642Sguido register u_32_t ipa; 212853642Sguido tcphdr_t *tcp = NULL; 212960852Sdarrenr u_short sport = 0, dport = 0, *csump = NULL; 213053642Sguido struct ifnet *ifp; 213153642Sguido int natadd = 1; 213253642Sguido frentry_t *fr; 213360852Sdarrenr u_int nflags = 0, hv, msk; 213453642Sguido u_32_t iph; 213553642Sguido nat_t *nat; 213653642Sguido int i; 213753642Sguido 213860852Sdarrenr if (nat_list == NULL || (fr_nat_lock)) 213953642Sguido return 0; 214053642Sguido 214153642Sguido if ((fr = fin->fin_fr) && !(fr->fr_flags & FR_DUP) && 214253642Sguido fr->fr_tif.fd_ifp && fr->fr_tif.fd_ifp != (void *)-1) 214353642Sguido ifp = fr->fr_tif.fd_ifp; 214453642Sguido else 214553642Sguido ifp = fin->fin_ifp; 214653642Sguido 214753642Sguido if (!(ip->ip_off & IP_OFFMASK) && !(fin->fin_fi.fi_fl & FI_SHORT)) { 214853642Sguido if (ip->ip_p == IPPROTO_TCP) 214953642Sguido nflags = IPN_TCP; 215053642Sguido else if (ip->ip_p == IPPROTO_UDP) 215153642Sguido nflags = IPN_UDP; 215253642Sguido if ((nflags & IPN_TCPUDP)) { 215353642Sguido tcp = (tcphdr_t *)fin->fin_dp; 215453642Sguido sport = tcp->th_sport; 215553642Sguido dport = tcp->th_dport; 215653642Sguido } 215753642Sguido } 215853642Sguido 215953642Sguido ipa = ip->ip_src.s_addr; 216053642Sguido 216153642Sguido READ_ENTER(&ipf_nat); 216260852Sdarrenr 216360852Sdarrenr if ((ip->ip_p == IPPROTO_ICMP) && 216460852Sdarrenr (nat = nat_icmp(ip, fin, &nflags, NAT_OUTBOUND))) 216560852Sdarrenr ; 216660852Sdarrenr else if ((ip->ip_off & (IP_OFFMASK|IP_MF)) && 216760852Sdarrenr (nat = ipfr_nat_knownfrag(ip, fin))) 216853642Sguido natadd = 0; 216953642Sguido else if ((nat = nat_outlookup(ifp, nflags, (u_int)ip->ip_p, ip->ip_src, 217053642Sguido ip->ip_dst, (dport << 16) | sport))) { 217153642Sguido nflags = nat->nat_flags; 217253642Sguido if ((nflags & (FI_W_SPORT|FI_W_DPORT)) != 0) { 217353642Sguido if ((nflags & FI_W_SPORT) && 217453642Sguido (nat->nat_inport != sport)) 217553642Sguido nat->nat_inport = sport; 217653642Sguido else if ((nflags & FI_W_DPORT) && 217753642Sguido (nat->nat_oport != dport)) 217853642Sguido nat->nat_oport = dport; 217953642Sguido if (nat->nat_outport == 0) 218053642Sguido nat->nat_outport = sport; 218153642Sguido nat->nat_flags &= ~(FI_W_DPORT|FI_W_SPORT); 218253642Sguido nflags = nat->nat_flags; 218367614Sdarrenr nat_wilds--; 218453642Sguido } 218553642Sguido } else { 218653642Sguido RWLOCK_EXIT(&ipf_nat); 218753642Sguido WRITE_ENTER(&ipf_nat); 218853642Sguido /* 218953642Sguido * If there is no current entry in the nat table for this IP#, 219053642Sguido * create one for it (if there is a matching rule). 219153642Sguido */ 219253642Sguido msk = 0xffffffff; 219353642Sguido i = 32; 219453642Sguidomaskloop: 219553642Sguido iph = ipa & htonl(msk); 219660852Sdarrenr hv = NAT_HASH_FN(iph, 0, ipf_natrules_sz); 219753642Sguido for (np = nat_rules[hv]; np; np = np->in_mnext) 219853642Sguido { 219960852Sdarrenr if ((np->in_ifp && (np->in_ifp != ifp)) || 220060852Sdarrenr !np->in_space) 220160852Sdarrenr continue; 220260852Sdarrenr if ((np->in_flags & IPN_RF) && 220360852Sdarrenr !(np->in_flags & nflags)) 220460852Sdarrenr continue; 220560852Sdarrenr if (np->in_flags & IPN_FILTER) { 220660852Sdarrenr if (!nat_match(fin, np, ip)) 220760852Sdarrenr continue; 220860852Sdarrenr } else if ((ipa & np->in_inmsk) != np->in_inip) 220960852Sdarrenr continue; 221060852Sdarrenr if (np->in_redir & (NAT_MAP|NAT_MAPBLK)) { 221153642Sguido if (*np->in_plabel && !appr_ok(ip, tcp, np)) 221253642Sguido continue; 221353642Sguido /* 221453642Sguido * If it's a redirection, then we don't want to 221553642Sguido * create new outgoing port stuff. 221653642Sguido * Redirections are only for incoming 221753642Sguido * connections. 221853642Sguido */ 221953642Sguido if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK))) 222053642Sguido continue; 222153642Sguido if ((nat = nat_new(np, ip, fin, (u_int)nflags, 222253642Sguido NAT_OUTBOUND))) { 222353642Sguido np->in_hits++; 222453642Sguido#ifdef IPFILTER_LOG 222553642Sguido nat_log(nat, (u_int)np->in_redir); 222653642Sguido#endif 222753642Sguido break; 222853642Sguido } 222953642Sguido } 223053642Sguido } 223153642Sguido if ((np == NULL) && (i > 0)) { 223253642Sguido do { 223353642Sguido i--; 223453642Sguido msk <<= 1; 223553642Sguido } while ((i >= 0) && ((nat_masks & (1 << i)) == 0)); 223653642Sguido if (i >= 0) 223753642Sguido goto maskloop; 223853642Sguido } 223953642Sguido MUTEX_DOWNGRADE(&ipf_nat); 224053642Sguido } 224153642Sguido 224253642Sguido if (nat) { 224353642Sguido np = nat->nat_ptr; 224453642Sguido if (natadd && fin->fin_fi.fi_fl & FI_FRAG) 224553642Sguido ipfr_nat_newfrag(ip, fin, 0, nat); 224660852Sdarrenr MUTEX_ENTER(&nat->nat_lock); 224753642Sguido nat->nat_age = fr_defnatage; 224853642Sguido nat->nat_bytes += ip->ip_len; 224953642Sguido nat->nat_pkts++; 225060852Sdarrenr MUTEX_EXIT(&nat->nat_lock); 225153642Sguido 225253642Sguido /* 225353642Sguido * Fix up checksums, not by recalculating them, but 225453642Sguido * simply computing adjustments. 225553642Sguido */ 225663523Sdarrenr if (nflags == IPN_ICMPERR) { 225763523Sdarrenr u_32_t s1, s2, sumd; 225863523Sdarrenr 225963523Sdarrenr s1 = LONG_SUM(ntohl(ip->ip_src.s_addr)); 226063523Sdarrenr s2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)); 226163523Sdarrenr CALC_SUMD(s1, s2, sumd); 226263523Sdarrenr 226363523Sdarrenr if (nat->nat_dir == NAT_OUTBOUND) 226467614Sdarrenr fix_incksum(&ip->ip_sum, sumd); 226563523Sdarrenr else 226667614Sdarrenr fix_outcksum(&ip->ip_sum, sumd); 226763523Sdarrenr } 226853642Sguido#if SOLARIS || defined(__sgi) 226963523Sdarrenr else { 227063523Sdarrenr if (nat->nat_dir == NAT_OUTBOUND) 227167614Sdarrenr fix_outcksum(&ip->ip_sum, nat->nat_ipsumd); 227263523Sdarrenr else 227367614Sdarrenr fix_incksum(&ip->ip_sum, nat->nat_ipsumd); 227463523Sdarrenr } 227553642Sguido#endif 227663523Sdarrenr ip->ip_src = nat->nat_outip; 227753642Sguido 227853642Sguido if (!(ip->ip_off & IP_OFFMASK) && 227953642Sguido !(fin->fin_fi.fi_fl & FI_SHORT)) { 228053642Sguido 228153642Sguido if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) { 228253642Sguido tcp->th_sport = nat->nat_outport; 228353642Sguido fin->fin_data[0] = ntohs(tcp->th_sport); 228453642Sguido } 228553642Sguido 228653642Sguido if (ip->ip_p == IPPROTO_TCP) { 228753642Sguido csump = &tcp->th_sum; 228860852Sdarrenr MUTEX_ENTER(&nat->nat_lock); 228953642Sguido fr_tcp_age(&nat->nat_age, 229060852Sdarrenr nat->nat_tcpstate, fin, 1); 229153642Sguido if (nat->nat_age < fr_defnaticmpage) 229253642Sguido nat->nat_age = fr_defnaticmpage; 229353642Sguido#ifdef LARGE_NAT 229460852Sdarrenr else if (nat->nat_age > fr_defnatage) 229560852Sdarrenr nat->nat_age = fr_defnatage; 229653642Sguido#endif 229753642Sguido /* 229853642Sguido * Increase this because we may have 229953642Sguido * "keep state" following this too and 230053642Sguido * packet storms can occur if this is 230153642Sguido * removed too quickly. 230253642Sguido */ 230353642Sguido if (nat->nat_age == fr_tcpclosed) 230453642Sguido nat->nat_age = fr_tcplastack; 230560852Sdarrenr MUTEX_EXIT(&nat->nat_lock); 230653642Sguido } else if (ip->ip_p == IPPROTO_UDP) { 230753642Sguido udphdr_t *udp = (udphdr_t *)tcp; 230853642Sguido 230953642Sguido if (udp->uh_sum) 231053642Sguido csump = &udp->uh_sum; 231160852Sdarrenr } else if (ip->ip_p == IPPROTO_ICMP) { 231260852Sdarrenr nat->nat_age = fr_defnaticmpage; 231353642Sguido } 231463523Sdarrenr 231553642Sguido if (csump) { 231653642Sguido if (nat->nat_dir == NAT_OUTBOUND) 231767614Sdarrenr fix_outcksum(csump, nat->nat_sumd[1]); 231853642Sguido else 231967614Sdarrenr fix_incksum(csump, nat->nat_sumd[1]); 232053642Sguido } 232153642Sguido } 232260852Sdarrenr 232353642Sguido if ((np->in_apr != NULL) && (np->in_dport == 0 || 232460852Sdarrenr (tcp != NULL && dport == np->in_dport))) { 232560852Sdarrenr i = appr_check(ip, fin, nat); 232660852Sdarrenr if (i == 0) 232760852Sdarrenr i = 1; 232860852Sdarrenr } else 232960852Sdarrenr i = 1; 233060852Sdarrenr ATOMIC_INCL(nat_stats.ns_mapped[1]); 233153642Sguido RWLOCK_EXIT(&ipf_nat); /* READ */ 233260852Sdarrenr return i; 233353642Sguido } 233453642Sguido RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ 233553642Sguido return 0; 233653642Sguido} 233753642Sguido 233853642Sguido 233953642Sguido/* 234053642Sguido * Packets coming in from the external interface go through this. 234153642Sguido * Here, the destination address requires alteration, if anything. 234253642Sguido */ 234353642Sguidoint ip_natin(ip, fin) 234453642Sguidoip_t *ip; 234553642Sguidofr_info_t *fin; 234653642Sguido{ 234753642Sguido register struct in_addr src; 234853642Sguido register struct in_addr in; 234953642Sguido register ipnat_t *np; 235053642Sguido u_int nflags = 0, natadd = 1, hv, msk; 235153642Sguido struct ifnet *ifp = fin->fin_ifp; 235253642Sguido tcphdr_t *tcp = NULL; 235353642Sguido u_short sport = 0, dport = 0, *csump = NULL; 235453642Sguido nat_t *nat; 235553642Sguido u_32_t iph; 235653642Sguido int i; 235753642Sguido 235860852Sdarrenr if ((nat_list == NULL) || (ip->ip_v != 4) || (fr_nat_lock)) 235953642Sguido return 0; 236053642Sguido 236153642Sguido if (!(ip->ip_off & IP_OFFMASK) && !(fin->fin_fi.fi_fl & FI_SHORT)) { 236253642Sguido if (ip->ip_p == IPPROTO_TCP) 236353642Sguido nflags = IPN_TCP; 236453642Sguido else if (ip->ip_p == IPPROTO_UDP) 236553642Sguido nflags = IPN_UDP; 236653642Sguido if ((nflags & IPN_TCPUDP)) { 236753642Sguido tcp = (tcphdr_t *)fin->fin_dp; 236853642Sguido dport = tcp->th_dport; 236953642Sguido sport = tcp->th_sport; 237053642Sguido } 237153642Sguido } 237253642Sguido 237353642Sguido in = ip->ip_dst; 237453642Sguido /* make sure the source address is to be redirected */ 237553642Sguido src = ip->ip_src; 237653642Sguido 237753642Sguido READ_ENTER(&ipf_nat); 237853642Sguido 237960852Sdarrenr if ((ip->ip_p == IPPROTO_ICMP) && 238060852Sdarrenr (nat = nat_icmp(ip, fin, &nflags, NAT_INBOUND))) 238153642Sguido ; 238263523Sdarrenr else if ((ip->ip_off & (IP_OFFMASK|IP_MF)) && 238353642Sguido (nat = ipfr_nat_knownfrag(ip, fin))) 238453642Sguido natadd = 0; 238553642Sguido else if ((nat = nat_inlookup(fin->fin_ifp, nflags, (u_int)ip->ip_p, 238653642Sguido ip->ip_src, in, (dport << 16) | sport))) { 238753642Sguido nflags = nat->nat_flags; 238853642Sguido if ((nflags & (FI_W_SPORT|FI_W_DPORT)) != 0) { 238953642Sguido if ((nat->nat_oport != sport) && (nflags & FI_W_DPORT)) 239053642Sguido nat->nat_oport = sport; 239153642Sguido else if ((nat->nat_outport != dport) && 239253642Sguido (nflags & FI_W_SPORT)) 239353642Sguido nat->nat_outport = dport; 239453642Sguido nat->nat_flags &= ~(FI_W_SPORT|FI_W_DPORT); 239553642Sguido nflags = nat->nat_flags; 239667614Sdarrenr nat_wilds--; 239753642Sguido } 239853642Sguido } else { 239953642Sguido RWLOCK_EXIT(&ipf_nat); 240053642Sguido WRITE_ENTER(&ipf_nat); 240153642Sguido /* 240253642Sguido * If there is no current entry in the nat table for this IP#, 240353642Sguido * create one for it (if there is a matching rule). 240453642Sguido */ 240553642Sguido msk = 0xffffffff; 240653642Sguido i = 32; 240753642Sguidomaskloop: 240853642Sguido iph = in.s_addr & htonl(msk); 240960852Sdarrenr hv = NAT_HASH_FN(iph, 0, ipf_rdrrules_sz); 241060852Sdarrenr for (np = rdr_rules[hv]; np; np = np->in_rnext) { 241160852Sdarrenr if ((np->in_ifp && (np->in_ifp != ifp)) || 241260852Sdarrenr (np->in_p && (np->in_p != ip->ip_p)) || 241360852Sdarrenr (np->in_flags && !(nflags & np->in_flags))) 241460852Sdarrenr continue; 241560852Sdarrenr if (np->in_flags & IPN_FILTER) { 241660852Sdarrenr if (!nat_match(fin, np, ip)) 241760852Sdarrenr continue; 241860852Sdarrenr } else if ((in.s_addr & np->in_outmsk) != np->in_outip) 241960852Sdarrenr continue; 242060852Sdarrenr if ((np->in_redir & NAT_REDIRECT) && 242163523Sdarrenr (!np->in_pmin || (np->in_flags & IPN_FILTER) || 242260852Sdarrenr ((ntohs(np->in_pmax) >= ntohs(dport)) && 242360852Sdarrenr (ntohs(dport) >= ntohs(np->in_pmin))))) 242453642Sguido if ((nat = nat_new(np, ip, fin, nflags, 242553642Sguido NAT_INBOUND))) { 242653642Sguido np->in_hits++; 242753642Sguido#ifdef IPFILTER_LOG 242853642Sguido nat_log(nat, (u_int)np->in_redir); 242953642Sguido#endif 243053642Sguido break; 243153642Sguido } 243260852Sdarrenr } 243360852Sdarrenr 243453642Sguido if ((np == NULL) && (i > 0)) { 243553642Sguido do { 243653642Sguido i--; 243753642Sguido msk <<= 1; 243853642Sguido } while ((i >= 0) && ((rdr_masks & (1 << i)) == 0)); 243953642Sguido if (i >= 0) 244053642Sguido goto maskloop; 244153642Sguido } 244253642Sguido MUTEX_DOWNGRADE(&ipf_nat); 244353642Sguido } 244453642Sguido if (nat) { 244553642Sguido np = nat->nat_ptr; 244653642Sguido fin->fin_fr = nat->nat_fr; 244753642Sguido if (natadd && fin->fin_fi.fi_fl & FI_FRAG) 244853642Sguido ipfr_nat_newfrag(ip, fin, 0, nat); 244953642Sguido if ((np->in_apr != NULL) && (np->in_dport == 0 || 245060852Sdarrenr (tcp != NULL && sport == np->in_dport))) { 245160852Sdarrenr i = appr_check(ip, fin, nat); 245260852Sdarrenr if (i == -1) { 245360852Sdarrenr RWLOCK_EXIT(&ipf_nat); 245460852Sdarrenr return i; 245560852Sdarrenr } 245660852Sdarrenr } 245753642Sguido 245860852Sdarrenr MUTEX_ENTER(&nat->nat_lock); 245953642Sguido if (nflags != IPN_ICMPERR) 246053642Sguido nat->nat_age = fr_defnatage; 246153642Sguido 246253642Sguido nat->nat_bytes += ip->ip_len; 246353642Sguido nat->nat_pkts++; 246460852Sdarrenr MUTEX_EXIT(&nat->nat_lock); 246553642Sguido ip->ip_dst = nat->nat_inip; 246660852Sdarrenr fin->fin_fi.fi_daddr = nat->nat_inip.s_addr; 246753642Sguido 246853642Sguido /* 246953642Sguido * Fix up checksums, not by recalculating them, but 247053642Sguido * simply computing adjustments. 247153642Sguido */ 247253642Sguido#if SOLARIS || defined(__sgi) 247353642Sguido if (nat->nat_dir == NAT_OUTBOUND) 247467614Sdarrenr fix_incksum(&ip->ip_sum, nat->nat_ipsumd); 247553642Sguido else 247667614Sdarrenr fix_outcksum(&ip->ip_sum, nat->nat_ipsumd); 247753642Sguido#endif 247853642Sguido if (!(ip->ip_off & IP_OFFMASK) && 247953642Sguido !(fin->fin_fi.fi_fl & FI_SHORT)) { 248053642Sguido 248153642Sguido if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) { 248253642Sguido tcp->th_dport = nat->nat_inport; 248353642Sguido fin->fin_data[1] = ntohs(tcp->th_dport); 248453642Sguido } 248553642Sguido 248653642Sguido if (ip->ip_p == IPPROTO_TCP) { 248753642Sguido csump = &tcp->th_sum; 248860852Sdarrenr MUTEX_ENTER(&nat->nat_lock); 248953642Sguido fr_tcp_age(&nat->nat_age, 249060852Sdarrenr nat->nat_tcpstate, fin, 0); 249153642Sguido if (nat->nat_age < fr_defnaticmpage) 249253642Sguido nat->nat_age = fr_defnaticmpage; 249353642Sguido#ifdef LARGE_NAT 249460852Sdarrenr else if (nat->nat_age > fr_defnatage) 249560852Sdarrenr nat->nat_age = fr_defnatage; 249653642Sguido#endif 249753642Sguido /* 249853642Sguido * Increase this because we may have 249953642Sguido * "keep state" following this too and 250053642Sguido * packet storms can occur if this is 250153642Sguido * removed too quickly. 250253642Sguido */ 250353642Sguido if (nat->nat_age == fr_tcpclosed) 250453642Sguido nat->nat_age = fr_tcplastack; 250560852Sdarrenr MUTEX_EXIT(&nat->nat_lock); 250653642Sguido } else if (ip->ip_p == IPPROTO_UDP) { 250753642Sguido udphdr_t *udp = (udphdr_t *)tcp; 250853642Sguido 250953642Sguido if (udp->uh_sum) 251053642Sguido csump = &udp->uh_sum; 251160852Sdarrenr } else if (ip->ip_p == IPPROTO_ICMP) { 251260852Sdarrenr nat->nat_age = fr_defnaticmpage; 251353642Sguido } 251460852Sdarrenr 251553642Sguido if (csump) { 251653642Sguido if (nat->nat_dir == NAT_OUTBOUND) 251767614Sdarrenr fix_incksum(csump, nat->nat_sumd[0]); 251853642Sguido else 251967614Sdarrenr fix_outcksum(csump, nat->nat_sumd[0]); 252053642Sguido } 252153642Sguido } 252260852Sdarrenr ATOMIC_INCL(nat_stats.ns_mapped[0]); 252353642Sguido RWLOCK_EXIT(&ipf_nat); /* READ */ 252453642Sguido return 1; 252553642Sguido } 252653642Sguido RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ 252753642Sguido return 0; 252853642Sguido} 252953642Sguido 253053642Sguido 253153642Sguido/* 253253642Sguido * Free all memory used by NAT structures allocated at runtime. 253353642Sguido */ 253453642Sguidovoid ip_natunload() 253553642Sguido{ 253653642Sguido WRITE_ENTER(&ipf_nat); 253753642Sguido (void) nat_clearlist(); 253853642Sguido (void) nat_flushtable(); 253953642Sguido RWLOCK_EXIT(&ipf_nat); 254053642Sguido 254153642Sguido if (nat_table[0] != NULL) { 254253642Sguido KFREES(nat_table[0], sizeof(nat_t *) * ipf_nattable_sz); 254353642Sguido nat_table[0] = NULL; 254453642Sguido } 254553642Sguido if (nat_table[1] != NULL) { 254653642Sguido KFREES(nat_table[1], sizeof(nat_t *) * ipf_nattable_sz); 254753642Sguido nat_table[1] = NULL; 254853642Sguido } 254953642Sguido if (nat_rules != NULL) { 255053642Sguido KFREES(nat_rules, sizeof(ipnat_t *) * ipf_natrules_sz); 255153642Sguido nat_rules = NULL; 255253642Sguido } 255353642Sguido if (rdr_rules != NULL) { 255453642Sguido KFREES(rdr_rules, sizeof(ipnat_t *) * ipf_rdrrules_sz); 255553642Sguido rdr_rules = NULL; 255653642Sguido } 255760852Sdarrenr if (maptable != NULL) { 255860852Sdarrenr KFREES(maptable, sizeof(hostmap_t *) * ipf_hostmap_sz); 255960852Sdarrenr maptable = NULL; 256060852Sdarrenr } 256153642Sguido} 256253642Sguido 256353642Sguido 256453642Sguido/* 256553642Sguido * Slowly expire held state for NAT entries. Timeouts are set in 256653642Sguido * expectation of this being called twice per second. 256753642Sguido */ 256853642Sguidovoid ip_natexpire() 256953642Sguido{ 257053642Sguido register struct nat *nat, **natp; 257153642Sguido#if defined(_KERNEL) && !SOLARIS 257253642Sguido int s; 257353642Sguido#endif 257453642Sguido 257553642Sguido SPL_NET(s); 257653642Sguido WRITE_ENTER(&ipf_nat); 257753642Sguido for (natp = &nat_instances; (nat = *natp); ) { 257853642Sguido nat->nat_age--; 257953642Sguido if (nat->nat_age) { 258053642Sguido natp = &nat->nat_next; 258153642Sguido continue; 258253642Sguido } 258353642Sguido *natp = nat->nat_next; 258453642Sguido#ifdef IPFILTER_LOG 258553642Sguido nat_log(nat, NL_EXPIRE); 258653642Sguido#endif 258753642Sguido nat_delete(nat); 258853642Sguido nat_stats.ns_expire++; 258953642Sguido } 259053642Sguido RWLOCK_EXIT(&ipf_nat); 259153642Sguido SPL_X(s); 259253642Sguido} 259353642Sguido 259453642Sguido 259553642Sguido/* 259653642Sguido */ 259753642Sguidovoid ip_natsync(ifp) 259853642Sguidovoid *ifp; 259953642Sguido{ 260053642Sguido register ipnat_t *n; 260153642Sguido register nat_t *nat; 260253642Sguido register u_32_t sum1, sum2, sumd; 260353642Sguido struct in_addr in; 260453642Sguido ipnat_t *np; 260553642Sguido void *ifp2; 260653642Sguido#if defined(_KERNEL) && !SOLARIS 260753642Sguido int s; 260853642Sguido#endif 260953642Sguido 261053642Sguido /* 261153642Sguido * Change IP addresses for NAT sessions for any protocol except TCP 261253642Sguido * since it will break the TCP connection anyway. 261353642Sguido */ 261453642Sguido SPL_NET(s); 261553642Sguido WRITE_ENTER(&ipf_nat); 261653642Sguido for (nat = nat_instances; nat; nat = nat->nat_next) 261753642Sguido if (((ifp == NULL) || (ifp == nat->nat_ifp)) && 261853642Sguido !(nat->nat_flags & IPN_TCP) && (np = nat->nat_ptr) && 261953642Sguido (np->in_outmsk == 0xffffffff) && !np->in_nip) { 262053642Sguido ifp2 = nat->nat_ifp; 262153642Sguido /* 262253642Sguido * Change the map-to address to be the same as the 262353642Sguido * new one. 262453642Sguido */ 262553642Sguido sum1 = nat->nat_outip.s_addr; 262660852Sdarrenr if (fr_ifpaddr(4, ifp2, &in) != -1) 262755929Sguido nat->nat_outip = in; 262853642Sguido sum2 = nat->nat_outip.s_addr; 262953642Sguido 263053642Sguido if (sum1 == sum2) 263153642Sguido continue; 263253642Sguido /* 263353642Sguido * Readjust the checksum adjustment to take into 263453642Sguido * account the new IP#. 263553642Sguido */ 263653642Sguido CALC_SUMD(sum1, sum2, sumd); 263755929Sguido /* XXX - dont change for TCP when solaris does 263855929Sguido * hardware checksumming. 263955929Sguido */ 264055929Sguido sumd += nat->nat_sumd[0]; 264155929Sguido nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 264255929Sguido nat->nat_sumd[1] = nat->nat_sumd[0]; 264353642Sguido } 264453642Sguido 264553642Sguido for (n = nat_list; (n != NULL); n = n->in_next) 264655929Sguido if (n->in_ifp == ifp) { 264760852Sdarrenr n->in_ifp = (void *)GETUNIT(n->in_ifname, 4); 264860852Sdarrenr if (!n->in_ifp) 264960852Sdarrenr n->in_ifp = (void *)-1; 265060852Sdarrenr } 265160852Sdarrenr RWLOCK_EXIT(&ipf_nat); 265253642Sguido SPL_X(s); 265353642Sguido} 265453642Sguido 265553642Sguido 265653642Sguido#ifdef IPFILTER_LOG 265753642Sguidovoid nat_log(nat, type) 265853642Sguidostruct nat *nat; 265953642Sguidou_int type; 266053642Sguido{ 266153642Sguido struct ipnat *np; 266253642Sguido struct natlog natl; 266353642Sguido void *items[1]; 266453642Sguido size_t sizes[1]; 266553642Sguido int rulen, types[1]; 266653642Sguido 266753642Sguido natl.nl_inip = nat->nat_inip; 266853642Sguido natl.nl_outip = nat->nat_outip; 266953642Sguido natl.nl_origip = nat->nat_oip; 267053642Sguido natl.nl_bytes = nat->nat_bytes; 267153642Sguido natl.nl_pkts = nat->nat_pkts; 267253642Sguido natl.nl_origport = nat->nat_oport; 267353642Sguido natl.nl_inport = nat->nat_inport; 267453642Sguido natl.nl_outport = nat->nat_outport; 267557096Sguido natl.nl_p = nat->nat_p; 267653642Sguido natl.nl_type = type; 267753642Sguido natl.nl_rule = -1; 267853642Sguido#ifndef LARGE_NAT 267953642Sguido if (nat->nat_ptr != NULL) { 268053642Sguido for (rulen = 0, np = nat_list; np; np = np->in_next, rulen++) 268153642Sguido if (np == nat->nat_ptr) { 268253642Sguido natl.nl_rule = rulen; 268353642Sguido break; 268453642Sguido } 268553642Sguido } 268653642Sguido#endif 268753642Sguido items[0] = &natl; 268853642Sguido sizes[0] = sizeof(natl); 268953642Sguido types[0] = 0; 269053642Sguido 269153642Sguido (void) ipllog(IPL_LOGNAT, NULL, items, sizes, types, 1); 269253642Sguido} 269353642Sguido#endif 2694