ip_nat.c revision 95418
153642Sguido/* 280482Sdarrenr * Copyright (C) 1995-2001 by Darren Reed. 353642Sguido * 480482Sdarrenr * See the IPFILTER.LICENCE file for details on licencing. 553642Sguido * 653642Sguido * Added redirect stuff and a LOT of bug fixes. (mcn@EnGarde.com) 753642Sguido */ 853642Sguido 953642Sguido#if defined(__FreeBSD__) && defined(KERNEL) && !defined(_KERNEL) 1053642Sguido#define _KERNEL 1153642Sguido#endif 1253642Sguido 1392685Sdarrenr#ifdef __sgi 1492685Sdarrenr# include <sys/ptimers.h> 1592685Sdarrenr#endif 1653642Sguido#include <sys/errno.h> 1753642Sguido#include <sys/types.h> 1853642Sguido#include <sys/param.h> 1953642Sguido#include <sys/time.h> 2053642Sguido#include <sys/file.h> 2153642Sguido#if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \ 2253642Sguido defined(_KERNEL) 2353642Sguido# include "opt_ipfilter_log.h" 2453642Sguido#endif 2553642Sguido#if !defined(_KERNEL) && !defined(KERNEL) 2653642Sguido# include <stdio.h> 2753642Sguido# include <string.h> 2853642Sguido# include <stdlib.h> 2953642Sguido#endif 3060852Sdarrenr#if (defined(KERNEL) || defined(_KERNEL)) && (__FreeBSD_version >= 220000) 3153642Sguido# include <sys/filio.h> 3253642Sguido# include <sys/fcntl.h> 3353642Sguido#else 3453642Sguido# include <sys/ioctl.h> 3553642Sguido#endif 3653642Sguido#include <sys/fcntl.h> 3753642Sguido#ifndef linux 3853642Sguido# include <sys/protosw.h> 3953642Sguido#endif 4053642Sguido#include <sys/socket.h> 4153642Sguido#if defined(_KERNEL) && !defined(linux) 4253642Sguido# include <sys/systm.h> 4353642Sguido#endif 4453642Sguido#if !defined(__SVR4) && !defined(__svr4__) 4553642Sguido# ifndef linux 4653642Sguido# include <sys/mbuf.h> 4753642Sguido# endif 4853642Sguido#else 4953642Sguido# include <sys/filio.h> 5053642Sguido# include <sys/byteorder.h> 5153642Sguido# ifdef _KERNEL 5253642Sguido# include <sys/dditypes.h> 5353642Sguido# endif 5453642Sguido# include <sys/stream.h> 5553642Sguido# include <sys/kmem.h> 5653642Sguido#endif 5753642Sguido#if __FreeBSD_version >= 300000 5853642Sguido# include <sys/queue.h> 5953642Sguido#endif 6053642Sguido#include <net/if.h> 6153642Sguido#if __FreeBSD_version >= 300000 6253642Sguido# include <net/if_var.h> 6353642Sguido# if defined(_KERNEL) && !defined(IPFILTER_LKM) 6453642Sguido# include "opt_ipfilter.h" 6553642Sguido# endif 6653642Sguido#endif 6753642Sguido#ifdef sun 6853642Sguido# include <net/af.h> 6953642Sguido#endif 7053642Sguido#include <net/route.h> 7153642Sguido#include <netinet/in.h> 7253642Sguido#include <netinet/in_systm.h> 7353642Sguido#include <netinet/ip.h> 7453642Sguido 7553642Sguido#ifdef __sgi 7653642Sguido# ifdef IFF_DRVRLOCK /* IRIX6 */ 7753642Sguido#include <sys/hashing.h> 7853642Sguido#include <netinet/in_var.h> 7953642Sguido# endif 8053642Sguido#endif 8153642Sguido 8253642Sguido#ifdef RFC1825 8353642Sguido# include <vpn/md5.h> 8453642Sguido# include <vpn/ipsec.h> 8553642Sguidoextern struct ifnet vpnif; 8653642Sguido#endif 8753642Sguido 8853642Sguido#ifndef linux 8953642Sguido# include <netinet/ip_var.h> 9080482Sdarrenr# include <netinet/tcp_fsm.h> 9153642Sguido#endif 9253642Sguido#include <netinet/tcp.h> 9353642Sguido#include <netinet/udp.h> 9453642Sguido#include <netinet/ip_icmp.h> 9553642Sguido#include "netinet/ip_compat.h" 9653642Sguido#include <netinet/tcpip.h> 9753642Sguido#include "netinet/ip_fil.h" 9853642Sguido#include "netinet/ip_nat.h" 9953642Sguido#include "netinet/ip_frag.h" 10053642Sguido#include "netinet/ip_state.h" 10192685Sdarrenr#include "netinet/ip_proxy.h" 10253642Sguido#if (__FreeBSD_version >= 300000) 10353642Sguido# include <sys/malloc.h> 10453642Sguido#endif 10553642Sguido#ifndef MIN 10653642Sguido# define MIN(a,b) (((a)<(b))?(a):(b)) 10753642Sguido#endif 10853642Sguido#undef SOCKADDR_IN 10953642Sguido#define SOCKADDR_IN struct sockaddr_in 11053642Sguido 11180482Sdarrenr#if !defined(lint) 11280482Sdarrenrstatic const char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed"; 11380482Sdarrenr/* static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.37.2.44 2001/07/21 07:17:22 darrenr Exp $"; */ 11480482Sdarrenrstatic const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_nat.c 95418 2002-04-25 03:31:39Z darrenr $"; 11580482Sdarrenr#endif 11680482Sdarrenr 11753642Sguidonat_t **nat_table[2] = { NULL, NULL }, 11853642Sguido *nat_instances = NULL; 11953642Sguidoipnat_t *nat_list = NULL; 12053642Sguidou_int ipf_nattable_sz = NAT_TABLE_SZ; 12153642Sguidou_int ipf_natrules_sz = NAT_SIZE; 12253642Sguidou_int ipf_rdrrules_sz = RDR_SIZE; 12360852Sdarrenru_int ipf_hostmap_sz = HOSTMAP_SIZE; 12453642Sguidou_32_t nat_masks = 0; 12553642Sguidou_32_t rdr_masks = 0; 12653642Sguidoipnat_t **nat_rules = NULL; 12753642Sguidoipnat_t **rdr_rules = NULL; 12860852Sdarrenrhostmap_t **maptable = NULL; 12953642Sguido 13053642Sguidou_long fr_defnatage = DEF_NAT_AGE, 13153642Sguido fr_defnaticmpage = 6; /* 3 seconds */ 13264580Sdarrenrnatstat_t nat_stats; 13360852Sdarrenrint fr_nat_lock = 0; 13453642Sguido#if (SOLARIS || defined(__sgi)) && defined(_KERNEL) 13572006Sdarrenrextern kmutex_t ipf_rw; 13653642Sguidoextern KRWLOCK_T ipf_nat; 13753642Sguido#endif 13853642Sguido 13953642Sguidostatic int nat_flushtable __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)); 14892685Sdarrenrstatic void nat_tabmove __P((fr_info_t *, nat_t *)); 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. 25572006Sdarrenr * 25672006Sdarrenr * Must be called with ipf_nat held as a write lock. 25760852Sdarrenr */ 25860852Sdarrenrstatic struct hostmap *nat_hostmap(np, real, map) 25960852Sdarrenripnat_t *np; 26060852Sdarrenrstruct in_addr real; 26160852Sdarrenrstruct in_addr map; 26260852Sdarrenr{ 26360852Sdarrenr hostmap_t *hm; 26453642Sguido u_int hv; 26553642Sguido 26660852Sdarrenr hv = real.s_addr % HOSTMAP_SIZE; 26760852Sdarrenr for (hm = maptable[hv]; hm; hm = hm->hm_next) 26860852Sdarrenr if ((hm->hm_realip.s_addr == real.s_addr) && 26960852Sdarrenr (np == hm->hm_ipnat)) { 27060852Sdarrenr hm->hm_ref++; 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 return hm; 28753642Sguido} 28853642Sguido 28953642Sguido 29072006Sdarrenr/* 29172006Sdarrenr * Must be called with ipf_nat held as a write lock. 29272006Sdarrenr */ 29360852Sdarrenrstatic void nat_hostmapdel(hm) 29460852Sdarrenrstruct hostmap *hm; 29560852Sdarrenr{ 29660852Sdarrenr ATOMIC_DEC32(hm->hm_ref); 29760852Sdarrenr if (hm->hm_ref == 0) { 29860852Sdarrenr if (hm->hm_next) 29960852Sdarrenr hm->hm_next->hm_pnext = hm->hm_pnext; 30060852Sdarrenr *hm->hm_pnext = hm->hm_next; 30160852Sdarrenr KFREE(hm); 30260852Sdarrenr } 30360852Sdarrenr} 30460852Sdarrenr 30560852Sdarrenr 30680482Sdarrenrvoid fix_outcksum(fin, sp, n) 30780482Sdarrenrfr_info_t *fin; 30853642Sguidou_short *sp; 30953642Sguidou_32_t n; 31053642Sguido{ 31153642Sguido register u_short sumshort; 31253642Sguido register u_32_t sum1; 31353642Sguido 31453642Sguido if (!n) 31553642Sguido return; 31655929Sguido else if (n & NAT_HW_CKSUM) { 31780482Sdarrenr n &= 0xffff; 31880482Sdarrenr n += fin->fin_dlen; 31980482Sdarrenr n = (n & 0xffff) + (n >> 16); 32055929Sguido *sp = n & 0xffff; 32155929Sguido return; 32255929Sguido } 32353642Sguido sum1 = (~ntohs(*sp)) & 0xffff; 32453642Sguido sum1 += (n); 32553642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 32653642Sguido /* Again */ 32753642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 32853642Sguido sumshort = ~(u_short)sum1; 32953642Sguido *(sp) = htons(sumshort); 33053642Sguido} 33153642Sguido 33253642Sguido 33380482Sdarrenrvoid fix_incksum(fin, sp, n) 33480482Sdarrenrfr_info_t *fin; 33553642Sguidou_short *sp; 33653642Sguidou_32_t n; 33753642Sguido{ 33853642Sguido register u_short sumshort; 33953642Sguido register u_32_t sum1; 34053642Sguido 34153642Sguido if (!n) 34253642Sguido return; 34355929Sguido else if (n & NAT_HW_CKSUM) { 34480482Sdarrenr n &= 0xffff; 34580482Sdarrenr n += fin->fin_dlen; 34680482Sdarrenr n = (n & 0xffff) + (n >> 16); 34755929Sguido *sp = n & 0xffff; 34855929Sguido return; 34955929Sguido } 35053642Sguido#ifdef sparc 35153642Sguido sum1 = (~(*sp)) & 0xffff; 35253642Sguido#else 35353642Sguido sum1 = (~ntohs(*sp)) & 0xffff; 35453642Sguido#endif 35553642Sguido sum1 += ~(n) & 0xffff; 35653642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 35753642Sguido /* Again */ 35853642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 35953642Sguido sumshort = ~(u_short)sum1; 36053642Sguido *(sp) = htons(sumshort); 36153642Sguido} 36253642Sguido 36353642Sguido 36453642Sguido/* 36567614Sdarrenr * fix_datacksum is used *only* for the adjustments of checksums in the data 36667614Sdarrenr * section of an IP packet. 36767614Sdarrenr * 36867614Sdarrenr * The only situation in which you need to do this is when NAT'ing an 36967614Sdarrenr * ICMP error message. Such a message, contains in its body the IP header 37067614Sdarrenr * of the original IP packet, that causes the error. 37167614Sdarrenr * 37267614Sdarrenr * You can't use fix_incksum or fix_outcksum in that case, because for the 37367614Sdarrenr * kernel the data section of the ICMP error is just data, and no special 37467614Sdarrenr * processing like hardware cksum or ntohs processing have been done by the 37567614Sdarrenr * kernel on the data section. 37667614Sdarrenr */ 37767614Sdarrenrvoid fix_datacksum(sp, n) 37867614Sdarrenru_short *sp; 37967614Sdarrenru_32_t n; 38067614Sdarrenr{ 38167614Sdarrenr register u_short sumshort; 38267614Sdarrenr register u_32_t sum1; 38367614Sdarrenr 38467614Sdarrenr if (!n) 38567614Sdarrenr return; 38667614Sdarrenr 38767614Sdarrenr sum1 = (~ntohs(*sp)) & 0xffff; 38867614Sdarrenr sum1 += (n); 38967614Sdarrenr sum1 = (sum1 >> 16) + (sum1 & 0xffff); 39067614Sdarrenr /* Again */ 39167614Sdarrenr sum1 = (sum1 >> 16) + (sum1 & 0xffff); 39267614Sdarrenr sumshort = ~(u_short)sum1; 39367614Sdarrenr *(sp) = htons(sumshort); 39467614Sdarrenr} 39567614Sdarrenr 39667614Sdarrenr/* 39753642Sguido * How the NAT is organised and works. 39853642Sguido * 39953642Sguido * Inside (interface y) NAT Outside (interface x) 40053642Sguido * -------------------- -+- ------------------------------------- 40153642Sguido * Packet going | out, processsed by ip_natout() for x 40253642Sguido * ------------> | ------------> 40353642Sguido * src=10.1.1.1 | src=192.1.1.1 40453642Sguido * | 40553642Sguido * | in, processed by ip_natin() for x 40653642Sguido * <------------ | <------------ 40753642Sguido * dst=10.1.1.1 | dst=192.1.1.1 40853642Sguido * -------------------- -+- ------------------------------------- 40953642Sguido * ip_natout() - changes ip_src and if required, sport 41053642Sguido * - creates a new mapping, if required. 41153642Sguido * ip_natin() - changes ip_dst and if required, dport 41253642Sguido * 41353642Sguido * In the NAT table, internal source is recorded as "in" and externally 41453642Sguido * seen as "out". 41553642Sguido */ 41653642Sguido 41753642Sguido/* 41853642Sguido * Handle ioctls which manipulate the NAT. 41953642Sguido */ 42053642Sguidoint nat_ioctl(data, cmd, mode) 42160852Sdarrenr#if defined(__NetBSD__) || defined(__OpenBSD__) || (__FreeBSD_version >= 300003) 42253642Sguidou_long cmd; 42353642Sguido#else 42453642Sguidoint cmd; 42553642Sguido#endif 42653642Sguidocaddr_t data; 42753642Sguidoint mode; 42853642Sguido{ 42953642Sguido register ipnat_t *nat, *nt, *n = NULL, **np = NULL; 43095418Sdarrenr int error = 0, ret, arg, getlock; 43153642Sguido ipnat_t natd; 43253642Sguido u_32_t i, j; 43353642Sguido 43453642Sguido#if (BSD >= 199306) && defined(_KERNEL) 43588876Sdarrenr if ((securelevel >= 3) && (mode & FWRITE)) 43653642Sguido return EPERM; 43753642Sguido#endif 43853642Sguido 43953642Sguido nat = NULL; /* XXX gcc -Wuninitialized */ 44053642Sguido KMALLOC(nt, ipnat_t *); 44195418Sdarrenr getlock = (mode & NAT_LOCKHELD) ? 0 : 1; 44295418Sdarrenr if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) { 44395418Sdarrenr if (mode & NAT_SYSSPACE) { 44495418Sdarrenr bcopy(data, (char *)&natd, sizeof(natd)); 44595418Sdarrenr error = 0; 44695418Sdarrenr } else { 44795418Sdarrenr error = IRCOPYPTR(data, (char *)&natd, sizeof(natd)); 44895418Sdarrenr } 44995418Sdarrenr } else if (cmd == SIOCIPFFL) { /* SIOCFLNAT & SIOCCNATL */ 45060852Sdarrenr error = IRCOPY(data, (char *)&arg, sizeof(arg)); 45164580Sdarrenr if (error) 45264580Sdarrenr error = EFAULT; 45364580Sdarrenr } 45453642Sguido 45560852Sdarrenr if (error) 45660852Sdarrenr goto done; 45760852Sdarrenr 45853642Sguido /* 45953642Sguido * For add/delete, look to see if the NAT entry is already present 46053642Sguido */ 46195418Sdarrenr if (getlock == 1) 46295418Sdarrenr WRITE_ENTER(&ipf_nat); 46353642Sguido if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) { 46453642Sguido nat = &natd; 46553642Sguido nat->in_flags &= IPN_USERFLAGS; 46653642Sguido if ((nat->in_redir & NAT_MAPBLK) == 0) { 46760852Sdarrenr if ((nat->in_flags & IPN_SPLIT) == 0) 46860852Sdarrenr nat->in_inip &= nat->in_inmsk; 46960852Sdarrenr if ((nat->in_flags & IPN_IPRANGE) == 0) 47053642Sguido nat->in_outip &= nat->in_outmsk; 47153642Sguido } 47253642Sguido for (np = &nat_list; (n = *np); np = &n->in_next) 47353642Sguido if (!bcmp((char *)&nat->in_flags, (char *)&n->in_flags, 47453642Sguido IPN_CMPSIZ)) 47553642Sguido break; 47653642Sguido } 47753642Sguido 47853642Sguido switch (cmd) 47953642Sguido { 48055929Sguido#ifdef IPFILTER_LOG 48155929Sguido case SIOCIPFFB : 48260852Sdarrenr { 48360852Sdarrenr int tmp; 48460852Sdarrenr 48555929Sguido if (!(mode & FWRITE)) 48655929Sguido error = EPERM; 48760852Sdarrenr else { 48860852Sdarrenr tmp = ipflog_clear(IPL_LOGNAT); 48960852Sdarrenr IWCOPY((char *)&tmp, (char *)data, sizeof(tmp)); 49060852Sdarrenr } 49155929Sguido break; 49260852Sdarrenr } 49355929Sguido#endif 49453642Sguido case SIOCADNAT : 49553642Sguido if (!(mode & FWRITE)) { 49653642Sguido error = EPERM; 49753642Sguido break; 49853642Sguido } 49953642Sguido if (n) { 50053642Sguido error = EEXIST; 50153642Sguido break; 50253642Sguido } 50353642Sguido if (nt == NULL) { 50453642Sguido error = ENOMEM; 50553642Sguido break; 50653642Sguido } 50753642Sguido n = nt; 50853642Sguido nt = NULL; 50953642Sguido bcopy((char *)nat, (char *)n, sizeof(*n)); 51060852Sdarrenr n->in_ifp = (void *)GETUNIT(n->in_ifname, 4); 51153642Sguido if (!n->in_ifp) 51253642Sguido n->in_ifp = (void *)-1; 51353642Sguido if (n->in_plabel[0] != '\0') { 51492685Sdarrenr n->in_apr = appr_lookup(n->in_p, n->in_plabel); 51553642Sguido if (!n->in_apr) { 51653642Sguido error = ENOENT; 51753642Sguido break; 51853642Sguido } 51953642Sguido } 52053642Sguido n->in_next = NULL; 52153642Sguido *np = n; 52253642Sguido 52363523Sdarrenr if (n->in_redir & NAT_REDIRECT) { 52463523Sdarrenr n->in_flags &= ~IPN_NOTDST; 52560852Sdarrenr nat_addrdr(n); 52663523Sdarrenr } 52763523Sdarrenr if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { 52863523Sdarrenr n->in_flags &= ~IPN_NOTSRC; 52960852Sdarrenr nat_addnat(n); 53063523Sdarrenr } 53153642Sguido 53253642Sguido n->in_use = 0; 53353642Sguido if (n->in_redir & NAT_MAPBLK) 53453642Sguido n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk); 53553642Sguido else if (n->in_flags & IPN_AUTOPORTMAP) 53653642Sguido n->in_space = USABLE_PORTS * ~ntohl(n->in_inmsk); 53760852Sdarrenr else if (n->in_flags & IPN_IPRANGE) 53853642Sguido n->in_space = ntohl(n->in_outmsk) - ntohl(n->in_outip); 53960852Sdarrenr else if (n->in_flags & IPN_SPLIT) 54060852Sdarrenr n->in_space = 2; 54153642Sguido else 54253642Sguido n->in_space = ~ntohl(n->in_outmsk); 54353642Sguido /* 54453642Sguido * Calculate the number of valid IP addresses in the output 54553642Sguido * mapping range. In all cases, the range is inclusive of 54653642Sguido * the start and ending IP addresses. 54753642Sguido * If to a CIDR address, lose 2: broadcast + network address 54864580Sdarrenr * (so subtract 1) 54953642Sguido * If to a range, add one. 55053642Sguido * If to a single IP address, set to 1. 55153642Sguido */ 55253642Sguido if (n->in_space) { 55360852Sdarrenr if ((n->in_flags & IPN_IPRANGE) != 0) 55453642Sguido n->in_space += 1; 55553642Sguido else 55653642Sguido n->in_space -= 1; 55753642Sguido } else 55853642Sguido n->in_space = 1; 55953642Sguido if ((n->in_outmsk != 0xffffffff) && (n->in_outmsk != 0) && 56060852Sdarrenr ((n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0)) 56153642Sguido n->in_nip = ntohl(n->in_outip) + 1; 56260852Sdarrenr else if ((n->in_flags & IPN_SPLIT) && 56360852Sdarrenr (n->in_redir & NAT_REDIRECT)) 56460852Sdarrenr n->in_nip = ntohl(n->in_inip); 56553642Sguido else 56653642Sguido n->in_nip = ntohl(n->in_outip); 56753642Sguido if (n->in_redir & NAT_MAP) { 56853642Sguido n->in_pnext = ntohs(n->in_pmin); 56953642Sguido /* 57053642Sguido * Multiply by the number of ports made available. 57153642Sguido */ 57253642Sguido if (ntohs(n->in_pmax) >= ntohs(n->in_pmin)) { 57353642Sguido n->in_space *= (ntohs(n->in_pmax) - 57453642Sguido ntohs(n->in_pmin) + 1); 57553642Sguido /* 57653642Sguido * Because two different sources can map to 57753642Sguido * different destinations but use the same 57853642Sguido * local IP#/port #. 57953642Sguido * If the result is smaller than in_space, then 58053642Sguido * we may have wrapped around 32bits. 58153642Sguido */ 58253642Sguido i = n->in_inmsk; 58353642Sguido if ((i != 0) && (i != 0xffffffff)) { 58453642Sguido j = n->in_space * (~ntohl(i) + 1); 58553642Sguido if (j >= n->in_space) 58653642Sguido n->in_space = j; 58753642Sguido else 58853642Sguido n->in_space = 0xffffffff; 58953642Sguido } 59053642Sguido } 59153642Sguido /* 59253642Sguido * If no protocol is specified, multiple by 256. 59353642Sguido */ 59453642Sguido if ((n->in_flags & IPN_TCPUDP) == 0) { 59553642Sguido j = n->in_space * 256; 59653642Sguido if (j >= n->in_space) 59753642Sguido n->in_space = j; 59853642Sguido else 59953642Sguido n->in_space = 0xffffffff; 60053642Sguido } 60153642Sguido } 60253642Sguido /* Otherwise, these fields are preset */ 60353642Sguido n = NULL; 60453642Sguido nat_stats.ns_rules++; 60553642Sguido break; 60653642Sguido case SIOCRMNAT : 60753642Sguido if (!(mode & FWRITE)) { 60853642Sguido error = EPERM; 60953642Sguido n = NULL; 61053642Sguido break; 61153642Sguido } 61253642Sguido if (!n) { 61353642Sguido error = ESRCH; 61453642Sguido break; 61553642Sguido } 61653642Sguido if (n->in_redir & NAT_REDIRECT) 61753642Sguido nat_delrdr(n); 61853642Sguido if (n->in_redir & (NAT_MAPBLK|NAT_MAP)) 61953642Sguido nat_delnat(n); 62053642Sguido if (nat_list == NULL) { 62153642Sguido nat_masks = 0; 62253642Sguido rdr_masks = 0; 62353642Sguido } 62453642Sguido *np = n->in_next; 62553642Sguido if (!n->in_use) { 62653642Sguido if (n->in_apr) 62753642Sguido appr_free(n->in_apr); 62853642Sguido KFREE(n); 62953642Sguido nat_stats.ns_rules--; 63053642Sguido } else { 63153642Sguido n->in_flags |= IPN_DELETE; 63253642Sguido n->in_next = NULL; 63353642Sguido } 63453642Sguido n = NULL; 63553642Sguido break; 63653642Sguido case SIOCGNATS : 63753642Sguido MUTEX_DOWNGRADE(&ipf_nat); 63853642Sguido nat_stats.ns_table[0] = nat_table[0]; 63953642Sguido nat_stats.ns_table[1] = nat_table[1]; 64053642Sguido nat_stats.ns_list = nat_list; 64180482Sdarrenr nat_stats.ns_maptable = maptable; 64253642Sguido nat_stats.ns_nattab_sz = ipf_nattable_sz; 64353642Sguido nat_stats.ns_rultab_sz = ipf_natrules_sz; 64453642Sguido nat_stats.ns_rdrtab_sz = ipf_rdrrules_sz; 64580482Sdarrenr nat_stats.ns_hostmap_sz = ipf_hostmap_sz; 64653642Sguido nat_stats.ns_instances = nat_instances; 64753642Sguido nat_stats.ns_apslist = ap_sess_list; 64860852Sdarrenr error = IWCOPYPTR((char *)&nat_stats, (char *)data, 64960852Sdarrenr sizeof(nat_stats)); 65053642Sguido break; 65153642Sguido case SIOCGNATL : 65253642Sguido { 65353642Sguido natlookup_t nl; 65453642Sguido 65553642Sguido MUTEX_DOWNGRADE(&ipf_nat); 65660852Sdarrenr error = IRCOPYPTR((char *)data, (char *)&nl, sizeof(nl)); 65760852Sdarrenr if (error) 65860852Sdarrenr break; 65953642Sguido 66053642Sguido if (nat_lookupredir(&nl)) { 66160852Sdarrenr error = IWCOPYPTR((char *)&nl, (char *)data, 66260852Sdarrenr sizeof(nl)); 66353642Sguido } else 66453642Sguido error = ESRCH; 66553642Sguido break; 66653642Sguido } 66760852Sdarrenr case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */ 66853642Sguido if (!(mode & FWRITE)) { 66953642Sguido error = EPERM; 67053642Sguido break; 67153642Sguido } 67260852Sdarrenr error = 0; 67360852Sdarrenr if (arg == 0) 67460852Sdarrenr ret = nat_flushtable(); 67560852Sdarrenr else if (arg == 1) 67660852Sdarrenr ret = nat_clearlist(); 67760852Sdarrenr else 67860852Sdarrenr error = EINVAL; 67953642Sguido MUTEX_DOWNGRADE(&ipf_nat); 68060852Sdarrenr if (!error) { 68160852Sdarrenr error = IWCOPY((caddr_t)&ret, data, sizeof(ret)); 68260852Sdarrenr if (error) 68360852Sdarrenr error = EFAULT; 68460852Sdarrenr } 68553642Sguido break; 68660852Sdarrenr case SIOCSTLCK : 68760852Sdarrenr error = IRCOPY(data, (caddr_t)&arg, sizeof(arg)); 68860852Sdarrenr if (!error) { 68960852Sdarrenr error = IWCOPY((caddr_t)&fr_nat_lock, data, 69060852Sdarrenr sizeof(fr_nat_lock)); 69160852Sdarrenr if (!error) 69260852Sdarrenr fr_nat_lock = arg; 69364580Sdarrenr } else 69464580Sdarrenr error = EFAULT; 69553642Sguido break; 69660852Sdarrenr case SIOCSTPUT : 69760852Sdarrenr if (fr_nat_lock) 69860852Sdarrenr error = fr_natputent(data); 69960852Sdarrenr else 70060852Sdarrenr error = EACCES; 70160852Sdarrenr break; 70260852Sdarrenr case SIOCSTGSZ : 70360852Sdarrenr if (fr_nat_lock) 70460852Sdarrenr error = fr_natgetsz(data); 70560852Sdarrenr else 70660852Sdarrenr error = EACCES; 70760852Sdarrenr break; 70860852Sdarrenr case SIOCSTGET : 70960852Sdarrenr if (fr_nat_lock) 71060852Sdarrenr error = fr_natgetent(data); 71160852Sdarrenr else 71260852Sdarrenr error = EACCES; 71360852Sdarrenr break; 71453642Sguido case FIONREAD : 71553642Sguido#ifdef IPFILTER_LOG 71672006Sdarrenr arg = (int)iplused[IPL_LOGNAT]; 71753642Sguido MUTEX_DOWNGRADE(&ipf_nat); 71872006Sdarrenr error = IWCOPY((caddr_t)&arg, (caddr_t)data, sizeof(arg)); 71964580Sdarrenr if (error) 72064580Sdarrenr error = EFAULT; 72153642Sguido#endif 72253642Sguido break; 72353642Sguido default : 72453642Sguido error = EINVAL; 72553642Sguido break; 72653642Sguido } 72795418Sdarrenr if (getlock == 1) 72895418Sdarrenr RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ 72960852Sdarrenrdone: 73053642Sguido if (nt) 73153642Sguido KFREE(nt); 73253642Sguido return error; 73353642Sguido} 73453642Sguido 73553642Sguido 73660852Sdarrenrstatic int fr_natgetsz(data) 73760852Sdarrenrcaddr_t data; 73860852Sdarrenr{ 73960852Sdarrenr ap_session_t *aps; 74060852Sdarrenr nat_t *nat, *n; 74160852Sdarrenr int error = 0; 74260852Sdarrenr natget_t ng; 74360852Sdarrenr 74460852Sdarrenr error = IRCOPY(data, (caddr_t)&ng, sizeof(ng)); 74560852Sdarrenr if (error) 74660852Sdarrenr return EFAULT; 74760852Sdarrenr 74860852Sdarrenr nat = ng.ng_ptr; 74960852Sdarrenr if (!nat) { 75060852Sdarrenr nat = nat_instances; 75160852Sdarrenr ng.ng_sz = 0; 75260852Sdarrenr if (nat == NULL) { 75360852Sdarrenr error = IWCOPY((caddr_t)&ng, data, sizeof(ng)); 75460852Sdarrenr if (error) 75560852Sdarrenr error = EFAULT; 75660852Sdarrenr return error; 75760852Sdarrenr } 75860852Sdarrenr } else { 75960852Sdarrenr /* 76060852Sdarrenr * Make sure the pointer we're copying from exists in the 76160852Sdarrenr * current list of entries. Security precaution to prevent 76260852Sdarrenr * copying of random kernel data. 76360852Sdarrenr */ 76460852Sdarrenr for (n = nat_instances; n; n = n->nat_next) 76560852Sdarrenr if (n == nat) 76660852Sdarrenr break; 76760852Sdarrenr if (!n) 76860852Sdarrenr return ESRCH; 76960852Sdarrenr } 77060852Sdarrenr 77160852Sdarrenr ng.ng_sz = sizeof(nat_save_t); 77260852Sdarrenr aps = nat->nat_aps; 77360852Sdarrenr if ((aps != NULL) && (aps->aps_data != 0)) { 77460852Sdarrenr ng.ng_sz += sizeof(ap_session_t); 77560852Sdarrenr ng.ng_sz += aps->aps_psiz; 77660852Sdarrenr } 77760852Sdarrenr 77860852Sdarrenr error = IWCOPY((caddr_t)&ng, data, sizeof(ng)); 77960852Sdarrenr if (error) 78060852Sdarrenr error = EFAULT; 78160852Sdarrenr return error; 78260852Sdarrenr} 78360852Sdarrenr 78460852Sdarrenr 78560852Sdarrenrstatic int fr_natgetent(data) 78660852Sdarrenrcaddr_t data; 78760852Sdarrenr{ 78864580Sdarrenr nat_save_t ipn, *ipnp, *ipnn = NULL; 78960852Sdarrenr register nat_t *n, *nat; 79060852Sdarrenr ap_session_t *aps; 79160852Sdarrenr int error; 79260852Sdarrenr 79360852Sdarrenr error = IRCOPY(data, (caddr_t)&ipnp, sizeof(ipnp)); 79460852Sdarrenr if (error) 79560852Sdarrenr return EFAULT; 79660852Sdarrenr error = IRCOPY((caddr_t)ipnp, (caddr_t)&ipn, sizeof(ipn)); 79760852Sdarrenr if (error) 79860852Sdarrenr return EFAULT; 79960852Sdarrenr 80060852Sdarrenr nat = ipn.ipn_next; 80160852Sdarrenr if (!nat) { 80260852Sdarrenr nat = nat_instances; 80360852Sdarrenr if (nat == NULL) { 80460852Sdarrenr if (nat_instances == NULL) 80560852Sdarrenr return ENOENT; 80660852Sdarrenr return 0; 80760852Sdarrenr } 80860852Sdarrenr } else { 80960852Sdarrenr /* 81060852Sdarrenr * Make sure the pointer we're copying from exists in the 81160852Sdarrenr * current list of entries. Security precaution to prevent 81260852Sdarrenr * copying of random kernel data. 81360852Sdarrenr */ 81460852Sdarrenr for (n = nat_instances; n; n = n->nat_next) 81560852Sdarrenr if (n == nat) 81660852Sdarrenr break; 81760852Sdarrenr if (!n) 81860852Sdarrenr return ESRCH; 81960852Sdarrenr } 82060852Sdarrenr 82160852Sdarrenr ipn.ipn_next = nat->nat_next; 82260852Sdarrenr ipn.ipn_dsize = 0; 82360852Sdarrenr bcopy((char *)nat, (char *)&ipn.ipn_nat, sizeof(ipn.ipn_nat)); 82460852Sdarrenr ipn.ipn_nat.nat_data = NULL; 82560852Sdarrenr 82660852Sdarrenr if (nat->nat_ptr) { 82760852Sdarrenr bcopy((char *)nat->nat_ptr, (char *)&ipn.ipn_ipnat, 82860852Sdarrenr sizeof(ipn.ipn_ipnat)); 82960852Sdarrenr } 83060852Sdarrenr 83160852Sdarrenr if (nat->nat_fr) 83260852Sdarrenr bcopy((char *)nat->nat_fr, (char *)&ipn.ipn_rule, 83360852Sdarrenr sizeof(ipn.ipn_rule)); 83460852Sdarrenr 83560852Sdarrenr if ((aps = nat->nat_aps)) { 83660852Sdarrenr ipn.ipn_dsize = sizeof(*aps); 83760852Sdarrenr if (aps->aps_data) 83860852Sdarrenr ipn.ipn_dsize += aps->aps_psiz; 83960852Sdarrenr KMALLOCS(ipnn, nat_save_t *, sizeof(*ipnn) + ipn.ipn_dsize); 84060852Sdarrenr if (ipnn == NULL) 84164580Sdarrenr return ENOMEM; 84260852Sdarrenr bcopy((char *)&ipn, (char *)ipnn, sizeof(ipn)); 84360852Sdarrenr 84495418Sdarrenr bcopy((char *)aps, (char *)ipnn->ipn_data, sizeof(*aps)); 84560852Sdarrenr if (aps->aps_data) { 84664580Sdarrenr bcopy(aps->aps_data, ipnn->ipn_data + sizeof(*aps), 84760852Sdarrenr aps->aps_psiz); 84864580Sdarrenr ipnn->ipn_dsize += aps->aps_psiz; 84960852Sdarrenr } 85060852Sdarrenr error = IWCOPY((caddr_t)ipnn, ipnp, 85160852Sdarrenr sizeof(ipn) + ipn.ipn_dsize); 85260852Sdarrenr if (error) 85364580Sdarrenr error = EFAULT; 85460852Sdarrenr KFREES(ipnn, sizeof(*ipnn) + ipn.ipn_dsize); 85560852Sdarrenr } else { 85660852Sdarrenr error = IWCOPY((caddr_t)&ipn, ipnp, sizeof(ipn)); 85760852Sdarrenr if (error) 85864580Sdarrenr error = EFAULT; 85960852Sdarrenr } 86064580Sdarrenr return error; 86160852Sdarrenr} 86260852Sdarrenr 86360852Sdarrenr 86460852Sdarrenrstatic int fr_natputent(data) 86560852Sdarrenrcaddr_t data; 86660852Sdarrenr{ 86764580Sdarrenr nat_save_t ipn, *ipnp, *ipnn = NULL; 86860852Sdarrenr register nat_t *n, *nat; 86960852Sdarrenr ap_session_t *aps; 87060852Sdarrenr frentry_t *fr; 87160852Sdarrenr ipnat_t *in; 87260852Sdarrenr 87360852Sdarrenr int error; 87460852Sdarrenr 87560852Sdarrenr error = IRCOPY(data, (caddr_t)&ipnp, sizeof(ipnp)); 87660852Sdarrenr if (error) 87760852Sdarrenr return EFAULT; 87860852Sdarrenr error = IRCOPY((caddr_t)ipnp, (caddr_t)&ipn, sizeof(ipn)); 87960852Sdarrenr if (error) 88060852Sdarrenr return EFAULT; 88164580Sdarrenr nat = NULL; 88260852Sdarrenr if (ipn.ipn_dsize) { 88360852Sdarrenr KMALLOCS(ipnn, nat_save_t *, sizeof(ipn) + ipn.ipn_dsize); 88460852Sdarrenr if (ipnn == NULL) 88560852Sdarrenr return ENOMEM; 88660852Sdarrenr bcopy((char *)&ipn, (char *)ipnn, sizeof(ipn)); 88760852Sdarrenr error = IRCOPY((caddr_t)ipnp, (caddr_t)ipn.ipn_data, 88860852Sdarrenr ipn.ipn_dsize); 88964580Sdarrenr if (error) { 89064580Sdarrenr error = EFAULT; 89164580Sdarrenr goto junkput; 89264580Sdarrenr } 89360852Sdarrenr } else 89460852Sdarrenr ipnn = NULL; 89560852Sdarrenr 89660852Sdarrenr KMALLOC(nat, nat_t *); 89764580Sdarrenr if (nat == NULL) { 89864580Sdarrenr error = EFAULT; 89964580Sdarrenr goto junkput; 90064580Sdarrenr } 90160852Sdarrenr 90260852Sdarrenr bcopy((char *)&ipn.ipn_nat, (char *)nat, sizeof(*nat)); 90360852Sdarrenr /* 90460852Sdarrenr * Initialize all these so that nat_delete() doesn't cause a crash. 90560852Sdarrenr */ 90667614Sdarrenr nat->nat_phnext[0] = NULL; 90767614Sdarrenr nat->nat_phnext[1] = NULL; 90860852Sdarrenr fr = nat->nat_fr; 90960852Sdarrenr nat->nat_fr = NULL; 91060852Sdarrenr aps = nat->nat_aps; 91160852Sdarrenr nat->nat_aps = NULL; 91260852Sdarrenr in = nat->nat_ptr; 91360852Sdarrenr nat->nat_ptr = NULL; 91487394Sguido nat->nat_hm = NULL; 91560852Sdarrenr nat->nat_data = NULL; 91692685Sdarrenr nat->nat_ifp = GETUNIT(nat->nat_ifname, 4); 91760852Sdarrenr 91860852Sdarrenr /* 91960852Sdarrenr * Restore the rule associated with this nat session 92060852Sdarrenr */ 92160852Sdarrenr if (in) { 92260852Sdarrenr KMALLOC(in, ipnat_t *); 92360852Sdarrenr if (in == NULL) { 92460852Sdarrenr error = ENOMEM; 92560852Sdarrenr goto junkput; 92660852Sdarrenr } 92760852Sdarrenr nat->nat_ptr = in; 92860852Sdarrenr bcopy((char *)&ipn.ipn_ipnat, (char *)in, sizeof(*in)); 92960852Sdarrenr in->in_use = 1; 93060852Sdarrenr in->in_flags |= IPN_DELETE; 93160852Sdarrenr in->in_next = NULL; 93260852Sdarrenr in->in_rnext = NULL; 93360852Sdarrenr in->in_prnext = NULL; 93460852Sdarrenr in->in_mnext = NULL; 93560852Sdarrenr in->in_pmnext = NULL; 93660852Sdarrenr in->in_ifp = GETUNIT(in->in_ifname, 4); 93760852Sdarrenr if (in->in_plabel[0] != '\0') { 93892685Sdarrenr in->in_apr = appr_lookup(in->in_p, in->in_plabel); 93960852Sdarrenr } 94060852Sdarrenr } 94160852Sdarrenr 94260852Sdarrenr /* 94360852Sdarrenr * Restore ap_session_t structure. Include the private data allocated 94460852Sdarrenr * if it was there. 94560852Sdarrenr */ 94660852Sdarrenr if (aps) { 94760852Sdarrenr KMALLOC(aps, ap_session_t *); 94860852Sdarrenr if (aps == NULL) { 94960852Sdarrenr error = ENOMEM; 95060852Sdarrenr goto junkput; 95160852Sdarrenr } 95260852Sdarrenr nat->nat_aps = aps; 95360852Sdarrenr aps->aps_next = ap_sess_list; 95460852Sdarrenr ap_sess_list = aps; 95560852Sdarrenr bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps)); 95660852Sdarrenr if (in) 95760852Sdarrenr aps->aps_apr = in->in_apr; 95860852Sdarrenr if (aps->aps_psiz) { 95960852Sdarrenr KMALLOCS(aps->aps_data, void *, aps->aps_psiz); 96060852Sdarrenr if (aps->aps_data == NULL) { 96160852Sdarrenr error = ENOMEM; 96260852Sdarrenr goto junkput; 96360852Sdarrenr } 96460852Sdarrenr bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data, 96560852Sdarrenr aps->aps_psiz); 96660852Sdarrenr } else { 96760852Sdarrenr aps->aps_psiz = 0; 96860852Sdarrenr aps->aps_data = NULL; 96960852Sdarrenr } 97060852Sdarrenr } 97160852Sdarrenr 97260852Sdarrenr /* 97360852Sdarrenr * If there was a filtering rule associated with this entry then 97460852Sdarrenr * build up a new one. 97560852Sdarrenr */ 97660852Sdarrenr if (fr != NULL) { 97760852Sdarrenr if (nat->nat_flags & FI_NEWFR) { 97860852Sdarrenr KMALLOC(fr, frentry_t *); 97960852Sdarrenr nat->nat_fr = fr; 98060852Sdarrenr if (fr == NULL) { 98160852Sdarrenr error = ENOMEM; 98260852Sdarrenr goto junkput; 98360852Sdarrenr } 98460852Sdarrenr bcopy((char *)&ipn.ipn_fr, (char *)fr, sizeof(*fr)); 98560852Sdarrenr ipn.ipn_nat.nat_fr = fr; 98660852Sdarrenr error = IWCOPY((caddr_t)&ipn, ipnp, sizeof(ipn)); 98760852Sdarrenr if (error) { 98860852Sdarrenr error = EFAULT; 98960852Sdarrenr goto junkput; 99060852Sdarrenr } 99160852Sdarrenr } else { 99260852Sdarrenr for (n = nat_instances; n; n = n->nat_next) 99360852Sdarrenr if (n->nat_fr == fr) 99460852Sdarrenr break; 99560852Sdarrenr if (!n) { 99660852Sdarrenr error = ESRCH; 99760852Sdarrenr goto junkput; 99860852Sdarrenr } 99960852Sdarrenr } 100060852Sdarrenr } 100160852Sdarrenr 100260852Sdarrenr if (ipnn) 100360852Sdarrenr KFREES(ipnn, sizeof(ipn) + ipn.ipn_dsize); 100460852Sdarrenr nat_insert(nat); 100560852Sdarrenr return 0; 100660852Sdarrenrjunkput: 100760852Sdarrenr if (ipnn) 100860852Sdarrenr KFREES(ipnn, sizeof(ipn) + ipn.ipn_dsize); 100960852Sdarrenr if (nat) 101060852Sdarrenr nat_delete(nat); 101160852Sdarrenr return error; 101260852Sdarrenr} 101360852Sdarrenr 101460852Sdarrenr 101553642Sguido/* 101653642Sguido * Delete a nat entry from the various lists and table. 101753642Sguido */ 101853642Sguidostatic void nat_delete(natd) 101953642Sguidostruct nat *natd; 102053642Sguido{ 102153642Sguido struct ipnat *ipn; 102253642Sguido 102367614Sdarrenr if (natd->nat_flags & FI_WILDP) 102467853Sdarrenr nat_stats.ns_wilds--; 102567614Sdarrenr if (natd->nat_hnext[0]) 102667614Sdarrenr natd->nat_hnext[0]->nat_phnext[0] = natd->nat_phnext[0]; 102767614Sdarrenr *natd->nat_phnext[0] = natd->nat_hnext[0]; 102867614Sdarrenr if (natd->nat_hnext[1]) 102967614Sdarrenr natd->nat_hnext[1]->nat_phnext[1] = natd->nat_phnext[1]; 103067614Sdarrenr *natd->nat_phnext[1] = natd->nat_hnext[1]; 103192685Sdarrenr if (natd->nat_me != NULL) 103292685Sdarrenr *natd->nat_me = NULL; 103353642Sguido 103453642Sguido if (natd->nat_fr != NULL) { 103560852Sdarrenr ATOMIC_DEC32(natd->nat_fr->fr_ref); 103653642Sguido } 103760852Sdarrenr 103860852Sdarrenr if (natd->nat_hm != NULL) 103960852Sdarrenr nat_hostmapdel(natd->nat_hm); 104060852Sdarrenr 104153642Sguido /* 104253642Sguido * If there is an active reference from the nat entry to its parent 104353642Sguido * rule, decrement the rule's reference count and free it too if no 104453642Sguido * longer being used. 104553642Sguido */ 104653642Sguido ipn = natd->nat_ptr; 104753642Sguido if (ipn != NULL) { 104853642Sguido ipn->in_space++; 104953642Sguido ipn->in_use--; 105053642Sguido if (!ipn->in_use && (ipn->in_flags & IPN_DELETE)) { 105153642Sguido if (ipn->in_apr) 105253642Sguido appr_free(ipn->in_apr); 105353642Sguido KFREE(ipn); 105453642Sguido nat_stats.ns_rules--; 105553642Sguido } 105653642Sguido } 105753642Sguido 105860852Sdarrenr MUTEX_DESTROY(&natd->nat_lock); 105953642Sguido /* 106053642Sguido * If there's a fragment table entry too for this nat entry, then 106153642Sguido * dereference that as well. 106253642Sguido */ 106353642Sguido ipfr_forget((void *)natd); 106453642Sguido aps_free(natd->nat_aps); 106553642Sguido nat_stats.ns_inuse--; 106653642Sguido KFREE(natd); 106753642Sguido} 106853642Sguido 106953642Sguido 107053642Sguido/* 107153642Sguido * nat_flushtable - clear the NAT table of all mapping entries. 107292685Sdarrenr * (this is for the dynamic mappings) 107353642Sguido */ 107453642Sguidostatic int nat_flushtable() 107553642Sguido{ 107653642Sguido register nat_t *nat, **natp; 107753642Sguido register int j = 0; 107867614Sdarrenr 107953642Sguido /* 108053642Sguido * ALL NAT mappings deleted, so lets just make the deletions 108153642Sguido * quicker. 108253642Sguido */ 108353642Sguido if (nat_table[0] != NULL) 108453642Sguido bzero((char *)nat_table[0], 108553642Sguido sizeof(nat_table[0]) * ipf_nattable_sz); 108653642Sguido if (nat_table[1] != NULL) 108753642Sguido bzero((char *)nat_table[1], 108853642Sguido sizeof(nat_table[1]) * ipf_nattable_sz); 108953642Sguido 109053642Sguido for (natp = &nat_instances; (nat = *natp); ) { 109153642Sguido *natp = nat->nat_next; 109272006Sdarrenr#ifdef IPFILTER_LOG 109372006Sdarrenr nat_log(nat, NL_FLUSH); 109472006Sdarrenr#endif 109553642Sguido nat_delete(nat); 109653642Sguido j++; 109753642Sguido } 109853642Sguido nat_stats.ns_inuse = 0; 109953642Sguido return j; 110053642Sguido} 110153642Sguido 110253642Sguido 110353642Sguido/* 110453642Sguido * nat_clearlist - delete all rules in the active NAT mapping list. 110592685Sdarrenr * (this is for NAT/RDR rules) 110653642Sguido */ 110792685Sdarrenrint nat_clearlist() 110853642Sguido{ 110953642Sguido register ipnat_t *n, **np = &nat_list; 111053642Sguido int i = 0; 111153642Sguido 111253642Sguido if (nat_rules != NULL) 111353642Sguido bzero((char *)nat_rules, sizeof(*nat_rules) * ipf_natrules_sz); 111453642Sguido if (rdr_rules != NULL) 111553642Sguido bzero((char *)rdr_rules, sizeof(*rdr_rules) * ipf_rdrrules_sz); 111653642Sguido 111753642Sguido while ((n = *np)) { 111853642Sguido *np = n->in_next; 111953642Sguido if (!n->in_use) { 112053642Sguido if (n->in_apr) 112153642Sguido appr_free(n->in_apr); 112253642Sguido KFREE(n); 112353642Sguido nat_stats.ns_rules--; 112453642Sguido } else { 112553642Sguido n->in_flags |= IPN_DELETE; 112653642Sguido n->in_next = NULL; 112753642Sguido } 112853642Sguido i++; 112953642Sguido } 113053642Sguido nat_masks = 0; 113153642Sguido rdr_masks = 0; 113253642Sguido return i; 113353642Sguido} 113453642Sguido 113553642Sguido 113653642Sguido/* 113753642Sguido * Create a new NAT table entry. 113892685Sdarrenr * NOTE: Assumes write lock on ipf_nat has been obtained already. 113992685Sdarrenr * If you intend on changing this, beware: appr_new() may call nat_new() 114092685Sdarrenr * recursively! 114153642Sguido */ 114292685Sdarrenrnat_t *nat_new(fin, ip, np, natsave, flags, direction) 114392685Sdarrenrfr_info_t *fin; 114492685Sdarrenrip_t *ip; 114553642Sguidoipnat_t *np; 114692685Sdarrenrnat_t **natsave; 114753642Sguidou_int flags; 114853642Sguidoint direction; 114953642Sguido{ 115053642Sguido register u_32_t sum1, sum2, sumd, l; 115153642Sguido u_short port = 0, sport = 0, dport = 0, nport = 0; 115253642Sguido struct in_addr in, inb; 115392685Sdarrenr u_short nflags, sp, dp; 115453642Sguido tcphdr_t *tcp = NULL; 115560852Sdarrenr hostmap_t *hm = NULL; 115660852Sdarrenr nat_t *nat, *natl; 115755929Sguido#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) 115855929Sguido qif_t *qf = fin->fin_qif; 115955929Sguido#endif 116053642Sguido 116153642Sguido nflags = flags & np->in_flags; 116253642Sguido if (flags & IPN_TCPUDP) { 116353642Sguido tcp = (tcphdr_t *)fin->fin_dp; 116492685Sdarrenr sport = htons(fin->fin_data[0]); 116592685Sdarrenr dport = htons(fin->fin_data[1]); 116653642Sguido } 116753642Sguido 116853642Sguido /* Give me a new nat */ 116953642Sguido KMALLOC(nat, nat_t *); 117060852Sdarrenr if (nat == NULL) { 117160852Sdarrenr nat_stats.ns_memfail++; 117253642Sguido return NULL; 117360852Sdarrenr } 117453642Sguido 117553642Sguido bzero((char *)nat, sizeof(*nat)); 117653642Sguido nat->nat_flags = flags; 117767614Sdarrenr if (flags & FI_WILDP) 117867853Sdarrenr nat_stats.ns_wilds++; 117953642Sguido /* 118053642Sguido * Search the current table for a match. 118153642Sguido */ 118253642Sguido if (direction == NAT_OUTBOUND) { 118353642Sguido /* 118453642Sguido * Values at which the search for a free resouce starts. 118553642Sguido */ 118653642Sguido u_32_t st_ip; 118753642Sguido u_short st_port; 118853642Sguido 118953642Sguido /* 119053642Sguido * If it's an outbound packet which doesn't match any existing 119153642Sguido * record, then create a new port 119253642Sguido */ 119353642Sguido l = 0; 119453642Sguido st_ip = np->in_nip; 119553642Sguido st_port = np->in_pnext; 119653642Sguido 119753642Sguido do { 119853642Sguido port = 0; 119960852Sdarrenr in.s_addr = htonl(np->in_nip); 120053642Sguido if (l == 0) { 120157096Sguido /* 120257096Sguido * Check to see if there is an existing NAT 120357096Sguido * setup for this IP address pair. 120457096Sguido */ 120592685Sdarrenr hm = nat_hostmap(np, fin->fin_src, in); 120660852Sdarrenr if (hm != NULL) 120760852Sdarrenr in.s_addr = hm->hm_mapip.s_addr; 120860852Sdarrenr } else if ((l == 1) && (hm != NULL)) { 120960852Sdarrenr nat_hostmapdel(hm); 121060852Sdarrenr hm = NULL; 121153642Sguido } 121260852Sdarrenr in.s_addr = ntohl(in.s_addr); 121353642Sguido 121460852Sdarrenr nat->nat_hm = hm; 121560852Sdarrenr 121653642Sguido if ((np->in_outmsk == 0xffffffff) && 121753642Sguido (np->in_pnext == 0)) { 121860852Sdarrenr if (l > 0) 121960852Sdarrenr goto badnat; 122053642Sguido } 122153642Sguido 122253642Sguido if (np->in_redir & NAT_MAPBLK) { 122353642Sguido if ((l >= np->in_ppip) || ((l > 0) && 122460852Sdarrenr !(flags & IPN_TCPUDP))) 122560852Sdarrenr goto badnat; 122653642Sguido /* 122753642Sguido * map-block - Calculate destination address. 122853642Sguido */ 122992685Sdarrenr in.s_addr = ntohl(fin->fin_saddr); 123053642Sguido in.s_addr &= ntohl(~np->in_inmsk); 123153642Sguido inb.s_addr = in.s_addr; 123253642Sguido in.s_addr /= np->in_ippip; 123353642Sguido in.s_addr &= ntohl(~np->in_outmsk); 123453642Sguido in.s_addr += ntohl(np->in_outip); 123553642Sguido /* 123653642Sguido * Calculate destination port. 123753642Sguido */ 123853642Sguido if ((flags & IPN_TCPUDP) && 123953642Sguido (np->in_ppip != 0)) { 124053642Sguido port = ntohs(sport) + l; 124153642Sguido port %= np->in_ppip; 124253642Sguido port += np->in_ppip * 124353642Sguido (inb.s_addr % np->in_ippip); 124453642Sguido port += MAPBLK_MINPORT; 124553642Sguido port = htons(port); 124653642Sguido } 124760852Sdarrenr } else if (!np->in_outip && 124853642Sguido (np->in_outmsk == 0xffffffff)) { 124953642Sguido /* 125053642Sguido * 0/32 - use the interface's IP address. 125153642Sguido */ 125253642Sguido if ((l > 0) || 125360852Sdarrenr fr_ifpaddr(4, fin->fin_ifp, &in) == -1) 125460852Sdarrenr goto badnat; 125555929Sguido in.s_addr = ntohl(in.s_addr); 125660852Sdarrenr } else if (!np->in_outip && !np->in_outmsk) { 125753642Sguido /* 125853642Sguido * 0/0 - use the original source address/port. 125953642Sguido */ 126060852Sdarrenr if (l > 0) 126160852Sdarrenr goto badnat; 126292685Sdarrenr in.s_addr = ntohl(fin->fin_saddr); 126353642Sguido } else if ((np->in_outmsk != 0xffffffff) && 126453642Sguido (np->in_pnext == 0) && 126560852Sdarrenr ((l > 0) || (hm == NULL))) 126653642Sguido np->in_nip++; 126753642Sguido natl = NULL; 126853642Sguido 126953642Sguido if ((nflags & IPN_TCPUDP) && 127053642Sguido ((np->in_redir & NAT_MAPBLK) == 0) && 127153642Sguido (np->in_flags & IPN_AUTOPORTMAP)) { 127253642Sguido if ((l > 0) && (l % np->in_ppip == 0)) { 127353642Sguido if (l > np->in_space) { 127460852Sdarrenr goto badnat; 127553642Sguido } else if ((l > np->in_ppip) && 127653642Sguido np->in_outmsk != 0xffffffff) 127753642Sguido np->in_nip++; 127853642Sguido } 127953642Sguido if (np->in_ppip != 0) { 128053642Sguido port = ntohs(sport); 128153642Sguido port += (l % np->in_ppip); 128253642Sguido port %= np->in_ppip; 128353642Sguido port += np->in_ppip * 128492685Sdarrenr (ntohl(fin->fin_saddr) % 128553642Sguido np->in_ippip); 128653642Sguido port += MAPBLK_MINPORT; 128753642Sguido port = htons(port); 128853642Sguido } 128953642Sguido } else if (((np->in_redir & NAT_MAPBLK) == 0) && 129053642Sguido (nflags & IPN_TCPUDP) && 129153642Sguido (np->in_pnext != 0)) { 129253642Sguido port = htons(np->in_pnext++); 129353642Sguido if (np->in_pnext > ntohs(np->in_pmax)) { 129453642Sguido np->in_pnext = ntohs(np->in_pmin); 129553642Sguido if (np->in_outmsk != 0xffffffff) 129653642Sguido np->in_nip++; 129753642Sguido } 129853642Sguido } 129953642Sguido 130060852Sdarrenr if (np->in_flags & IPN_IPRANGE) { 130160852Sdarrenr if (np->in_nip > ntohl(np->in_outmsk)) 130253642Sguido np->in_nip = ntohl(np->in_outip); 130353642Sguido } else { 130453642Sguido if ((np->in_outmsk != 0xffffffff) && 130553642Sguido ((np->in_nip + 1) & ntohl(np->in_outmsk)) > 130653642Sguido ntohl(np->in_outip)) 130753642Sguido np->in_nip = ntohl(np->in_outip) + 1; 130853642Sguido } 130953642Sguido 131053642Sguido if (!port && (flags & IPN_TCPUDP)) 131153642Sguido port = sport; 131253642Sguido 131353642Sguido /* 131453642Sguido * Here we do a lookup of the connection as seen from 131553642Sguido * the outside. If an IP# pair already exists, try 131653642Sguido * again. So if you have A->B becomes C->B, you can 131753642Sguido * also have D->E become C->E but not D->B causing 131853642Sguido * another C->B. Also take protocol and ports into 131953642Sguido * account when determining whether a pre-existing 132053642Sguido * NAT setup will cause an external conflict where 132153642Sguido * this is appropriate. 132253642Sguido */ 132353642Sguido inb.s_addr = htonl(in.s_addr); 132492685Sdarrenr sp = fin->fin_data[0]; 132592685Sdarrenr dp = fin->fin_data[1]; 132692685Sdarrenr fin->fin_data[0] = fin->fin_data[1]; 132792685Sdarrenr fin->fin_data[1] = htons(port); 132892685Sdarrenr natl = nat_inlookup(fin, flags & ~FI_WILDP, 132992685Sdarrenr (u_int)fin->fin_p, fin->fin_dst, 133092685Sdarrenr inb, 1); 133192685Sdarrenr fin->fin_data[0] = sp; 133292685Sdarrenr fin->fin_data[1] = dp; 133353642Sguido 133453642Sguido /* 133553642Sguido * Has the search wrapped around and come back to the 133653642Sguido * start ? 133753642Sguido */ 133853642Sguido if ((natl != NULL) && 133953642Sguido (np->in_pnext != 0) && (st_port == np->in_pnext) && 134060852Sdarrenr (np->in_nip != 0) && (st_ip == np->in_nip)) 134160852Sdarrenr goto badnat; 134253642Sguido l++; 134353642Sguido } while (natl != NULL); 134453642Sguido 134553642Sguido if (np->in_space > 0) 134653642Sguido np->in_space--; 134753642Sguido 134853642Sguido /* Setup the NAT table */ 134992685Sdarrenr nat->nat_inip = fin->fin_src; 135053642Sguido nat->nat_outip.s_addr = htonl(in.s_addr); 135192685Sdarrenr nat->nat_oip = fin->fin_dst; 135260852Sdarrenr if (nat->nat_hm == NULL) 135392685Sdarrenr nat->nat_hm = nat_hostmap(np, fin->fin_src, 135460852Sdarrenr nat->nat_outip); 135553642Sguido 135692685Sdarrenr sum1 = LONG_SUM(ntohl(fin->fin_saddr)) + ntohs(sport); 135753642Sguido sum2 = LONG_SUM(in.s_addr) + ntohs(port); 135853642Sguido 135953642Sguido if (flags & IPN_TCPUDP) { 136053642Sguido nat->nat_inport = sport; 136153642Sguido nat->nat_outport = port; /* sport */ 136253642Sguido nat->nat_oport = dport; 136353642Sguido } 136453642Sguido } else { 136553642Sguido /* 136653642Sguido * Otherwise, it's an inbound packet. Most likely, we don't 136753642Sguido * want to rewrite source ports and source addresses. Instead, 136853642Sguido * we want to rewrite to a fixed internal address and fixed 136953642Sguido * internal port. 137053642Sguido */ 137160852Sdarrenr if (np->in_flags & IPN_SPLIT) { 137260852Sdarrenr in.s_addr = np->in_nip; 137360852Sdarrenr if (np->in_inip == htonl(in.s_addr)) 137460852Sdarrenr np->in_nip = ntohl(np->in_inmsk); 137560852Sdarrenr else { 137660852Sdarrenr np->in_nip = ntohl(np->in_inip); 137760852Sdarrenr if (np->in_flags & IPN_ROUNDR) { 137860852Sdarrenr nat_delrdr(np); 137960852Sdarrenr nat_addrdr(np); 138060852Sdarrenr } 138160852Sdarrenr } 138260852Sdarrenr } else { 138360852Sdarrenr in.s_addr = ntohl(np->in_inip); 138460852Sdarrenr if (np->in_flags & IPN_ROUNDR) { 138560852Sdarrenr nat_delrdr(np); 138660852Sdarrenr nat_addrdr(np); 138760852Sdarrenr } 138860852Sdarrenr } 138960852Sdarrenr if (!np->in_pnext) 139053642Sguido nport = dport; 139160852Sdarrenr else { 139260852Sdarrenr /* 139360852Sdarrenr * Whilst not optimized for the case where 139460852Sdarrenr * pmin == pmax, the gain is not significant. 139560852Sdarrenr */ 139692685Sdarrenr if (np->in_pmin != np->in_pmax) { 139792685Sdarrenr nport = ntohs(dport) - ntohs(np->in_pmin) + 139892685Sdarrenr ntohs(np->in_pnext); 139992685Sdarrenr nport = ntohs(nport); 140092685Sdarrenr } else 140192685Sdarrenr nport = np->in_pnext; 140260852Sdarrenr } 140353642Sguido 140453642Sguido /* 140553642Sguido * When the redirect-to address is set to 0.0.0.0, just 140692685Sdarrenr * assume a blank `forwarding' of the packet. 140753642Sguido */ 140892685Sdarrenr if (in.s_addr == 0) 140992685Sdarrenr in.s_addr = ntohl(fin->fin_daddr); 141053642Sguido 141153642Sguido nat->nat_inip.s_addr = htonl(in.s_addr); 141292685Sdarrenr nat->nat_outip = fin->fin_dst; 141392685Sdarrenr nat->nat_oip = fin->fin_src; 141453642Sguido 141592685Sdarrenr sum1 = LONG_SUM(ntohl(fin->fin_daddr)) + ntohs(dport); 141653642Sguido sum2 = LONG_SUM(in.s_addr) + ntohs(nport); 141753642Sguido 141853642Sguido if (flags & IPN_TCPUDP) { 141953642Sguido nat->nat_inport = nport; 142053642Sguido nat->nat_outport = dport; 142153642Sguido nat->nat_oport = sport; 142253642Sguido } 142353642Sguido } 142453642Sguido 142553642Sguido CALC_SUMD(sum1, sum2, sumd); 142655929Sguido nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 142755929Sguido#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) 142880482Sdarrenr if ((flags & IPN_TCPUDP) && dohwcksum && 142955929Sguido (qf->qf_ill->ill_ick.ick_magic == ICK_M_CTL_MAGIC)) { 143055929Sguido if (direction == NAT_OUTBOUND) 143155929Sguido sum1 = LONG_SUM(ntohl(in.s_addr)); 143255929Sguido else 143392685Sdarrenr sum1 = LONG_SUM(ntohl(fin->fin_saddr)); 143492685Sdarrenr sum1 += LONG_SUM(ntohl(fin->fin_daddr)); 143580482Sdarrenr sum1 += IPPROTO_TCP; 143655929Sguido sum1 = (sum1 & 0xffff) + (sum1 >> 16); 143755929Sguido nat->nat_sumd[1] = NAT_HW_CKSUM|(sum1 & 0xffff); 143855929Sguido } else 143955929Sguido#endif 144055929Sguido nat->nat_sumd[1] = nat->nat_sumd[0]; 144153642Sguido 144253642Sguido if ((flags & IPN_TCPUDP) && ((sport != port) || (dport != nport))) { 144353642Sguido if (direction == NAT_OUTBOUND) 144492685Sdarrenr sum1 = LONG_SUM(ntohl(fin->fin_saddr)); 144553642Sguido else 144692685Sdarrenr sum1 = LONG_SUM(ntohl(fin->fin_daddr)); 144753642Sguido 144853642Sguido sum2 = LONG_SUM(in.s_addr); 144953642Sguido 145053642Sguido CALC_SUMD(sum1, sum2, sumd); 145153642Sguido nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); 145253642Sguido } else 145355929Sguido nat->nat_ipsumd = nat->nat_sumd[0]; 145453642Sguido 145553642Sguido in.s_addr = htonl(in.s_addr); 145660852Sdarrenr 145760852Sdarrenr strncpy(nat->nat_ifname, IFNAME(fin->fin_ifp), IFNAMSIZ); 145860852Sdarrenr 145992685Sdarrenr nat->nat_me = natsave; 146053642Sguido nat->nat_dir = direction; 146153642Sguido nat->nat_ifp = fin->fin_ifp; 146253642Sguido nat->nat_ptr = np; 146392685Sdarrenr nat->nat_p = fin->fin_p; 146453642Sguido nat->nat_bytes = 0; 146553642Sguido nat->nat_pkts = 0; 146653642Sguido nat->nat_fr = fin->fin_fr; 146753642Sguido if (nat->nat_fr != NULL) { 146860852Sdarrenr ATOMIC_INC32(nat->nat_fr->fr_ref); 146953642Sguido } 147053642Sguido if (direction == NAT_OUTBOUND) { 147153642Sguido if (flags & IPN_TCPUDP) 147253642Sguido tcp->th_sport = port; 147353642Sguido } else { 147453642Sguido if (flags & IPN_TCPUDP) 147553642Sguido tcp->th_dport = nport; 147653642Sguido } 147792685Sdarrenr 147892685Sdarrenr nat_insert(nat); 147992685Sdarrenr 148092685Sdarrenr if ((np->in_apr != NULL) && (np->in_dport == 0 || 148192685Sdarrenr (tcp != NULL && dport == np->in_dport))) 148292685Sdarrenr (void) appr_new(fin, ip, nat); 148392685Sdarrenr 148453642Sguido np->in_use++; 148572006Sdarrenr#ifdef IPFILTER_LOG 148672006Sdarrenr nat_log(nat, (u_int)np->in_redir); 148772006Sdarrenr#endif 148853642Sguido return nat; 148960852Sdarrenrbadnat: 149060852Sdarrenr nat_stats.ns_badnat++; 149160852Sdarrenr if ((hm = nat->nat_hm) != NULL) 149260852Sdarrenr nat_hostmapdel(hm); 149360852Sdarrenr KFREE(nat); 149460852Sdarrenr return NULL; 149553642Sguido} 149653642Sguido 149753642Sguido 149892685Sdarrenr/* 149992685Sdarrenr * Insert a NAT entry into the hash tables for searching and add it to the 150092685Sdarrenr * list of active NAT entries. Adjust global counters when complete. 150192685Sdarrenr */ 150260852Sdarrenrvoid nat_insert(nat) 150360852Sdarrenrnat_t *nat; 150460852Sdarrenr{ 150580482Sdarrenr u_int hv1, hv2; 150660852Sdarrenr nat_t **natp; 150760852Sdarrenr 150860852Sdarrenr MUTEX_INIT(&nat->nat_lock, "nat entry lock", NULL); 150960852Sdarrenr 151060852Sdarrenr nat->nat_age = fr_defnatage; 151160852Sdarrenr nat->nat_ifname[sizeof(nat->nat_ifname) - 1] = '\0'; 151260852Sdarrenr if (nat->nat_ifname[0] !='\0') { 151360852Sdarrenr nat->nat_ifp = GETUNIT(nat->nat_ifname, 4); 151460852Sdarrenr } 151560852Sdarrenr 151660852Sdarrenr nat->nat_next = nat_instances; 151760852Sdarrenr nat_instances = nat; 151867614Sdarrenr 151980482Sdarrenr if (!(nat->nat_flags & (FI_W_SPORT|FI_W_DPORT))) { 152080482Sdarrenr hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 152180482Sdarrenr 0xffffffff); 152280482Sdarrenr hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1 + nat->nat_oport, 152380482Sdarrenr ipf_nattable_sz); 152480482Sdarrenr hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, 152580482Sdarrenr 0xffffffff); 152680482Sdarrenr hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2 + nat->nat_oport, 152780482Sdarrenr ipf_nattable_sz); 152880482Sdarrenr } else { 152992685Sdarrenr hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, nat->nat_inip.s_addr, 153092685Sdarrenr ipf_nattable_sz); 153192685Sdarrenr hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, nat->nat_outip.s_addr, 153292685Sdarrenr ipf_nattable_sz); 153380482Sdarrenr } 153480482Sdarrenr 153580482Sdarrenr natp = &nat_table[0][hv1]; 153667614Sdarrenr if (*natp) 153767614Sdarrenr (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 153867614Sdarrenr nat->nat_phnext[0] = natp; 153960852Sdarrenr nat->nat_hnext[0] = *natp; 154060852Sdarrenr *natp = nat; 154167614Sdarrenr 154280482Sdarrenr natp = &nat_table[1][hv2]; 154367614Sdarrenr if (*natp) 154467614Sdarrenr (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 154567614Sdarrenr nat->nat_phnext[1] = natp; 154660852Sdarrenr nat->nat_hnext[1] = *natp; 154760852Sdarrenr *natp = nat; 154860852Sdarrenr 154960852Sdarrenr nat_stats.ns_added++; 155060852Sdarrenr nat_stats.ns_inuse++; 155160852Sdarrenr} 155260852Sdarrenr 155360852Sdarrenr 155460852Sdarrenrnat_t *nat_icmplookup(ip, fin, dir) 155553642Sguidoip_t *ip; 155653642Sguidofr_info_t *fin; 155760852Sdarrenrint dir; 155853642Sguido{ 155953642Sguido icmphdr_t *icmp; 156053642Sguido tcphdr_t *tcp = NULL; 156153642Sguido ip_t *oip; 156264580Sdarrenr int flags = 0, type, minlen; 156353642Sguido 156453642Sguido icmp = (icmphdr_t *)fin->fin_dp; 156553642Sguido /* 156653642Sguido * Does it at least have the return (basic) IP header ? 156753642Sguido * Only a basic IP header (no options) should be with an ICMP error 156853642Sguido * header. 156953642Sguido */ 157053642Sguido if ((ip->ip_hl != 5) || (ip->ip_len < ICMPERR_MINPKTLEN)) 157153642Sguido return NULL; 157253642Sguido type = icmp->icmp_type; 157353642Sguido /* 157453642Sguido * If it's not an error type, then return. 157553642Sguido */ 157653642Sguido if ((type != ICMP_UNREACH) && (type != ICMP_SOURCEQUENCH) && 157753642Sguido (type != ICMP_REDIRECT) && (type != ICMP_TIMXCEED) && 157853642Sguido (type != ICMP_PARAMPROB)) 157953642Sguido return NULL; 158053642Sguido 158153642Sguido oip = (ip_t *)((char *)fin->fin_dp + 8); 158264580Sdarrenr minlen = (oip->ip_hl << 2); 158364580Sdarrenr if (minlen < sizeof(ip_t)) 158453642Sguido return NULL; 158564580Sdarrenr if (ip->ip_len < ICMPERR_IPICMPHLEN + minlen) 158664580Sdarrenr return NULL; 158764580Sdarrenr /* 158864580Sdarrenr * Is the buffer big enough for all of it ? It's the size of the IP 158964580Sdarrenr * header claimed in the encapsulated part which is of concern. It 159064580Sdarrenr * may be too big to be in this buffer but not so big that it's 159164580Sdarrenr * outside the ICMP packet, leading to TCP deref's causing problems. 159264580Sdarrenr * This is possible because we don't know how big oip_hl is when we 159364580Sdarrenr * do the pullup early in fr_check() and thus can't gaurantee it is 159464580Sdarrenr * all here now. 159564580Sdarrenr */ 159664580Sdarrenr#ifdef _KERNEL 159764580Sdarrenr { 159864580Sdarrenr mb_t *m; 159964580Sdarrenr 160064580Sdarrenr# if SOLARIS 160164580Sdarrenr m = fin->fin_qfm; 160264580Sdarrenr if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr) 160364580Sdarrenr return NULL; 160464580Sdarrenr# else 160564580Sdarrenr m = *(mb_t **)fin->fin_mp; 160664580Sdarrenr if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > 160764580Sdarrenr (char *)ip + m->m_len) 160864580Sdarrenr return NULL; 160964580Sdarrenr# endif 161064580Sdarrenr } 161164580Sdarrenr#endif 161264580Sdarrenr 161353642Sguido if (oip->ip_p == IPPROTO_TCP) 161453642Sguido flags = IPN_TCP; 161553642Sguido else if (oip->ip_p == IPPROTO_UDP) 161653642Sguido flags = IPN_UDP; 161753642Sguido if (flags & IPN_TCPUDP) { 161892685Sdarrenr u_short data[2]; 161992685Sdarrenr nat_t *nat; 162092685Sdarrenr 162164580Sdarrenr minlen += 8; /* + 64bits of data to get ports */ 162264580Sdarrenr if (ip->ip_len < ICMPERR_IPICMPHLEN + minlen) 162364580Sdarrenr return NULL; 162492685Sdarrenr 162592685Sdarrenr data[0] = fin->fin_data[0]; 162692685Sdarrenr data[1] = fin->fin_data[1]; 162753642Sguido tcp = (tcphdr_t *)((char *)oip + (oip->ip_hl << 2)); 162892685Sdarrenr fin->fin_data[0] = ntohs(tcp->th_dport); 162992685Sdarrenr fin->fin_data[1] = ntohs(tcp->th_sport); 163092685Sdarrenr 163192685Sdarrenr if (dir == NAT_INBOUND) { 163292685Sdarrenr nat = nat_inlookup(fin, flags, (u_int)oip->ip_p, 163392685Sdarrenr oip->ip_dst, oip->ip_src, 0); 163492685Sdarrenr } else { 163592685Sdarrenr nat = nat_outlookup(fin, flags, (u_int)oip->ip_p, 163692685Sdarrenr oip->ip_dst, oip->ip_src, 0); 163792685Sdarrenr } 163892685Sdarrenr fin->fin_data[0] = data[0]; 163992685Sdarrenr fin->fin_data[1] = data[1]; 164092685Sdarrenr return nat; 164153642Sguido } 164260852Sdarrenr if (dir == NAT_INBOUND) 164392685Sdarrenr return nat_inlookup(fin, 0, (u_int)oip->ip_p, 164492685Sdarrenr oip->ip_dst, oip->ip_src, 0); 164560852Sdarrenr else 164692685Sdarrenr return nat_outlookup(fin, 0, (u_int)oip->ip_p, 164792685Sdarrenr oip->ip_dst, oip->ip_src, 0); 164853642Sguido} 164953642Sguido 165053642Sguido 165153642Sguido/* 165253642Sguido * This should *ONLY* be used for incoming packets to make sure a NAT'd ICMP 165353642Sguido * packet gets correctly recognised. 165453642Sguido */ 165560852Sdarrenrnat_t *nat_icmp(ip, fin, nflags, dir) 165653642Sguidoip_t *ip; 165753642Sguidofr_info_t *fin; 165853642Sguidou_int *nflags; 165960852Sdarrenrint dir; 166053642Sguido{ 166172006Sdarrenr u_32_t sum1, sum2, sumd, sumd2 = 0; 166253642Sguido struct in_addr in; 166395418Sdarrenr int flags, dlen; 166453642Sguido icmphdr_t *icmp; 166567614Sdarrenr udphdr_t *udp; 166695418Sdarrenr tcphdr_t *tcp; 166753642Sguido nat_t *nat; 166853642Sguido ip_t *oip; 166953642Sguido 167080482Sdarrenr if ((fin->fin_fl & FI_SHORT) || (fin->fin_off != 0)) 167163523Sdarrenr return NULL; 167267614Sdarrenr /* 167367614Sdarrenr * nat_icmplookup() will return NULL for `defective' packets. 167467614Sdarrenr */ 167560852Sdarrenr if ((ip->ip_v != 4) || !(nat = nat_icmplookup(ip, fin, dir))) 167653642Sguido return NULL; 167792685Sdarrenr 167892685Sdarrenr flags = 0; 167953642Sguido *nflags = IPN_ICMPERR; 168053642Sguido icmp = (icmphdr_t *)fin->fin_dp; 168153642Sguido oip = (ip_t *)&icmp->icmp_ip; 168253642Sguido if (oip->ip_p == IPPROTO_TCP) 168353642Sguido flags = IPN_TCP; 168453642Sguido else if (oip->ip_p == IPPROTO_UDP) 168553642Sguido flags = IPN_UDP; 168667614Sdarrenr udp = (udphdr_t *)((((char *)oip) + (oip->ip_hl << 2))); 168795418Sdarrenr dlen = ip->ip_len - ((char *)udp - (char *)ip); 168853642Sguido /* 168995418Sdarrenr * XXX - what if this is bogus hl and we go off the end ? 169095418Sdarrenr * In this case, nat_icmplookup() will have returned NULL. 169195418Sdarrenr */ 169295418Sdarrenr tcp = (tcphdr_t *)udp; 169395418Sdarrenr 169495418Sdarrenr /* 169553642Sguido * Need to adjust ICMP header to include the real IP#'s and 169653642Sguido * port #'s. Only apply a checksum change relative to the 169767614Sdarrenr * IP address change as it will be modified again in ip_natout 169853642Sguido * for both address and port. Two checksum changes are 169953642Sguido * necessary for the two header address changes. Be careful 170053642Sguido * to only modify the checksum once for the port # and twice 170153642Sguido * for the IP#. 170253642Sguido */ 170360852Sdarrenr 170467614Sdarrenr /* 170567614Sdarrenr * Step 1 170667614Sdarrenr * Fix the IP addresses in the offending IP packet. You also need 170767614Sdarrenr * to adjust the IP header checksum of that offending IP packet 170867614Sdarrenr * and the ICMP checksum of the ICMP error message itself. 170967614Sdarrenr * 171067614Sdarrenr * Unfortunately, for UDP and TCP, the IP addresses are also contained 171167614Sdarrenr * in the pseudo header that is used to compute the UDP resp. TCP 171267614Sdarrenr * checksum. So, we must compensate that as well. Even worse, the 171367614Sdarrenr * change in the UDP and TCP checksums require yet another 171467614Sdarrenr * adjustment of the ICMP checksum of the ICMP error message. 171567614Sdarrenr * 171667614Sdarrenr */ 171767614Sdarrenr 171872006Sdarrenr if (oip->ip_dst.s_addr == nat->nat_oip.s_addr) { 171953642Sguido sum1 = LONG_SUM(ntohl(oip->ip_src.s_addr)); 172053642Sguido in = nat->nat_inip; 172153642Sguido oip->ip_src = in; 172253642Sguido } else { 172353642Sguido sum1 = LONG_SUM(ntohl(oip->ip_dst.s_addr)); 172453642Sguido in = nat->nat_outip; 172553642Sguido oip->ip_dst = in; 172653642Sguido } 172753642Sguido 172853642Sguido sum2 = LONG_SUM(ntohl(in.s_addr)); 172953642Sguido 173053642Sguido CALC_SUMD(sum1, sum2, sumd); 173153642Sguido 173253642Sguido if (nat->nat_dir == NAT_OUTBOUND) { 173367614Sdarrenr /* 173467614Sdarrenr * Fix IP checksum of the offending IP packet to adjust for 173567614Sdarrenr * the change in the IP address. 173667614Sdarrenr * 173767614Sdarrenr * Normally, you would expect that the ICMP checksum of the 173867614Sdarrenr * ICMP error message needs to be adjusted as well for the 173967614Sdarrenr * IP address change in oip. 174067614Sdarrenr * However, this is a NOP, because the ICMP checksum is 174167614Sdarrenr * calculated over the complete ICMP packet, which includes the 174267614Sdarrenr * changed oip IP addresses and oip->ip_sum. However, these 174367614Sdarrenr * two changes cancel each other out (if the delta for 174467614Sdarrenr * the IP address is x, then the delta for ip_sum is minus x), 174567614Sdarrenr * so no change in the icmp_cksum is necessary. 174667614Sdarrenr * 174767614Sdarrenr * Be careful that nat_dir refers to the direction of the 174867614Sdarrenr * offending IP packet (oip), not to its ICMP response (icmp) 174967614Sdarrenr */ 175067614Sdarrenr fix_datacksum(&oip->ip_sum, sumd); 175153642Sguido 175267614Sdarrenr /* 175367614Sdarrenr * Fix UDP pseudo header checksum to compensate for the 175467614Sdarrenr * IP address change. 175567614Sdarrenr */ 175667614Sdarrenr if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) { 175767614Sdarrenr /* 175867614Sdarrenr * The UDP checksum is optional, only adjust it 175967614Sdarrenr * if it has been set. 176067614Sdarrenr */ 176167614Sdarrenr sum1 = ntohs(udp->uh_sum); 176267614Sdarrenr fix_datacksum(&udp->uh_sum, sumd); 176367614Sdarrenr sum2 = ntohs(udp->uh_sum); 176467614Sdarrenr 176567614Sdarrenr /* 176667614Sdarrenr * Fix ICMP checksum to compensate the UDP 176767614Sdarrenr * checksum adjustment. 176867614Sdarrenr */ 176967614Sdarrenr CALC_SUMD(sum1, sum2, sumd); 177072006Sdarrenr sumd2 = sumd; 177167614Sdarrenr } 177267614Sdarrenr 177395418Sdarrenr#if 1 177467614Sdarrenr /* 177567614Sdarrenr * Fix TCP pseudo header checksum to compensate for the 177667614Sdarrenr * IP address change. Before we can do the change, we 177767614Sdarrenr * must make sure that oip is sufficient large to hold 177867614Sdarrenr * the TCP checksum (normally it does not!). 177967614Sdarrenr */ 178095418Sdarrenr if (oip->ip_p == IPPROTO_TCP && dlen >= 18) { 178167614Sdarrenr 178295418Sdarrenr sum1 = ntohs(tcp->th_sum); 178395418Sdarrenr fix_datacksum(&tcp->th_sum, sumd); 178495418Sdarrenr sum2 = ntohs(tcp->th_sum); 178595418Sdarrenr 178695418Sdarrenr /* 178795418Sdarrenr * Fix ICMP checksum to compensate the TCP 178895418Sdarrenr * checksum adjustment. 178995418Sdarrenr */ 179095418Sdarrenr CALC_SUMD(sum1, sum2, sumd); 179195418Sdarrenr sumd2 = sumd; 179267614Sdarrenr } 179367614Sdarrenr#endif 179453642Sguido } else { 179567614Sdarrenr 179667614Sdarrenr /* 179767614Sdarrenr * Fix IP checksum of the offending IP packet to adjust for 179867614Sdarrenr * the change in the IP address. 179967614Sdarrenr * 180067614Sdarrenr * Normally, you would expect that the ICMP checksum of the 180167614Sdarrenr * ICMP error message needs to be adjusted as well for the 180267614Sdarrenr * IP address change in oip. 180367614Sdarrenr * However, this is a NOP, because the ICMP checksum is 180467614Sdarrenr * calculated over the complete ICMP packet, which includes the 180567614Sdarrenr * changed oip IP addresses and oip->ip_sum. However, these 180667614Sdarrenr * two changes cancel each other out (if the delta for 180767614Sdarrenr * the IP address is x, then the delta for ip_sum is minus x), 180867614Sdarrenr * so no change in the icmp_cksum is necessary. 180967614Sdarrenr * 181067614Sdarrenr * Be careful that nat_dir refers to the direction of the 181167614Sdarrenr * offending IP packet (oip), not to its ICMP response (icmp) 181267614Sdarrenr */ 181367614Sdarrenr fix_datacksum(&oip->ip_sum, sumd); 181467614Sdarrenr 181567614Sdarrenr/* XXX FV : without having looked at Solaris source code, it seems unlikely 181667614Sdarrenr * that SOLARIS would compensate this in the kernel (a body of an IP packet 181767614Sdarrenr * in the data section of an ICMP packet). I have the feeling that this should 181867614Sdarrenr * be unconditional, but I'm not in a position to check. 181967614Sdarrenr */ 182063523Sdarrenr#if !SOLARIS && !defined(__sgi) 182167614Sdarrenr /* 182267614Sdarrenr * Fix UDP pseudo header checksum to compensate for the 182367614Sdarrenr * IP address change. 182467614Sdarrenr */ 182567614Sdarrenr if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) { 182667614Sdarrenr /* 182767614Sdarrenr * The UDP checksum is optional, only adjust it 182867614Sdarrenr * if it has been set 182967614Sdarrenr */ 183067614Sdarrenr sum1 = ntohs(udp->uh_sum); 183167614Sdarrenr fix_datacksum(&udp->uh_sum, sumd); 183267614Sdarrenr sum2 = ntohs(udp->uh_sum); 183367614Sdarrenr 183467614Sdarrenr /* 183567614Sdarrenr * Fix ICMP checksum to compensate the UDP 183667614Sdarrenr * checksum adjustment. 183767614Sdarrenr */ 183867614Sdarrenr CALC_SUMD(sum1, sum2, sumd); 183972006Sdarrenr sumd2 = sumd; 184067614Sdarrenr } 184167614Sdarrenr 184295418Sdarrenr#if 1 184367614Sdarrenr /* 184467614Sdarrenr * Fix TCP pseudo header checksum to compensate for the 184567614Sdarrenr * IP address change. Before we can do the change, we 184667614Sdarrenr * must make sure that oip is sufficient large to hold 184767614Sdarrenr * the TCP checksum (normally it does not!). 184867614Sdarrenr */ 184995418Sdarrenr if (oip->ip_p == IPPROTO_TCP && dlen >= 18) { 185067614Sdarrenr 185195418Sdarrenr sum1 = ntohs(tcp->th_sum); 185295418Sdarrenr fix_datacksum(&tcp->th_sum, sumd); 185395418Sdarrenr sum2 = ntohs(tcp->th_sum); 185495418Sdarrenr 185595418Sdarrenr /* 185695418Sdarrenr * Fix ICMP checksum to compensate the TCP 185795418Sdarrenr * checksum adjustment. 185895418Sdarrenr */ 185995418Sdarrenr CALC_SUMD(sum1, sum2, sumd); 186095418Sdarrenr sumd2 = sumd; 186167614Sdarrenr }; 186263523Sdarrenr#endif 186367614Sdarrenr 186467614Sdarrenr#endif 186553642Sguido } 186653642Sguido 186753642Sguido if ((flags & IPN_TCPUDP) != 0) { 186864580Sdarrenr /* 186967614Sdarrenr * Step 2 : 187067614Sdarrenr * For offending TCP/UDP IP packets, translate the ports as 187167614Sdarrenr * well, based on the NAT specification. Of course such 187267614Sdarrenr * a change must be reflected in the ICMP checksum as well. 187367614Sdarrenr * 187467614Sdarrenr * Advance notice : Now it becomes complicated :-) 187567614Sdarrenr * 187667614Sdarrenr * Since the port fields are part of the TCP/UDP checksum 187767614Sdarrenr * of the offending IP packet, you need to adjust that checksum 187867614Sdarrenr * as well... but, if you change, you must change the icmp 187967614Sdarrenr * checksum *again*, to reflect that change. 188067614Sdarrenr * 188167614Sdarrenr * To further complicate: the TCP checksum is not in the first 188267614Sdarrenr * 8 bytes of the offending ip packet, so it most likely is not 188395418Sdarrenr * available. Some OSses like Solaris return enough bytes to 188495418Sdarrenr * include the TCP checksum. So we have to check if the 188595418Sdarrenr * ip->ip_len actually holds the TCP checksum of the oip! 188667614Sdarrenr */ 188767614Sdarrenr 188872006Sdarrenr if (nat->nat_oport == tcp->th_dport) { 188953642Sguido if (tcp->th_sport != nat->nat_inport) { 189067614Sdarrenr /* 189167614Sdarrenr * Fix ICMP checksum to compensate port 189267614Sdarrenr * adjustment. 189367614Sdarrenr */ 189453642Sguido sum1 = ntohs(tcp->th_sport); 189553642Sguido sum2 = ntohs(nat->nat_inport); 189653642Sguido CALC_SUMD(sum1, sum2, sumd); 189772006Sdarrenr sumd2 += sumd; 189853642Sguido tcp->th_sport = nat->nat_inport; 189967614Sdarrenr 190067614Sdarrenr /* 190167614Sdarrenr * Fix udp checksum to compensate port 190267614Sdarrenr * adjustment. NOTE : the offending IP packet 190367614Sdarrenr * flows the other direction compared to the 190467614Sdarrenr * ICMP message. 190567614Sdarrenr * 190667614Sdarrenr * The UDP checksum is optional, only adjust 190767614Sdarrenr * it if it has been set. 190867614Sdarrenr */ 190967614Sdarrenr if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) { 191067614Sdarrenr 191167614Sdarrenr sum1 = ntohs(udp->uh_sum); 191267614Sdarrenr fix_datacksum(&udp->uh_sum, sumd); 191367614Sdarrenr sum2 = ntohs(udp->uh_sum); 191467614Sdarrenr 191567614Sdarrenr /* 191667614Sdarrenr * Fix ICMP checksum to 191767614Sdarrenr * compensate UDP checksum 191867614Sdarrenr * adjustment. 191967614Sdarrenr */ 192067614Sdarrenr CALC_SUMD(sum1, sum2, sumd); 192172006Sdarrenr sumd2 += sumd; 192267614Sdarrenr } 192395418Sdarrenr 192495418Sdarrenr /* 192595418Sdarrenr * Fix tcp checksum (if present) to compensate 192695418Sdarrenr * port adjustment. NOTE : the offending IP 192795418Sdarrenr * packet flows the other direction compared to 192895418Sdarrenr * the ICMP message. 192995418Sdarrenr */ 193095418Sdarrenr if (oip->ip_p == IPPROTO_TCP && dlen >= 18) { 193195418Sdarrenr 193295418Sdarrenr sum1 = ntohs(tcp->th_sum); 193395418Sdarrenr fix_datacksum(&tcp->th_sum, sumd); 193495418Sdarrenr sum2 = ntohs(tcp->th_sum); 193595418Sdarrenr 193695418Sdarrenr /* 193795418Sdarrenr * Fix ICMP checksum to 193895418Sdarrenr * compensate TCP checksum 193995418Sdarrenr * adjustment. 194095418Sdarrenr */ 194195418Sdarrenr CALC_SUMD(sum1, sum2, sumd); 194295418Sdarrenr sumd2 += sumd; 194395418Sdarrenr } 194453642Sguido } 194553642Sguido } else { 194653642Sguido if (tcp->th_dport != nat->nat_outport) { 194767614Sdarrenr /* 194867614Sdarrenr * Fix ICMP checksum to compensate port 194967614Sdarrenr * adjustment. 195067614Sdarrenr */ 195153642Sguido sum1 = ntohs(tcp->th_dport); 195253642Sguido sum2 = ntohs(nat->nat_outport); 195353642Sguido CALC_SUMD(sum1, sum2, sumd); 195472006Sdarrenr sumd2 += sumd; 195553642Sguido tcp->th_dport = nat->nat_outport; 195667614Sdarrenr 195767614Sdarrenr /* 195867614Sdarrenr * Fix udp checksum to compensate port 195967614Sdarrenr * adjustment. NOTE : the offending IP 196067614Sdarrenr * packet flows the other direction compared 196167614Sdarrenr * to the ICMP message. 196267614Sdarrenr * 196367614Sdarrenr * The UDP checksum is optional, only adjust 196467614Sdarrenr * it if it has been set. 196567614Sdarrenr */ 196667614Sdarrenr if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) { 196767614Sdarrenr 196867614Sdarrenr sum1 = ntohs(udp->uh_sum); 196967614Sdarrenr fix_datacksum(&udp->uh_sum, sumd); 197067614Sdarrenr sum2 = ntohs(udp->uh_sum); 197167614Sdarrenr 197267614Sdarrenr /* 197367614Sdarrenr * Fix ICMP checksum to compensate 197467614Sdarrenr * UDP checksum adjustment. 197567614Sdarrenr */ 197667614Sdarrenr CALC_SUMD(sum1, sum2, sumd); 197772006Sdarrenr sumd2 += sumd; 197867614Sdarrenr } 197995418Sdarrenr 198095418Sdarrenr /* 198195418Sdarrenr * Fix tcp checksum (if present) to compensate 198295418Sdarrenr * port adjustment. NOTE : the offending IP 198395418Sdarrenr * packet flows the other direction compared to 198495418Sdarrenr * the ICMP message. 198595418Sdarrenr */ 198695418Sdarrenr if (oip->ip_p == IPPROTO_TCP && dlen >= 18) { 198795418Sdarrenr 198895418Sdarrenr sum1 = ntohs(tcp->th_sum); 198995418Sdarrenr fix_datacksum(&tcp->th_sum, sumd); 199095418Sdarrenr sum2 = ntohs(tcp->th_sum); 199195418Sdarrenr 199295418Sdarrenr /* 199395418Sdarrenr * Fix ICMP checksum to compensate 199495418Sdarrenr * UDP checksum adjustment. 199595418Sdarrenr */ 199695418Sdarrenr CALC_SUMD(sum1, sum2, sumd); 199795418Sdarrenr sumd2 += sumd; 199895418Sdarrenr } 199953642Sguido } 200053642Sguido } 200172006Sdarrenr if (sumd2) { 200272006Sdarrenr sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 200372006Sdarrenr sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 200472006Sdarrenr if (nat->nat_dir == NAT_OUTBOUND) { 200580482Sdarrenr fix_outcksum(fin, &icmp->icmp_cksum, sumd2); 200672006Sdarrenr } else { 200780482Sdarrenr fix_incksum(fin, &icmp->icmp_cksum, sumd2); 200872006Sdarrenr } 200972006Sdarrenr } 201053642Sguido } 201180482Sdarrenr if (oip->ip_p == IPPROTO_ICMP) 201280482Sdarrenr nat->nat_age = fr_defnaticmpage; 201353642Sguido return nat; 201453642Sguido} 201553642Sguido 201653642Sguido 201753642Sguido/* 201853642Sguido * NB: these lookups don't lock access to the list, it assume it has already 201953642Sguido * been done! 202053642Sguido */ 202153642Sguido/* 202253642Sguido * Lookup a nat entry based on the mapped destination ip address/port and 202353642Sguido * real source address/port. We use this lookup when receiving a packet, 202453642Sguido * we're looking for a table entry, based on the destination address. 202553642Sguido * NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. 202653642Sguido */ 202792685Sdarrenrnat_t *nat_inlookup(fin, flags, p, src, mapdst, rw) 202892685Sdarrenrfr_info_t *fin; 202953642Sguidoregister u_int flags, p; 203053642Sguidostruct in_addr src , mapdst; 203172006Sdarrenrint rw; 203253642Sguido{ 203367614Sdarrenr register u_short sport, dport; 203453642Sguido register nat_t *nat; 203553642Sguido register int nflags; 203667614Sdarrenr register u_32_t dst; 203792685Sdarrenr ipnat_t *ipn; 203892685Sdarrenr void *ifp; 203953642Sguido u_int hv; 204053642Sguido 204192685Sdarrenr if (fin != NULL) 204292685Sdarrenr ifp = fin->fin_ifp; 204392685Sdarrenr else 204492685Sdarrenr ifp = NULL; 204567614Sdarrenr dst = mapdst.s_addr; 204692685Sdarrenr if (flags & IPN_TCPUDP) { 204792685Sdarrenr sport = htons(fin->fin_data[0]); 204892685Sdarrenr dport = htons(fin->fin_data[1]); 204992685Sdarrenr } else { 205092685Sdarrenr sport = 0; 205192685Sdarrenr dport = 0; 205292685Sdarrenr } 205353642Sguido 205480482Sdarrenr hv = NAT_HASH_FN(dst, dport, 0xffffffff); 205580482Sdarrenr hv = NAT_HASH_FN(src.s_addr, hv + sport, ipf_nattable_sz); 205653642Sguido nat = nat_table[1][hv]; 205753642Sguido for (; nat; nat = nat->nat_hnext[1]) { 205853642Sguido nflags = nat->nat_flags; 205953642Sguido if ((!ifp || ifp == nat->nat_ifp) && 206053642Sguido nat->nat_oip.s_addr == src.s_addr && 206167614Sdarrenr nat->nat_outip.s_addr == dst && 206292685Sdarrenr ((p == 0) || (p == nat->nat_p))) { 206392685Sdarrenr switch (p) 206492685Sdarrenr { 206592685Sdarrenr case IPPROTO_TCP : 206692685Sdarrenr case IPPROTO_UDP : 206792685Sdarrenr if (nat->nat_oport != sport) 206892685Sdarrenr continue; 206992685Sdarrenr if (nat->nat_outport != dport) 207092685Sdarrenr continue; 207192685Sdarrenr break; 207292685Sdarrenr default : 207392685Sdarrenr break; 207492685Sdarrenr } 207592685Sdarrenr 207692685Sdarrenr ipn = nat->nat_ptr; 207792685Sdarrenr if ((ipn != NULL) && (nat->nat_aps != NULL)) 207892685Sdarrenr if (appr_match(fin, nat) != 0) 207992685Sdarrenr continue; 208053642Sguido return nat; 208192685Sdarrenr } 208253642Sguido } 208392685Sdarrenr if (!nat_stats.ns_wilds || !(flags & FI_WILDP)) 208467614Sdarrenr return NULL; 208572006Sdarrenr if (!rw) { 208672006Sdarrenr RWLOCK_EXIT(&ipf_nat); 208772006Sdarrenr } 208880482Sdarrenr hv = NAT_HASH_FN(dst, 0, 0xffffffff); 208992685Sdarrenr hv = NAT_HASH_FN(src.s_addr, dst, ipf_nattable_sz); 209072006Sdarrenr if (!rw) { 209172006Sdarrenr WRITE_ENTER(&ipf_nat); 209272006Sdarrenr } 209367614Sdarrenr nat = nat_table[1][hv]; 209467614Sdarrenr for (; nat; nat = nat->nat_hnext[1]) { 209567614Sdarrenr nflags = nat->nat_flags; 209667614Sdarrenr if (ifp && ifp != nat->nat_ifp) 209767614Sdarrenr continue; 209867614Sdarrenr if (!(nflags & FI_WILDP)) 209967614Sdarrenr continue; 210067614Sdarrenr if (nat->nat_oip.s_addr != src.s_addr || 210167614Sdarrenr nat->nat_outip.s_addr != dst) 210267614Sdarrenr continue; 210367614Sdarrenr if (((nat->nat_oport == sport) || (nflags & FI_W_DPORT)) && 210467614Sdarrenr ((nat->nat_outport == dport) || (nflags & FI_W_SPORT))) { 210592685Sdarrenr nat_tabmove(fin, nat); 210667614Sdarrenr break; 210767614Sdarrenr } 210867614Sdarrenr } 210972006Sdarrenr if (!rw) { 211072006Sdarrenr MUTEX_DOWNGRADE(&ipf_nat); 211172006Sdarrenr } 211267614Sdarrenr return nat; 211353642Sguido} 211453642Sguido 211553642Sguido 211672006Sdarrenr/* 211772006Sdarrenr * This function is only called for TCP/UDP NAT table entries where the 211872006Sdarrenr * original was placed in the table without hashing on the ports and we now 211972006Sdarrenr * want to include hashing on port numbers. 212072006Sdarrenr */ 212192685Sdarrenrstatic void nat_tabmove(fin, nat) 212292685Sdarrenrfr_info_t *fin; 212367614Sdarrenrnat_t *nat; 212467614Sdarrenr{ 212572006Sdarrenr register u_short sport, dport; 212692685Sdarrenr u_int hv, nflags; 212767614Sdarrenr nat_t **natp; 212867614Sdarrenr 212992685Sdarrenr nflags = nat->nat_flags; 213072006Sdarrenr 213192685Sdarrenr sport = ntohs(fin->fin_data[0]); 213292685Sdarrenr dport = ntohs(fin->fin_data[1]); 213372006Sdarrenr 213467614Sdarrenr /* 213567614Sdarrenr * Remove the NAT entry from the old location 213667614Sdarrenr */ 213767614Sdarrenr if (nat->nat_hnext[0]) 213867614Sdarrenr nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 213967614Sdarrenr *nat->nat_phnext[0] = nat->nat_hnext[0]; 214067614Sdarrenr 214167614Sdarrenr if (nat->nat_hnext[1]) 214267853Sdarrenr nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 214367614Sdarrenr *nat->nat_phnext[1] = nat->nat_hnext[1]; 214467614Sdarrenr 214567853Sdarrenr /* 214667853Sdarrenr * Add into the NAT table in the new position 214767853Sdarrenr */ 214880482Sdarrenr hv = NAT_HASH_FN(nat->nat_inip.s_addr, sport, 0xffffffff); 214980482Sdarrenr hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + dport, ipf_nattable_sz); 215067614Sdarrenr natp = &nat_table[0][hv]; 215167614Sdarrenr if (*natp) 215267614Sdarrenr (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 215367614Sdarrenr nat->nat_phnext[0] = natp; 215467614Sdarrenr nat->nat_hnext[0] = *natp; 215567614Sdarrenr *natp = nat; 215667614Sdarrenr 215780482Sdarrenr hv = NAT_HASH_FN(nat->nat_outip.s_addr, sport, 0xffffffff); 215880482Sdarrenr hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + dport, ipf_nattable_sz); 215967614Sdarrenr natp = &nat_table[1][hv]; 216067614Sdarrenr if (*natp) 216167614Sdarrenr (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 216267614Sdarrenr nat->nat_phnext[1] = natp; 216367614Sdarrenr nat->nat_hnext[1] = *natp; 216467614Sdarrenr *natp = nat; 216567614Sdarrenr} 216667614Sdarrenr 216767614Sdarrenr 216853642Sguido/* 216953642Sguido * Lookup a nat entry based on the source 'real' ip address/port and 217053642Sguido * destination address/port. We use this lookup when sending a packet out, 217153642Sguido * we're looking for a table entry, based on the source address. 217253642Sguido * NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. 217353642Sguido */ 217492685Sdarrenrnat_t *nat_outlookup(fin, flags, p, src, dst, rw) 217592685Sdarrenrfr_info_t *fin; 217653642Sguidoregister u_int flags, p; 217753642Sguidostruct in_addr src , dst; 217872006Sdarrenrint rw; 217953642Sguido{ 218053642Sguido register u_short sport, dport; 218153642Sguido register nat_t *nat; 218253642Sguido register int nflags; 218392685Sdarrenr ipnat_t *ipn; 218467614Sdarrenr u_32_t srcip; 218592685Sdarrenr void *ifp; 218653642Sguido u_int hv; 218753642Sguido 218892685Sdarrenr ifp = fin->fin_ifp; 218967614Sdarrenr srcip = src.s_addr; 219092685Sdarrenr if (flags & IPN_TCPUDP) { 219192685Sdarrenr sport = ntohs(fin->fin_data[0]); 219292685Sdarrenr dport = ntohs(fin->fin_data[1]); 219392685Sdarrenr } else { 219492685Sdarrenr sport = 0; 219592685Sdarrenr dport = 0; 219692685Sdarrenr } 219753642Sguido 219880482Sdarrenr hv = NAT_HASH_FN(srcip, sport, 0xffffffff); 219980482Sdarrenr hv = NAT_HASH_FN(dst.s_addr, hv + dport, ipf_nattable_sz); 220053642Sguido nat = nat_table[0][hv]; 220153642Sguido for (; nat; nat = nat->nat_hnext[0]) { 220253642Sguido nflags = nat->nat_flags; 220353642Sguido 220453642Sguido if ((!ifp || ifp == nat->nat_ifp) && 220567614Sdarrenr nat->nat_inip.s_addr == srcip && 220653642Sguido nat->nat_oip.s_addr == dst.s_addr && 220792685Sdarrenr ((p == 0) || (p == nat->nat_p))) { 220892685Sdarrenr switch (p) 220992685Sdarrenr { 221092685Sdarrenr case IPPROTO_TCP : 221192685Sdarrenr case IPPROTO_UDP : 221292685Sdarrenr if (nat->nat_oport != dport) 221392685Sdarrenr continue; 221492685Sdarrenr if (nat->nat_inport != sport) 221592685Sdarrenr continue; 221692685Sdarrenr break; 221792685Sdarrenr default : 221892685Sdarrenr break; 221992685Sdarrenr } 222092685Sdarrenr 222192685Sdarrenr ipn = nat->nat_ptr; 222292685Sdarrenr if ((ipn != NULL) && (nat->nat_aps != NULL)) 222392685Sdarrenr if (appr_match(fin, nat) != 0) 222492685Sdarrenr continue; 222553642Sguido return nat; 222692685Sdarrenr } 222753642Sguido } 222892685Sdarrenr if (!nat_stats.ns_wilds || !(flags & FI_WILDP)) 222967614Sdarrenr return NULL; 223072006Sdarrenr if (!rw) { 223172006Sdarrenr RWLOCK_EXIT(&ipf_nat); 223272006Sdarrenr } 223392685Sdarrenr 223492685Sdarrenr hv = NAT_HASH_FN(dst.s_addr, srcip, ipf_nattable_sz); 223572006Sdarrenr if (!rw) { 223672006Sdarrenr WRITE_ENTER(&ipf_nat); 223772006Sdarrenr } 223867614Sdarrenr nat = nat_table[0][hv]; 223967614Sdarrenr for (; nat; nat = nat->nat_hnext[0]) { 224067614Sdarrenr nflags = nat->nat_flags; 224167614Sdarrenr if (ifp && ifp != nat->nat_ifp) 224267614Sdarrenr continue; 224367614Sdarrenr if (!(nflags & FI_WILDP)) 224467614Sdarrenr continue; 224567614Sdarrenr if ((nat->nat_inip.s_addr != srcip) || 224667614Sdarrenr (nat->nat_oip.s_addr != dst.s_addr)) 224767614Sdarrenr continue; 224872006Sdarrenr if (((nat->nat_inport == sport) || (nflags & FI_W_SPORT)) && 224972006Sdarrenr ((nat->nat_oport == dport) || (nflags & FI_W_DPORT))) { 225092685Sdarrenr nat_tabmove(fin, nat); 225167614Sdarrenr break; 225267614Sdarrenr } 225367614Sdarrenr } 225472006Sdarrenr if (!rw) { 225572006Sdarrenr MUTEX_DOWNGRADE(&ipf_nat); 225672006Sdarrenr } 225767614Sdarrenr return nat; 225853642Sguido} 225953642Sguido 226053642Sguido 226153642Sguido/* 226253642Sguido * Lookup the NAT tables to search for a matching redirect 226353642Sguido */ 226453642Sguidonat_t *nat_lookupredir(np) 226553642Sguidoregister natlookup_t *np; 226653642Sguido{ 226753642Sguido nat_t *nat; 226892685Sdarrenr fr_info_t fi; 226953642Sguido 227092685Sdarrenr bzero((char *)&fi, sizeof(fi)); 227192685Sdarrenr fi.fin_data[0] = np->nl_inport; 227292685Sdarrenr fi.fin_data[1] = np->nl_outport; 227392685Sdarrenr 227453642Sguido /* 227553642Sguido * If nl_inip is non null, this is a lookup based on the real 227653642Sguido * ip address. Else, we use the fake. 227753642Sguido */ 227892685Sdarrenr if ((nat = nat_outlookup(&fi, np->nl_flags, 0, np->nl_inip, 227992685Sdarrenr np->nl_outip, 0))) { 228053642Sguido np->nl_realip = nat->nat_outip; 228153642Sguido np->nl_realport = nat->nat_outport; 228253642Sguido } 228353642Sguido return nat; 228453642Sguido} 228553642Sguido 228653642Sguido 228760852Sdarrenrstatic int nat_match(fin, np, ip) 228860852Sdarrenrfr_info_t *fin; 228960852Sdarrenripnat_t *np; 229060852Sdarrenrip_t *ip; 229160852Sdarrenr{ 229260852Sdarrenr frtuc_t *ft; 229360852Sdarrenr 229460852Sdarrenr if (ip->ip_v != 4) 229560852Sdarrenr return 0; 229660852Sdarrenr 229792685Sdarrenr if (np->in_p && fin->fin_p != np->in_p) 229860852Sdarrenr return 0; 229960852Sdarrenr if (fin->fin_out) { 230063523Sdarrenr if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK))) 230160852Sdarrenr return 0; 230263523Sdarrenr if (((fin->fin_fi.fi_saddr & np->in_inmsk) != np->in_inip) 230363523Sdarrenr ^ ((np->in_flags & IPN_NOTSRC) != 0)) 230460852Sdarrenr return 0; 230563523Sdarrenr if (((fin->fin_fi.fi_daddr & np->in_srcmsk) != np->in_srcip) 230663523Sdarrenr ^ ((np->in_flags & IPN_NOTDST) != 0)) 230760852Sdarrenr return 0; 230860852Sdarrenr } else { 230963523Sdarrenr if (!(np->in_redir & NAT_REDIRECT)) 231060852Sdarrenr return 0; 231163523Sdarrenr if (((fin->fin_fi.fi_saddr & np->in_srcmsk) != np->in_srcip) 231263523Sdarrenr ^ ((np->in_flags & IPN_NOTSRC) != 0)) 231363523Sdarrenr return 0; 231463523Sdarrenr if (((fin->fin_fi.fi_daddr & np->in_outmsk) != np->in_outip) 231563523Sdarrenr ^ ((np->in_flags & IPN_NOTDST) != 0)) 231663523Sdarrenr return 0; 231760852Sdarrenr } 231860852Sdarrenr 231960852Sdarrenr ft = &np->in_tuc; 232080482Sdarrenr if (!(fin->fin_fl & FI_TCPUDP) || 232180482Sdarrenr (fin->fin_fl & FI_SHORT) || (fin->fin_off != 0)) { 232260852Sdarrenr if (ft->ftu_scmp || ft->ftu_dcmp) 232360852Sdarrenr return 0; 232460852Sdarrenr return 1; 232560852Sdarrenr } 232660852Sdarrenr 232760852Sdarrenr return fr_tcpudpchk(ft, fin); 232860852Sdarrenr} 232960852Sdarrenr 233060852Sdarrenr 233153642Sguido/* 233253642Sguido * Packets going out on the external interface go through this. 233353642Sguido * Here, the source address requires alteration, if anything. 233453642Sguido */ 233553642Sguidoint ip_natout(ip, fin) 233653642Sguidoip_t *ip; 233753642Sguidofr_info_t *fin; 233853642Sguido{ 233953642Sguido register ipnat_t *np = NULL; 234053642Sguido register u_32_t ipa; 234153642Sguido tcphdr_t *tcp = NULL; 234260852Sdarrenr u_short sport = 0, dport = 0, *csump = NULL; 234380482Sdarrenr int natadd = 1, i, icmpset = 1; 234480482Sdarrenr u_int nflags = 0, hv, msk; 234553642Sguido struct ifnet *ifp; 234653642Sguido frentry_t *fr; 234792685Sdarrenr void *sifp; 234853642Sguido u_32_t iph; 234953642Sguido nat_t *nat; 235053642Sguido 235160852Sdarrenr if (nat_list == NULL || (fr_nat_lock)) 235253642Sguido return 0; 235353642Sguido 235453642Sguido if ((fr = fin->fin_fr) && !(fr->fr_flags & FR_DUP) && 235592685Sdarrenr fr->fr_tif.fd_ifp && fr->fr_tif.fd_ifp != (void *)-1) { 235692685Sdarrenr sifp = fin->fin_ifp; 235792685Sdarrenr fin->fin_ifp = fr->fr_tif.fd_ifp; 235892685Sdarrenr } else 235992685Sdarrenr sifp = fin->fin_ifp; 236092685Sdarrenr ifp = fin->fin_ifp; 236153642Sguido 236280482Sdarrenr if ((fin->fin_off == 0) && !(fin->fin_fl & FI_SHORT)) { 236392685Sdarrenr if (fin->fin_p == IPPROTO_TCP) 236453642Sguido nflags = IPN_TCP; 236592685Sdarrenr else if (fin->fin_p == IPPROTO_UDP) 236653642Sguido nflags = IPN_UDP; 236753642Sguido if ((nflags & IPN_TCPUDP)) { 236853642Sguido tcp = (tcphdr_t *)fin->fin_dp; 236953642Sguido sport = tcp->th_sport; 237053642Sguido dport = tcp->th_dport; 237153642Sguido } 237253642Sguido } 237353642Sguido 237492685Sdarrenr ipa = fin->fin_saddr; 237553642Sguido 237653642Sguido READ_ENTER(&ipf_nat); 237760852Sdarrenr 237892685Sdarrenr if ((fin->fin_p == IPPROTO_ICMP) && 237960852Sdarrenr (nat = nat_icmp(ip, fin, &nflags, NAT_OUTBOUND))) 238080482Sdarrenr icmpset = 1; 238180482Sdarrenr else if ((fin->fin_fl & FI_FRAG) && 238272006Sdarrenr (nat = ipfr_nat_knownfrag(ip, fin))) 238353642Sguido natadd = 0; 238492685Sdarrenr else if ((nat = nat_outlookup(fin, nflags|FI_WILDP|FI_WILDA, 238592685Sdarrenr (u_int)fin->fin_p, fin->fin_src, 238692685Sdarrenr fin->fin_dst, 0))) { 238753642Sguido nflags = nat->nat_flags; 238853642Sguido if ((nflags & (FI_W_SPORT|FI_W_DPORT)) != 0) { 238953642Sguido if ((nflags & FI_W_SPORT) && 239053642Sguido (nat->nat_inport != sport)) 239153642Sguido nat->nat_inport = sport; 239292685Sdarrenr if ((nflags & FI_W_DPORT) && 239392685Sdarrenr (nat->nat_oport != dport)) 239453642Sguido nat->nat_oport = dport; 239592685Sdarrenr 239653642Sguido if (nat->nat_outport == 0) 239753642Sguido nat->nat_outport = sport; 239853642Sguido nat->nat_flags &= ~(FI_W_DPORT|FI_W_SPORT); 239953642Sguido nflags = nat->nat_flags; 240067853Sdarrenr nat_stats.ns_wilds--; 240153642Sguido } 240253642Sguido } else { 240353642Sguido RWLOCK_EXIT(&ipf_nat); 240492685Sdarrenr 240592685Sdarrenr msk = 0xffffffff; 240692685Sdarrenr i = 32; 240792685Sdarrenr 240853642Sguido WRITE_ENTER(&ipf_nat); 240953642Sguido /* 241053642Sguido * If there is no current entry in the nat table for this IP#, 241153642Sguido * create one for it (if there is a matching rule). 241253642Sguido */ 241353642Sguidomaskloop: 241453642Sguido iph = ipa & htonl(msk); 241560852Sdarrenr hv = NAT_HASH_FN(iph, 0, ipf_natrules_sz); 241653642Sguido for (np = nat_rules[hv]; np; np = np->in_mnext) 241753642Sguido { 241880482Sdarrenr if (np->in_ifp && (np->in_ifp != ifp)) 241960852Sdarrenr continue; 242060852Sdarrenr if ((np->in_flags & IPN_RF) && 242160852Sdarrenr !(np->in_flags & nflags)) 242260852Sdarrenr continue; 242360852Sdarrenr if (np->in_flags & IPN_FILTER) { 242460852Sdarrenr if (!nat_match(fin, np, ip)) 242560852Sdarrenr continue; 242660852Sdarrenr } else if ((ipa & np->in_inmsk) != np->in_inip) 242760852Sdarrenr continue; 242892685Sdarrenr if (*np->in_plabel && !appr_ok(ip, tcp, np)) 242992685Sdarrenr continue; 243092685Sdarrenr nat = nat_new(fin, ip, np, NULL, 243192685Sdarrenr (u_int)nflags, NAT_OUTBOUND); 243292685Sdarrenr if (nat != NULL) { 243392685Sdarrenr np->in_hits++; 243492685Sdarrenr break; 243553642Sguido } 243653642Sguido } 243753642Sguido if ((np == NULL) && (i > 0)) { 243853642Sguido do { 243953642Sguido i--; 244053642Sguido msk <<= 1; 244153642Sguido } while ((i >= 0) && ((nat_masks & (1 << i)) == 0)); 244253642Sguido if (i >= 0) 244353642Sguido goto maskloop; 244453642Sguido } 244553642Sguido MUTEX_DOWNGRADE(&ipf_nat); 244653642Sguido } 244753642Sguido 244872006Sdarrenr /* 244972006Sdarrenr * NOTE: ipf_nat must now only be held as a read lock 245072006Sdarrenr */ 245153642Sguido if (nat) { 245253642Sguido np = nat->nat_ptr; 245380482Sdarrenr if (natadd && (fin->fin_fl & FI_FRAG) && np) 245453642Sguido ipfr_nat_newfrag(ip, fin, 0, nat); 245560852Sdarrenr MUTEX_ENTER(&nat->nat_lock); 245692685Sdarrenr if (fin->fin_p != IPPROTO_TCP) { 245792685Sdarrenr if (np && np->in_age[1]) 245892685Sdarrenr nat->nat_age = np->in_age[1]; 245992685Sdarrenr else if (!icmpset && (fin->fin_p == IPPROTO_ICMP)) 246092685Sdarrenr nat->nat_age = fr_defnaticmpage; 246192685Sdarrenr else 246292685Sdarrenr nat->nat_age = fr_defnatage; 246392685Sdarrenr } 246453642Sguido nat->nat_bytes += ip->ip_len; 246553642Sguido nat->nat_pkts++; 246660852Sdarrenr MUTEX_EXIT(&nat->nat_lock); 246753642Sguido 246853642Sguido /* 246953642Sguido * Fix up checksums, not by recalculating them, but 247053642Sguido * simply computing adjustments. 247153642Sguido */ 247263523Sdarrenr if (nflags == IPN_ICMPERR) { 247363523Sdarrenr u_32_t s1, s2, sumd; 247463523Sdarrenr 247592685Sdarrenr s1 = LONG_SUM(ntohl(fin->fin_saddr)); 247663523Sdarrenr s2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)); 247763523Sdarrenr CALC_SUMD(s1, s2, sumd); 247863523Sdarrenr 247963523Sdarrenr if (nat->nat_dir == NAT_OUTBOUND) 248092685Sdarrenr fix_outcksum(fin, &ip->ip_sum, sumd); 248192685Sdarrenr else 248280482Sdarrenr fix_incksum(fin, &ip->ip_sum, sumd); 248363523Sdarrenr } 248492685Sdarrenr#if (SOLARIS || defined(__sgi)) && defined(_KERNEL) 248563523Sdarrenr else { 248663523Sdarrenr if (nat->nat_dir == NAT_OUTBOUND) 248780482Sdarrenr fix_outcksum(fin, &ip->ip_sum, nat->nat_ipsumd); 248863523Sdarrenr else 248980482Sdarrenr fix_incksum(fin, &ip->ip_sum, nat->nat_ipsumd); 249063523Sdarrenr } 249153642Sguido#endif 249292685Sdarrenr /* 249392685Sdarrenr * Only change the packet contents, not what is filtered upon. 249492685Sdarrenr */ 249563523Sdarrenr ip->ip_src = nat->nat_outip; 249653642Sguido 249780482Sdarrenr if ((fin->fin_off == 0) && !(fin->fin_fl & FI_SHORT)) { 249853642Sguido 249992685Sdarrenr if ((nat->nat_outport != 0) && (tcp != NULL)) { 250053642Sguido tcp->th_sport = nat->nat_outport; 250153642Sguido fin->fin_data[0] = ntohs(tcp->th_sport); 250253642Sguido } 250353642Sguido 250492685Sdarrenr if (fin->fin_p == IPPROTO_TCP) { 250553642Sguido csump = &tcp->th_sum; 250660852Sdarrenr MUTEX_ENTER(&nat->nat_lock); 250753642Sguido fr_tcp_age(&nat->nat_age, 250895418Sdarrenr nat->nat_tcpstate, fin, 1, 0); 250953642Sguido if (nat->nat_age < fr_defnaticmpage) 251053642Sguido nat->nat_age = fr_defnaticmpage; 251153642Sguido#ifdef LARGE_NAT 251260852Sdarrenr else if (nat->nat_age > fr_defnatage) 251360852Sdarrenr nat->nat_age = fr_defnatage; 251453642Sguido#endif 251553642Sguido /* 251653642Sguido * Increase this because we may have 251753642Sguido * "keep state" following this too and 251853642Sguido * packet storms can occur if this is 251953642Sguido * removed too quickly. 252053642Sguido */ 252153642Sguido if (nat->nat_age == fr_tcpclosed) 252253642Sguido nat->nat_age = fr_tcplastack; 252360852Sdarrenr MUTEX_EXIT(&nat->nat_lock); 252492685Sdarrenr } else if (fin->fin_p == IPPROTO_UDP) { 252553642Sguido udphdr_t *udp = (udphdr_t *)tcp; 252653642Sguido 252753642Sguido if (udp->uh_sum) 252853642Sguido csump = &udp->uh_sum; 252953642Sguido } 253063523Sdarrenr 253153642Sguido if (csump) { 253253642Sguido if (nat->nat_dir == NAT_OUTBOUND) 253392685Sdarrenr fix_outcksum(fin, csump, 253492685Sdarrenr nat->nat_sumd[1]); 253553642Sguido else 253692685Sdarrenr fix_incksum(fin, csump, 253792685Sdarrenr nat->nat_sumd[1]); 253853642Sguido } 253953642Sguido } 254060852Sdarrenr 254192685Sdarrenr if (np && (np->in_apr != NULL) && (np->in_dport == 0 || 254260852Sdarrenr (tcp != NULL && dport == np->in_dport))) { 254360852Sdarrenr i = appr_check(ip, fin, nat); 254460852Sdarrenr if (i == 0) 254560852Sdarrenr i = 1; 254660852Sdarrenr } else 254760852Sdarrenr i = 1; 254860852Sdarrenr ATOMIC_INCL(nat_stats.ns_mapped[1]); 254953642Sguido RWLOCK_EXIT(&ipf_nat); /* READ */ 255092685Sdarrenr fin->fin_ifp = sifp; 255160852Sdarrenr return i; 255253642Sguido } 255353642Sguido RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ 255492685Sdarrenr fin->fin_ifp = sifp; 255553642Sguido return 0; 255653642Sguido} 255753642Sguido 255853642Sguido 255953642Sguido/* 256053642Sguido * Packets coming in from the external interface go through this. 256153642Sguido * Here, the destination address requires alteration, if anything. 256253642Sguido */ 256353642Sguidoint ip_natin(ip, fin) 256453642Sguidoip_t *ip; 256553642Sguidofr_info_t *fin; 256653642Sguido{ 256753642Sguido register struct in_addr src; 256853642Sguido register struct in_addr in; 256953642Sguido register ipnat_t *np; 257080482Sdarrenr u_short sport = 0, dport = 0, *csump = NULL; 257153642Sguido u_int nflags = 0, natadd = 1, hv, msk; 257253642Sguido struct ifnet *ifp = fin->fin_ifp; 257353642Sguido tcphdr_t *tcp = NULL; 257480482Sdarrenr int i, icmpset = 0; 257553642Sguido nat_t *nat; 257653642Sguido u_32_t iph; 257753642Sguido 257860852Sdarrenr if ((nat_list == NULL) || (ip->ip_v != 4) || (fr_nat_lock)) 257953642Sguido return 0; 258053642Sguido 258180482Sdarrenr if ((fin->fin_off == 0) && !(fin->fin_fl & FI_SHORT)) { 258292685Sdarrenr if (fin->fin_p == IPPROTO_TCP) 258353642Sguido nflags = IPN_TCP; 258492685Sdarrenr else if (fin->fin_p == IPPROTO_UDP) 258553642Sguido nflags = IPN_UDP; 258653642Sguido if ((nflags & IPN_TCPUDP)) { 258753642Sguido tcp = (tcphdr_t *)fin->fin_dp; 258892685Sdarrenr sport = tcp->th_sport; 258953642Sguido dport = tcp->th_dport; 259053642Sguido } 259153642Sguido } 259253642Sguido 259392685Sdarrenr in = fin->fin_dst; 259453642Sguido /* make sure the source address is to be redirected */ 259592685Sdarrenr src = fin->fin_src; 259653642Sguido 259753642Sguido READ_ENTER(&ipf_nat); 259853642Sguido 259992685Sdarrenr if ((fin->fin_p == IPPROTO_ICMP) && 260060852Sdarrenr (nat = nat_icmp(ip, fin, &nflags, NAT_INBOUND))) 260180482Sdarrenr icmpset = 1; 260280482Sdarrenr else if ((fin->fin_fl & FI_FRAG) && 260353642Sguido (nat = ipfr_nat_knownfrag(ip, fin))) 260453642Sguido natadd = 0; 260592685Sdarrenr else if ((nat = nat_inlookup(fin, nflags|FI_WILDP|FI_WILDA, 260692685Sdarrenr (u_int)fin->fin_p, fin->fin_src, in, 0))) { 260753642Sguido nflags = nat->nat_flags; 260853642Sguido if ((nflags & (FI_W_SPORT|FI_W_DPORT)) != 0) { 260953642Sguido if ((nat->nat_oport != sport) && (nflags & FI_W_DPORT)) 261053642Sguido nat->nat_oport = sport; 261192685Sdarrenr if ((nat->nat_outport != dport) && 261253642Sguido (nflags & FI_W_SPORT)) 261353642Sguido nat->nat_outport = dport; 261453642Sguido nat->nat_flags &= ~(FI_W_SPORT|FI_W_DPORT); 261553642Sguido nflags = nat->nat_flags; 261667853Sdarrenr nat_stats.ns_wilds--; 261753642Sguido } 261853642Sguido } else { 261953642Sguido RWLOCK_EXIT(&ipf_nat); 262092685Sdarrenr 262192685Sdarrenr msk = 0xffffffff; 262292685Sdarrenr i = 32; 262392685Sdarrenr 262453642Sguido WRITE_ENTER(&ipf_nat); 262553642Sguido /* 262653642Sguido * If there is no current entry in the nat table for this IP#, 262753642Sguido * create one for it (if there is a matching rule). 262853642Sguido */ 262953642Sguidomaskloop: 263053642Sguido iph = in.s_addr & htonl(msk); 263160852Sdarrenr hv = NAT_HASH_FN(iph, 0, ipf_rdrrules_sz); 263260852Sdarrenr for (np = rdr_rules[hv]; np; np = np->in_rnext) { 263360852Sdarrenr if ((np->in_ifp && (np->in_ifp != ifp)) || 263492685Sdarrenr (np->in_p && (np->in_p != fin->fin_p)) || 263560852Sdarrenr (np->in_flags && !(nflags & np->in_flags))) 263660852Sdarrenr continue; 263760852Sdarrenr if (np->in_flags & IPN_FILTER) { 263860852Sdarrenr if (!nat_match(fin, np, ip)) 263960852Sdarrenr continue; 264060852Sdarrenr } else if ((in.s_addr & np->in_outmsk) != np->in_outip) 264160852Sdarrenr continue; 264292685Sdarrenr if ((!np->in_pmin || (np->in_flags & IPN_FILTER) || 264360852Sdarrenr ((ntohs(np->in_pmax) >= ntohs(dport)) && 264460852Sdarrenr (ntohs(dport) >= ntohs(np->in_pmin))))) 264592685Sdarrenr if ((nat = nat_new(fin, ip, np, NULL, nflags, 264653642Sguido NAT_INBOUND))) { 264753642Sguido np->in_hits++; 264853642Sguido break; 264953642Sguido } 265060852Sdarrenr } 265160852Sdarrenr 265253642Sguido if ((np == NULL) && (i > 0)) { 265353642Sguido do { 265453642Sguido i--; 265553642Sguido msk <<= 1; 265653642Sguido } while ((i >= 0) && ((rdr_masks & (1 << i)) == 0)); 265753642Sguido if (i >= 0) 265853642Sguido goto maskloop; 265953642Sguido } 266053642Sguido MUTEX_DOWNGRADE(&ipf_nat); 266153642Sguido } 266272006Sdarrenr 266372006Sdarrenr /* 266472006Sdarrenr * NOTE: ipf_nat must now only be held as a read lock 266572006Sdarrenr */ 266653642Sguido if (nat) { 266753642Sguido np = nat->nat_ptr; 266853642Sguido fin->fin_fr = nat->nat_fr; 266980482Sdarrenr if (natadd && (fin->fin_fl & FI_FRAG) && np) 267053642Sguido ipfr_nat_newfrag(ip, fin, 0, nat); 267192685Sdarrenr if (np && (np->in_apr != NULL) && (np->in_dport == 0 || 267292685Sdarrenr (tcp != NULL && sport == np->in_dport))) { 267360852Sdarrenr i = appr_check(ip, fin, nat); 267460852Sdarrenr if (i == -1) { 267560852Sdarrenr RWLOCK_EXIT(&ipf_nat); 267660852Sdarrenr return i; 267760852Sdarrenr } 267860852Sdarrenr } 267953642Sguido 268060852Sdarrenr MUTEX_ENTER(&nat->nat_lock); 268192685Sdarrenr if (fin->fin_p != IPPROTO_TCP) { 268292685Sdarrenr if (np && np->in_age[0]) 268392685Sdarrenr nat->nat_age = np->in_age[0]; 268492685Sdarrenr else if (!icmpset && (fin->fin_p == IPPROTO_ICMP)) 268592685Sdarrenr nat->nat_age = fr_defnaticmpage; 268692685Sdarrenr else 268792685Sdarrenr nat->nat_age = fr_defnatage; 268892685Sdarrenr } 268953642Sguido nat->nat_bytes += ip->ip_len; 269053642Sguido nat->nat_pkts++; 269160852Sdarrenr MUTEX_EXIT(&nat->nat_lock); 269253642Sguido ip->ip_dst = nat->nat_inip; 269360852Sdarrenr fin->fin_fi.fi_daddr = nat->nat_inip.s_addr; 269453642Sguido 269553642Sguido /* 269653642Sguido * Fix up checksums, not by recalculating them, but 269753642Sguido * simply computing adjustments. 269853642Sguido */ 269992685Sdarrenr#if (SOLARIS || defined(__sgi)) && defined(_KERNEL) 270053642Sguido if (nat->nat_dir == NAT_OUTBOUND) 270180482Sdarrenr fix_incksum(fin, &ip->ip_sum, nat->nat_ipsumd); 270253642Sguido else 270380482Sdarrenr fix_outcksum(fin, &ip->ip_sum, nat->nat_ipsumd); 270453642Sguido#endif 270580482Sdarrenr if ((fin->fin_off == 0) && !(fin->fin_fl & FI_SHORT)) { 270653642Sguido 270792685Sdarrenr if ((nat->nat_inport != 0) && (tcp != NULL)) { 270853642Sguido tcp->th_dport = nat->nat_inport; 270953642Sguido fin->fin_data[1] = ntohs(tcp->th_dport); 271053642Sguido } 271153642Sguido 271292685Sdarrenr if (fin->fin_p == IPPROTO_TCP) { 271353642Sguido csump = &tcp->th_sum; 271460852Sdarrenr MUTEX_ENTER(&nat->nat_lock); 271553642Sguido fr_tcp_age(&nat->nat_age, 271695418Sdarrenr nat->nat_tcpstate, fin, 0, 0); 271753642Sguido if (nat->nat_age < fr_defnaticmpage) 271853642Sguido nat->nat_age = fr_defnaticmpage; 271953642Sguido#ifdef LARGE_NAT 272060852Sdarrenr else if (nat->nat_age > fr_defnatage) 272160852Sdarrenr nat->nat_age = fr_defnatage; 272253642Sguido#endif 272353642Sguido /* 272453642Sguido * Increase this because we may have 272553642Sguido * "keep state" following this too and 272653642Sguido * packet storms can occur if this is 272753642Sguido * removed too quickly. 272853642Sguido */ 272953642Sguido if (nat->nat_age == fr_tcpclosed) 273053642Sguido nat->nat_age = fr_tcplastack; 273160852Sdarrenr MUTEX_EXIT(&nat->nat_lock); 273292685Sdarrenr } else if (fin->fin_p == IPPROTO_UDP) { 273353642Sguido udphdr_t *udp = (udphdr_t *)tcp; 273453642Sguido 273553642Sguido if (udp->uh_sum) 273653642Sguido csump = &udp->uh_sum; 273753642Sguido } 273860852Sdarrenr 273953642Sguido if (csump) { 274053642Sguido if (nat->nat_dir == NAT_OUTBOUND) 274192685Sdarrenr fix_incksum(fin, csump, 274292685Sdarrenr nat->nat_sumd[0]); 274353642Sguido else 274492685Sdarrenr fix_outcksum(fin, csump, 274592685Sdarrenr nat->nat_sumd[0]); 274653642Sguido } 274753642Sguido } 274860852Sdarrenr ATOMIC_INCL(nat_stats.ns_mapped[0]); 274953642Sguido RWLOCK_EXIT(&ipf_nat); /* READ */ 275053642Sguido return 1; 275153642Sguido } 275253642Sguido RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ 275353642Sguido return 0; 275453642Sguido} 275553642Sguido 275653642Sguido 275753642Sguido/* 275853642Sguido * Free all memory used by NAT structures allocated at runtime. 275953642Sguido */ 276053642Sguidovoid ip_natunload() 276153642Sguido{ 276253642Sguido WRITE_ENTER(&ipf_nat); 276353642Sguido (void) nat_clearlist(); 276453642Sguido (void) nat_flushtable(); 276553642Sguido RWLOCK_EXIT(&ipf_nat); 276653642Sguido 276753642Sguido if (nat_table[0] != NULL) { 276853642Sguido KFREES(nat_table[0], sizeof(nat_t *) * ipf_nattable_sz); 276953642Sguido nat_table[0] = NULL; 277053642Sguido } 277153642Sguido if (nat_table[1] != NULL) { 277253642Sguido KFREES(nat_table[1], sizeof(nat_t *) * ipf_nattable_sz); 277353642Sguido nat_table[1] = NULL; 277453642Sguido } 277553642Sguido if (nat_rules != NULL) { 277653642Sguido KFREES(nat_rules, sizeof(ipnat_t *) * ipf_natrules_sz); 277753642Sguido nat_rules = NULL; 277853642Sguido } 277953642Sguido if (rdr_rules != NULL) { 278053642Sguido KFREES(rdr_rules, sizeof(ipnat_t *) * ipf_rdrrules_sz); 278153642Sguido rdr_rules = NULL; 278253642Sguido } 278360852Sdarrenr if (maptable != NULL) { 278460852Sdarrenr KFREES(maptable, sizeof(hostmap_t *) * ipf_hostmap_sz); 278560852Sdarrenr maptable = NULL; 278660852Sdarrenr } 278753642Sguido} 278853642Sguido 278953642Sguido 279053642Sguido/* 279153642Sguido * Slowly expire held state for NAT entries. Timeouts are set in 279253642Sguido * expectation of this being called twice per second. 279353642Sguido */ 279453642Sguidovoid ip_natexpire() 279553642Sguido{ 279653642Sguido register struct nat *nat, **natp; 279753642Sguido#if defined(_KERNEL) && !SOLARIS 279853642Sguido int s; 279953642Sguido#endif 280053642Sguido 280153642Sguido SPL_NET(s); 280253642Sguido WRITE_ENTER(&ipf_nat); 280353642Sguido for (natp = &nat_instances; (nat = *natp); ) { 280453642Sguido nat->nat_age--; 280553642Sguido if (nat->nat_age) { 280653642Sguido natp = &nat->nat_next; 280753642Sguido continue; 280853642Sguido } 280953642Sguido *natp = nat->nat_next; 281053642Sguido#ifdef IPFILTER_LOG 281153642Sguido nat_log(nat, NL_EXPIRE); 281253642Sguido#endif 281353642Sguido nat_delete(nat); 281453642Sguido nat_stats.ns_expire++; 281553642Sguido } 281653642Sguido RWLOCK_EXIT(&ipf_nat); 281753642Sguido SPL_X(s); 281853642Sguido} 281953642Sguido 282053642Sguido 282153642Sguido/* 282253642Sguido */ 282353642Sguidovoid ip_natsync(ifp) 282453642Sguidovoid *ifp; 282553642Sguido{ 282653642Sguido register ipnat_t *n; 282753642Sguido register nat_t *nat; 282853642Sguido register u_32_t sum1, sum2, sumd; 282953642Sguido struct in_addr in; 283053642Sguido ipnat_t *np; 283153642Sguido void *ifp2; 283253642Sguido#if defined(_KERNEL) && !SOLARIS 283353642Sguido int s; 283453642Sguido#endif 283553642Sguido 283653642Sguido /* 283753642Sguido * Change IP addresses for NAT sessions for any protocol except TCP 283853642Sguido * since it will break the TCP connection anyway. 283953642Sguido */ 284053642Sguido SPL_NET(s); 284153642Sguido WRITE_ENTER(&ipf_nat); 284253642Sguido for (nat = nat_instances; nat; nat = nat->nat_next) 284353642Sguido if (((ifp == NULL) || (ifp == nat->nat_ifp)) && 284453642Sguido !(nat->nat_flags & IPN_TCP) && (np = nat->nat_ptr) && 284553642Sguido (np->in_outmsk == 0xffffffff) && !np->in_nip) { 284653642Sguido ifp2 = nat->nat_ifp; 284753642Sguido /* 284853642Sguido * Change the map-to address to be the same as the 284953642Sguido * new one. 285053642Sguido */ 285153642Sguido sum1 = nat->nat_outip.s_addr; 285260852Sdarrenr if (fr_ifpaddr(4, ifp2, &in) != -1) 285355929Sguido nat->nat_outip = in; 285453642Sguido sum2 = nat->nat_outip.s_addr; 285553642Sguido 285653642Sguido if (sum1 == sum2) 285753642Sguido continue; 285853642Sguido /* 285953642Sguido * Readjust the checksum adjustment to take into 286053642Sguido * account the new IP#. 286153642Sguido */ 286253642Sguido CALC_SUMD(sum1, sum2, sumd); 286355929Sguido /* XXX - dont change for TCP when solaris does 286455929Sguido * hardware checksumming. 286555929Sguido */ 286655929Sguido sumd += nat->nat_sumd[0]; 286755929Sguido nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 286855929Sguido nat->nat_sumd[1] = nat->nat_sumd[0]; 286953642Sguido } 287053642Sguido 287153642Sguido for (n = nat_list; (n != NULL); n = n->in_next) 287255929Sguido if (n->in_ifp == ifp) { 287360852Sdarrenr n->in_ifp = (void *)GETUNIT(n->in_ifname, 4); 287460852Sdarrenr if (!n->in_ifp) 287560852Sdarrenr n->in_ifp = (void *)-1; 287660852Sdarrenr } 287760852Sdarrenr RWLOCK_EXIT(&ipf_nat); 287853642Sguido SPL_X(s); 287953642Sguido} 288053642Sguido 288153642Sguido 288253642Sguido#ifdef IPFILTER_LOG 288353642Sguidovoid nat_log(nat, type) 288453642Sguidostruct nat *nat; 288553642Sguidou_int type; 288653642Sguido{ 288753642Sguido struct ipnat *np; 288853642Sguido struct natlog natl; 288953642Sguido void *items[1]; 289053642Sguido size_t sizes[1]; 289153642Sguido int rulen, types[1]; 289253642Sguido 289353642Sguido natl.nl_inip = nat->nat_inip; 289453642Sguido natl.nl_outip = nat->nat_outip; 289553642Sguido natl.nl_origip = nat->nat_oip; 289653642Sguido natl.nl_bytes = nat->nat_bytes; 289753642Sguido natl.nl_pkts = nat->nat_pkts; 289853642Sguido natl.nl_origport = nat->nat_oport; 289953642Sguido natl.nl_inport = nat->nat_inport; 290053642Sguido natl.nl_outport = nat->nat_outport; 290157096Sguido natl.nl_p = nat->nat_p; 290253642Sguido natl.nl_type = type; 290353642Sguido natl.nl_rule = -1; 290453642Sguido#ifndef LARGE_NAT 290553642Sguido if (nat->nat_ptr != NULL) { 290653642Sguido for (rulen = 0, np = nat_list; np; np = np->in_next, rulen++) 290753642Sguido if (np == nat->nat_ptr) { 290853642Sguido natl.nl_rule = rulen; 290953642Sguido break; 291053642Sguido } 291153642Sguido } 291253642Sguido#endif 291353642Sguido items[0] = &natl; 291453642Sguido sizes[0] = sizeof(natl); 291553642Sguido types[0] = 0; 291653642Sguido 291753642Sguido (void) ipllog(IPL_LOGNAT, NULL, items, sizes, types, 1); 291853642Sguido} 291953642Sguido#endif 292092685Sdarrenr 292192685Sdarrenr 292292685Sdarrenr#if defined(__OpenBSD__) 292392685Sdarrenrvoid nat_ifdetach(ifp) 292492685Sdarrenrvoid *ifp; 292592685Sdarrenr{ 292692685Sdarrenr frsync(); 292792685Sdarrenr return; 292892685Sdarrenr} 292992685Sdarrenr#endif 2930