ip_nat.c revision 172776
1145522Sdarrenr/* $FreeBSD: head/sys/contrib/ipfilter/netinet/ip_nat.c 172776 2007-10-18 21:52:14Z darrenr $ */ 2145522Sdarrenr 353642Sguido/* 4145522Sdarrenr * Copyright (C) 1995-2003 by Darren Reed. 553642Sguido * 680482Sdarrenr * See the IPFILTER.LICENCE file for details on licencing. 753642Sguido */ 8145522Sdarrenr#if defined(KERNEL) || defined(_KERNEL) 9145522Sdarrenr# undef KERNEL 10145522Sdarrenr# undef _KERNEL 11145522Sdarrenr# define KERNEL 1 12145522Sdarrenr# define _KERNEL 1 1353642Sguido#endif 1453642Sguido#include <sys/errno.h> 1553642Sguido#include <sys/types.h> 1653642Sguido#include <sys/param.h> 1753642Sguido#include <sys/time.h> 1853642Sguido#include <sys/file.h> 19170268Sdarrenr#if defined(_KERNEL) && defined(__NetBSD_Version__) && \ 20170268Sdarrenr (__NetBSD_Version__ >= 399002000) 21170268Sdarrenr# include <sys/kauth.h> 22170268Sdarrenr#endif 2353642Sguido#if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \ 2453642Sguido defined(_KERNEL) 25170268Sdarrenr#if defined(__NetBSD_Version__) && (__NetBSD_Version__ < 399001400) 26170268Sdarrenr# include "opt_ipfilter_log.h" 27170268Sdarrenr# else 28170268Sdarrenr# include "opt_ipfilter.h" 29170268Sdarrenr# endif 3053642Sguido#endif 31145522Sdarrenr#if !defined(_KERNEL) 3253642Sguido# include <stdio.h> 3353642Sguido# include <string.h> 3453642Sguido# include <stdlib.h> 35145522Sdarrenr# define _KERNEL 36145522Sdarrenr# ifdef __OpenBSD__ 37145522Sdarrenrstruct file; 38145522Sdarrenr# endif 39145522Sdarrenr# include <sys/uio.h> 40145522Sdarrenr# undef _KERNEL 4153642Sguido#endif 42145522Sdarrenr#if defined(_KERNEL) && (__FreeBSD_version >= 220000) 4353642Sguido# include <sys/filio.h> 4453642Sguido# include <sys/fcntl.h> 4553642Sguido#else 4653642Sguido# include <sys/ioctl.h> 4753642Sguido#endif 48153876Sguido#if !defined(AIX) 49153876Sguido# include <sys/fcntl.h> 50153876Sguido#endif 51145522Sdarrenr#if !defined(linux) 5253642Sguido# include <sys/protosw.h> 5353642Sguido#endif 5453642Sguido#include <sys/socket.h> 55145522Sdarrenr#if defined(_KERNEL) 5653642Sguido# include <sys/systm.h> 57145522Sdarrenr# if !defined(__SVR4) && !defined(__svr4__) 5853642Sguido# include <sys/mbuf.h> 5953642Sguido# endif 60145522Sdarrenr#endif 61145522Sdarrenr#if defined(__SVR4) || defined(__svr4__) 6253642Sguido# include <sys/filio.h> 6353642Sguido# include <sys/byteorder.h> 6453642Sguido# ifdef _KERNEL 6553642Sguido# include <sys/dditypes.h> 6653642Sguido# endif 6753642Sguido# include <sys/stream.h> 6853642Sguido# include <sys/kmem.h> 6953642Sguido#endif 7053642Sguido#if __FreeBSD_version >= 300000 7153642Sguido# include <sys/queue.h> 7253642Sguido#endif 7353642Sguido#include <net/if.h> 7453642Sguido#if __FreeBSD_version >= 300000 7553642Sguido# include <net/if_var.h> 7653642Sguido# if defined(_KERNEL) && !defined(IPFILTER_LKM) 7753642Sguido# include "opt_ipfilter.h" 7853642Sguido# endif 7953642Sguido#endif 8053642Sguido#ifdef sun 8153642Sguido# include <net/af.h> 8253642Sguido#endif 8353642Sguido#include <net/route.h> 8453642Sguido#include <netinet/in.h> 8553642Sguido#include <netinet/in_systm.h> 8653642Sguido#include <netinet/ip.h> 8753642Sguido 8853642Sguido#ifdef RFC1825 8953642Sguido# include <vpn/md5.h> 9053642Sguido# include <vpn/ipsec.h> 9153642Sguidoextern struct ifnet vpnif; 9253642Sguido#endif 9353642Sguido 94145522Sdarrenr#if !defined(linux) 9553642Sguido# include <netinet/ip_var.h> 9653642Sguido#endif 9753642Sguido#include <netinet/tcp.h> 9853642Sguido#include <netinet/udp.h> 9953642Sguido#include <netinet/ip_icmp.h> 10053642Sguido#include "netinet/ip_compat.h" 10153642Sguido#include <netinet/tcpip.h> 10253642Sguido#include "netinet/ip_fil.h" 10353642Sguido#include "netinet/ip_nat.h" 10453642Sguido#include "netinet/ip_frag.h" 10553642Sguido#include "netinet/ip_state.h" 10692685Sdarrenr#include "netinet/ip_proxy.h" 107145522Sdarrenr#ifdef IPFILTER_SYNC 108145522Sdarrenr#include "netinet/ip_sync.h" 109145522Sdarrenr#endif 11053642Sguido#if (__FreeBSD_version >= 300000) 11153642Sguido# include <sys/malloc.h> 11253642Sguido#endif 113145522Sdarrenr/* END OF INCLUDES */ 114145522Sdarrenr 11553642Sguido#undef SOCKADDR_IN 11653642Sguido#define SOCKADDR_IN struct sockaddr_in 11753642Sguido 11880482Sdarrenr#if !defined(lint) 11980482Sdarrenrstatic const char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed"; 12080482Sdarrenrstatic const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_nat.c 172776 2007-10-18 21:52:14Z darrenr $"; 121172776Sdarrenr/* static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.195.2.102 2007/10/16 10:08:10 darrenr Exp $"; */ 12280482Sdarrenr#endif 12380482Sdarrenr 124145522Sdarrenr 125145522Sdarrenr/* ======================================================================== */ 126145522Sdarrenr/* How the NAT is organised and works. */ 127145522Sdarrenr/* */ 128145522Sdarrenr/* Inside (interface y) NAT Outside (interface x) */ 129145522Sdarrenr/* -------------------- -+- ------------------------------------- */ 130145522Sdarrenr/* Packet going | out, processsed by fr_checknatout() for x */ 131145522Sdarrenr/* ------------> | ------------> */ 132145522Sdarrenr/* src=10.1.1.1 | src=192.1.1.1 */ 133145522Sdarrenr/* | */ 134145522Sdarrenr/* | in, processed by fr_checknatin() for x */ 135145522Sdarrenr/* <------------ | <------------ */ 136145522Sdarrenr/* dst=10.1.1.1 | dst=192.1.1.1 */ 137145522Sdarrenr/* -------------------- -+- ------------------------------------- */ 138145522Sdarrenr/* fr_checknatout() - changes ip_src and if required, sport */ 139145522Sdarrenr/* - creates a new mapping, if required. */ 140145522Sdarrenr/* fr_checknatin() - changes ip_dst and if required, dport */ 141145522Sdarrenr/* */ 142145522Sdarrenr/* In the NAT table, internal source is recorded as "in" and externally */ 143145522Sdarrenr/* seen as "out". */ 144145522Sdarrenr/* ======================================================================== */ 145145522Sdarrenr 146145522Sdarrenr 14753642Sguidonat_t **nat_table[2] = { NULL, NULL }, 14853642Sguido *nat_instances = NULL; 14953642Sguidoipnat_t *nat_list = NULL; 150130886Sdarrenru_int ipf_nattable_max = NAT_TABLE_MAX; 15153642Sguidou_int ipf_nattable_sz = NAT_TABLE_SZ; 15253642Sguidou_int ipf_natrules_sz = NAT_SIZE; 15353642Sguidou_int ipf_rdrrules_sz = RDR_SIZE; 15460852Sdarrenru_int ipf_hostmap_sz = HOSTMAP_SIZE; 155145522Sdarrenru_int fr_nat_maxbucket = 0, 156145522Sdarrenr fr_nat_maxbucket_reset = 1; 15753642Sguidou_32_t nat_masks = 0; 15853642Sguidou_32_t rdr_masks = 0; 159170268Sdarrenru_long nat_last_force_flush = 0; 16053642Sguidoipnat_t **nat_rules = NULL; 16153642Sguidoipnat_t **rdr_rules = NULL; 162170268Sdarrenrhostmap_t **ipf_hm_maptable = NULL; 163170268Sdarrenrhostmap_t *ipf_hm_maplist = NULL; 164145522Sdarrenripftq_t nat_tqb[IPF_TCP_NSTATES]; 165145522Sdarrenripftq_t nat_udptq; 166145522Sdarrenripftq_t nat_icmptq; 167145522Sdarrenripftq_t nat_iptq; 168145522Sdarrenripftq_t *nat_utqe = NULL; 169170268Sdarrenrint fr_nat_doflush = 0; 170145522Sdarrenr#ifdef IPFILTER_LOG 171145522Sdarrenrint nat_logging = 1; 172145522Sdarrenr#else 173145522Sdarrenrint nat_logging = 0; 174145522Sdarrenr#endif 17553642Sguido 17653642Sguidou_long fr_defnatage = DEF_NAT_AGE, 177145522Sdarrenr fr_defnatipage = 120, /* 60 seconds */ 17853642Sguido fr_defnaticmpage = 6; /* 3 seconds */ 17964580Sdarrenrnatstat_t nat_stats; 18060852Sdarrenrint fr_nat_lock = 0; 181145522Sdarrenrint fr_nat_init = 0; 182172776Sdarrenr#if SOLARIS && !defined(_INET_IP_STACK_H) 183145522Sdarrenrextern int pfil_delayed_copy; 18453642Sguido#endif 18553642Sguido 186170268Sdarrenrstatic int nat_flush_entry __P((void *)); 18753642Sguidostatic int nat_flushtable __P((void)); 188145522Sdarrenrstatic int nat_clearlist __P((void)); 18960852Sdarrenrstatic void nat_addnat __P((struct ipnat *)); 19060852Sdarrenrstatic void nat_addrdr __P((struct ipnat *)); 19153642Sguidostatic void nat_delrdr __P((struct ipnat *)); 19253642Sguidostatic void nat_delnat __P((struct ipnat *)); 19360852Sdarrenrstatic int fr_natgetent __P((caddr_t)); 19460852Sdarrenrstatic int fr_natgetsz __P((caddr_t)); 195145522Sdarrenrstatic int fr_natputent __P((caddr_t, int)); 196170268Sdarrenrstatic int nat_extraflush __P((int)); 197172776Sdarrenrstatic int nat_gettable __P((char *)); 198145522Sdarrenrstatic void nat_tabmove __P((nat_t *)); 199145522Sdarrenrstatic int nat_match __P((fr_info_t *, ipnat_t *)); 200145522Sdarrenrstatic INLINE int nat_newmap __P((fr_info_t *, nat_t *, natinfo_t *)); 201145522Sdarrenrstatic INLINE int nat_newrdr __P((fr_info_t *, nat_t *, natinfo_t *)); 20260852Sdarrenrstatic hostmap_t *nat_hostmap __P((ipnat_t *, struct in_addr, 203145522Sdarrenr struct in_addr, struct in_addr, u_32_t)); 204153876Sguidostatic int nat_icmpquerytype4 __P((int)); 205145522Sdarrenrstatic int nat_siocaddnat __P((ipnat_t *, ipnat_t **, int)); 206145522Sdarrenrstatic void nat_siocdelnat __P((ipnat_t *, ipnat_t **, int)); 207153876Sguidostatic int nat_finalise __P((fr_info_t *, nat_t *, natinfo_t *, 208145522Sdarrenr tcphdr_t *, nat_t **, int)); 209161356Sguidostatic int nat_resolverule __P((ipnat_t *)); 210145522Sdarrenrstatic nat_t *fr_natclone __P((fr_info_t *, nat_t *)); 211110916Sdarrenrstatic void nat_mssclamp __P((tcphdr_t *, u_32_t, fr_info_t *, u_short *)); 212153876Sguidostatic int nat_wildok __P((nat_t *, int, int, int, int)); 213170268Sdarrenrstatic int nat_getnext __P((ipftoken_t *, ipfgeniter_t *)); 214170268Sdarrenrstatic int nat_iterator __P((ipftoken_t *, ipfgeniter_t *)); 21553642Sguido 21653642Sguido 217145522Sdarrenr/* ------------------------------------------------------------------------ */ 218145522Sdarrenr/* Function: fr_natinit */ 219145522Sdarrenr/* Returns: int - 0 == success, -1 == failure */ 220145522Sdarrenr/* Parameters: Nil */ 221145522Sdarrenr/* */ 222145522Sdarrenr/* Initialise all of the NAT locks, tables and other structures. */ 223145522Sdarrenr/* ------------------------------------------------------------------------ */ 224145522Sdarrenrint fr_natinit() 22553642Sguido{ 226145522Sdarrenr int i; 227145522Sdarrenr 22853642Sguido KMALLOCS(nat_table[0], nat_t **, sizeof(nat_t *) * ipf_nattable_sz); 22953642Sguido if (nat_table[0] != NULL) 23053642Sguido bzero((char *)nat_table[0], ipf_nattable_sz * sizeof(nat_t *)); 23153642Sguido else 23253642Sguido return -1; 23353642Sguido 23453642Sguido KMALLOCS(nat_table[1], nat_t **, sizeof(nat_t *) * ipf_nattable_sz); 23553642Sguido if (nat_table[1] != NULL) 23653642Sguido bzero((char *)nat_table[1], ipf_nattable_sz * sizeof(nat_t *)); 23753642Sguido else 238145522Sdarrenr return -2; 23953642Sguido 24053642Sguido KMALLOCS(nat_rules, ipnat_t **, sizeof(ipnat_t *) * ipf_natrules_sz); 24153642Sguido if (nat_rules != NULL) 24253642Sguido bzero((char *)nat_rules, ipf_natrules_sz * sizeof(ipnat_t *)); 24353642Sguido else 244145522Sdarrenr return -3; 24553642Sguido 24653642Sguido KMALLOCS(rdr_rules, ipnat_t **, sizeof(ipnat_t *) * ipf_rdrrules_sz); 24753642Sguido if (rdr_rules != NULL) 24853642Sguido bzero((char *)rdr_rules, ipf_rdrrules_sz * sizeof(ipnat_t *)); 24953642Sguido else 250145522Sdarrenr return -4; 25160852Sdarrenr 252170268Sdarrenr KMALLOCS(ipf_hm_maptable, hostmap_t **, \ 253170268Sdarrenr sizeof(hostmap_t *) * ipf_hostmap_sz); 254170268Sdarrenr if (ipf_hm_maptable != NULL) 255170268Sdarrenr bzero((char *)ipf_hm_maptable, 256170268Sdarrenr sizeof(hostmap_t *) * ipf_hostmap_sz); 25760852Sdarrenr else 258145522Sdarrenr return -5; 259170268Sdarrenr ipf_hm_maplist = NULL; 260145522Sdarrenr 261145522Sdarrenr KMALLOCS(nat_stats.ns_bucketlen[0], u_long *, 262145522Sdarrenr ipf_nattable_sz * sizeof(u_long)); 263145522Sdarrenr if (nat_stats.ns_bucketlen[0] == NULL) 264145522Sdarrenr return -6; 265145522Sdarrenr bzero((char *)nat_stats.ns_bucketlen[0], 266145522Sdarrenr ipf_nattable_sz * sizeof(u_long)); 267145522Sdarrenr 268145522Sdarrenr KMALLOCS(nat_stats.ns_bucketlen[1], u_long *, 269145522Sdarrenr ipf_nattable_sz * sizeof(u_long)); 270145522Sdarrenr if (nat_stats.ns_bucketlen[1] == NULL) 271145522Sdarrenr return -7; 272145522Sdarrenr 273145522Sdarrenr bzero((char *)nat_stats.ns_bucketlen[1], 274145522Sdarrenr ipf_nattable_sz * sizeof(u_long)); 275145522Sdarrenr 276145522Sdarrenr if (fr_nat_maxbucket == 0) { 277145522Sdarrenr for (i = ipf_nattable_sz; i > 0; i >>= 1) 278145522Sdarrenr fr_nat_maxbucket++; 279145522Sdarrenr fr_nat_maxbucket *= 2; 280145522Sdarrenr } 281145522Sdarrenr 282145522Sdarrenr fr_sttab_init(nat_tqb); 283145522Sdarrenr /* 284145522Sdarrenr * Increase this because we may have "keep state" following this too 285145522Sdarrenr * and packet storms can occur if this is removed too quickly. 286145522Sdarrenr */ 287145522Sdarrenr nat_tqb[IPF_TCPS_CLOSED].ifq_ttl = fr_tcplastack; 288145522Sdarrenr nat_tqb[IPF_TCP_NSTATES - 1].ifq_next = &nat_udptq; 289145522Sdarrenr nat_udptq.ifq_ttl = fr_defnatage; 290145522Sdarrenr nat_udptq.ifq_ref = 1; 291145522Sdarrenr nat_udptq.ifq_head = NULL; 292145522Sdarrenr nat_udptq.ifq_tail = &nat_udptq.ifq_head; 293145522Sdarrenr MUTEX_INIT(&nat_udptq.ifq_lock, "nat ipftq udp tab"); 294145522Sdarrenr nat_udptq.ifq_next = &nat_icmptq; 295145522Sdarrenr nat_icmptq.ifq_ttl = fr_defnaticmpage; 296145522Sdarrenr nat_icmptq.ifq_ref = 1; 297145522Sdarrenr nat_icmptq.ifq_head = NULL; 298145522Sdarrenr nat_icmptq.ifq_tail = &nat_icmptq.ifq_head; 299145522Sdarrenr MUTEX_INIT(&nat_icmptq.ifq_lock, "nat icmp ipftq tab"); 300145522Sdarrenr nat_icmptq.ifq_next = &nat_iptq; 301145522Sdarrenr nat_iptq.ifq_ttl = fr_defnatipage; 302145522Sdarrenr nat_iptq.ifq_ref = 1; 303145522Sdarrenr nat_iptq.ifq_head = NULL; 304145522Sdarrenr nat_iptq.ifq_tail = &nat_iptq.ifq_head; 305145522Sdarrenr MUTEX_INIT(&nat_iptq.ifq_lock, "nat ip ipftq tab"); 306145522Sdarrenr nat_iptq.ifq_next = NULL; 307145522Sdarrenr 308145522Sdarrenr for (i = 0; i < IPF_TCP_NSTATES; i++) { 309145522Sdarrenr if (nat_tqb[i].ifq_ttl < fr_defnaticmpage) 310145522Sdarrenr nat_tqb[i].ifq_ttl = fr_defnaticmpage; 311145522Sdarrenr#ifdef LARGE_NAT 312145522Sdarrenr else if (nat_tqb[i].ifq_ttl > fr_defnatage) 313145522Sdarrenr nat_tqb[i].ifq_ttl = fr_defnatage; 314145522Sdarrenr#endif 315145522Sdarrenr } 316145522Sdarrenr 317145522Sdarrenr /* 318145522Sdarrenr * Increase this because we may have "keep state" following 319145522Sdarrenr * this too and packet storms can occur if this is removed 320145522Sdarrenr * too quickly. 321145522Sdarrenr */ 322145522Sdarrenr nat_tqb[IPF_TCPS_CLOSED].ifq_ttl = nat_tqb[IPF_TCPS_LAST_ACK].ifq_ttl; 323145522Sdarrenr 324145522Sdarrenr RWLOCK_INIT(&ipf_nat, "ipf IP NAT rwlock"); 325145522Sdarrenr RWLOCK_INIT(&ipf_natfrag, "ipf IP NAT-Frag rwlock"); 326145522Sdarrenr MUTEX_INIT(&ipf_nat_new, "ipf nat new mutex"); 327145522Sdarrenr MUTEX_INIT(&ipf_natio, "ipf nat io mutex"); 328145522Sdarrenr 329145522Sdarrenr fr_nat_init = 1; 330145522Sdarrenr 33153642Sguido return 0; 33253642Sguido} 33353642Sguido 33453642Sguido 335145522Sdarrenr/* ------------------------------------------------------------------------ */ 336145522Sdarrenr/* Function: nat_addrdr */ 337145522Sdarrenr/* Returns: Nil */ 338145522Sdarrenr/* Parameters: n(I) - pointer to NAT rule to add */ 339145522Sdarrenr/* */ 340145522Sdarrenr/* Adds a redirect rule to the hash table of redirect rules and the list of */ 341145522Sdarrenr/* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ 342145522Sdarrenr/* use by redirect rules. */ 343145522Sdarrenr/* ------------------------------------------------------------------------ */ 34460852Sdarrenrstatic void nat_addrdr(n) 34553642Sguidoipnat_t *n; 34653642Sguido{ 34760852Sdarrenr ipnat_t **np; 34860852Sdarrenr u_32_t j; 34953642Sguido u_int hv; 35060852Sdarrenr int k; 35153642Sguido 352145522Sdarrenr k = count4bits(n->in_outmsk); 35360852Sdarrenr if ((k >= 0) && (k != 32)) 35460852Sdarrenr rdr_masks |= 1 << k; 35560852Sdarrenr j = (n->in_outip & n->in_outmsk); 35660852Sdarrenr hv = NAT_HASH_FN(j, 0, ipf_rdrrules_sz); 35760852Sdarrenr np = rdr_rules + hv; 35860852Sdarrenr while (*np != NULL) 35960852Sdarrenr np = &(*np)->in_rnext; 36060852Sdarrenr n->in_rnext = NULL; 36160852Sdarrenr n->in_prnext = np; 362145522Sdarrenr n->in_hv = hv; 36360852Sdarrenr *np = n; 36453642Sguido} 36553642Sguido 36653642Sguido 367145522Sdarrenr/* ------------------------------------------------------------------------ */ 368145522Sdarrenr/* Function: nat_addnat */ 369145522Sdarrenr/* Returns: Nil */ 370145522Sdarrenr/* Parameters: n(I) - pointer to NAT rule to add */ 371145522Sdarrenr/* */ 372145522Sdarrenr/* Adds a NAT map rule to the hash table of rules and the list of loaded */ 373145522Sdarrenr/* NAT rules. Updates the bitmask indicating which netmasks are in use by */ 374145522Sdarrenr/* redirect rules. */ 375145522Sdarrenr/* ------------------------------------------------------------------------ */ 37660852Sdarrenrstatic void nat_addnat(n) 37760852Sdarrenripnat_t *n; 37860852Sdarrenr{ 37960852Sdarrenr ipnat_t **np; 38060852Sdarrenr u_32_t j; 38160852Sdarrenr u_int hv; 38260852Sdarrenr int k; 38360852Sdarrenr 384145522Sdarrenr k = count4bits(n->in_inmsk); 38560852Sdarrenr if ((k >= 0) && (k != 32)) 38660852Sdarrenr nat_masks |= 1 << k; 38760852Sdarrenr j = (n->in_inip & n->in_inmsk); 38860852Sdarrenr hv = NAT_HASH_FN(j, 0, ipf_natrules_sz); 38960852Sdarrenr np = nat_rules + hv; 39060852Sdarrenr while (*np != NULL) 39160852Sdarrenr np = &(*np)->in_mnext; 39260852Sdarrenr n->in_mnext = NULL; 39360852Sdarrenr n->in_pmnext = np; 394145522Sdarrenr n->in_hv = hv; 39560852Sdarrenr *np = n; 39660852Sdarrenr} 39760852Sdarrenr 39860852Sdarrenr 399145522Sdarrenr/* ------------------------------------------------------------------------ */ 400145522Sdarrenr/* Function: nat_delrdr */ 401145522Sdarrenr/* Returns: Nil */ 402145522Sdarrenr/* Parameters: n(I) - pointer to NAT rule to delete */ 403145522Sdarrenr/* */ 404145522Sdarrenr/* Removes a redirect rule from the hash table of redirect rules. */ 405145522Sdarrenr/* ------------------------------------------------------------------------ */ 40660852Sdarrenrstatic void nat_delrdr(n) 40760852Sdarrenripnat_t *n; 40860852Sdarrenr{ 40960852Sdarrenr if (n->in_rnext) 41060852Sdarrenr n->in_rnext->in_prnext = n->in_prnext; 41160852Sdarrenr *n->in_prnext = n->in_rnext; 41260852Sdarrenr} 41360852Sdarrenr 41460852Sdarrenr 415145522Sdarrenr/* ------------------------------------------------------------------------ */ 416145522Sdarrenr/* Function: nat_delnat */ 417145522Sdarrenr/* Returns: Nil */ 418145522Sdarrenr/* Parameters: n(I) - pointer to NAT rule to delete */ 419145522Sdarrenr/* */ 420145522Sdarrenr/* Removes a NAT map rule from the hash table of NAT map rules. */ 421145522Sdarrenr/* ------------------------------------------------------------------------ */ 42253642Sguidostatic void nat_delnat(n) 42353642Sguidoipnat_t *n; 42453642Sguido{ 425145522Sdarrenr if (n->in_mnext != NULL) 42660852Sdarrenr n->in_mnext->in_pmnext = n->in_pmnext; 42760852Sdarrenr *n->in_pmnext = n->in_mnext; 42860852Sdarrenr} 42960852Sdarrenr 43060852Sdarrenr 431145522Sdarrenr/* ------------------------------------------------------------------------ */ 432145522Sdarrenr/* Function: nat_hostmap */ 433145522Sdarrenr/* Returns: struct hostmap* - NULL if no hostmap could be created, */ 434145522Sdarrenr/* else a pointer to the hostmapping to use */ 435145522Sdarrenr/* Parameters: np(I) - pointer to NAT rule */ 436145522Sdarrenr/* real(I) - real IP address */ 437145522Sdarrenr/* map(I) - mapped IP address */ 438145522Sdarrenr/* port(I) - destination port number */ 439145522Sdarrenr/* Write Locks: ipf_nat */ 440145522Sdarrenr/* */ 441145522Sdarrenr/* Check if an ip address has already been allocated for a given mapping */ 442145522Sdarrenr/* that is not doing port based translation. If is not yet allocated, then */ 443145522Sdarrenr/* create a new entry if a non-NULL NAT rule pointer has been supplied. */ 444145522Sdarrenr/* ------------------------------------------------------------------------ */ 445145522Sdarrenrstatic struct hostmap *nat_hostmap(np, src, dst, map, port) 44660852Sdarrenripnat_t *np; 447145522Sdarrenrstruct in_addr src; 448145522Sdarrenrstruct in_addr dst; 44960852Sdarrenrstruct in_addr map; 450145522Sdarrenru_32_t port; 45160852Sdarrenr{ 45260852Sdarrenr hostmap_t *hm; 45353642Sguido u_int hv; 45453642Sguido 455145522Sdarrenr hv = (src.s_addr ^ dst.s_addr); 456145522Sdarrenr hv += src.s_addr; 457145522Sdarrenr hv += dst.s_addr; 458145522Sdarrenr hv %= HOSTMAP_SIZE; 459170268Sdarrenr for (hm = ipf_hm_maptable[hv]; hm; hm = hm->hm_next) 460145522Sdarrenr if ((hm->hm_srcip.s_addr == src.s_addr) && 461145522Sdarrenr (hm->hm_dstip.s_addr == dst.s_addr) && 462145522Sdarrenr ((np == NULL) || (np == hm->hm_ipnat)) && 463145522Sdarrenr ((port == 0) || (port == hm->hm_port))) { 46460852Sdarrenr hm->hm_ref++; 46560852Sdarrenr return hm; 46660852Sdarrenr } 46760852Sdarrenr 468145522Sdarrenr if (np == NULL) 469145522Sdarrenr return NULL; 470145522Sdarrenr 47160852Sdarrenr KMALLOC(hm, hostmap_t *); 47260852Sdarrenr if (hm) { 473170268Sdarrenr hm->hm_next = ipf_hm_maplist; 474170268Sdarrenr hm->hm_pnext = &ipf_hm_maplist; 475170268Sdarrenr if (ipf_hm_maplist != NULL) 476170268Sdarrenr ipf_hm_maplist->hm_pnext = &hm->hm_next; 477170268Sdarrenr ipf_hm_maplist = hm; 478170268Sdarrenr hm->hm_hnext = ipf_hm_maptable[hv]; 479170268Sdarrenr hm->hm_phnext = ipf_hm_maptable + hv; 480170268Sdarrenr if (ipf_hm_maptable[hv] != NULL) 481170268Sdarrenr ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; 482170268Sdarrenr ipf_hm_maptable[hv] = hm; 48360852Sdarrenr hm->hm_ipnat = np; 484145522Sdarrenr hm->hm_srcip = src; 485145522Sdarrenr hm->hm_dstip = dst; 48660852Sdarrenr hm->hm_mapip = map; 48760852Sdarrenr hm->hm_ref = 1; 488145522Sdarrenr hm->hm_port = port; 48960852Sdarrenr } 49060852Sdarrenr return hm; 49153642Sguido} 49253642Sguido 49353642Sguido 494145522Sdarrenr/* ------------------------------------------------------------------------ */ 495170268Sdarrenr/* Function: fr_hostmapdel */ 496145522Sdarrenr/* Returns: Nil */ 497170268Sdarrenr/* Parameters: hmp(I) - pointer to hostmap structure pointer */ 498145522Sdarrenr/* Write Locks: ipf_nat */ 499145522Sdarrenr/* */ 500145522Sdarrenr/* Decrement the references to this hostmap structure by one. If this */ 501145522Sdarrenr/* reaches zero then remove it and free it. */ 502145522Sdarrenr/* ------------------------------------------------------------------------ */ 503170268Sdarrenrvoid fr_hostmapdel(hmp) 504170268Sdarrenrstruct hostmap **hmp; 50560852Sdarrenr{ 506170268Sdarrenr struct hostmap *hm; 507170268Sdarrenr 508170268Sdarrenr hm = *hmp; 509170268Sdarrenr *hmp = NULL; 510170268Sdarrenr 511145522Sdarrenr hm->hm_ref--; 51260852Sdarrenr if (hm->hm_ref == 0) { 513170268Sdarrenr if (hm->hm_hnext) 514170268Sdarrenr hm->hm_hnext->hm_phnext = hm->hm_phnext; 515170268Sdarrenr *hm->hm_phnext = hm->hm_hnext; 51660852Sdarrenr if (hm->hm_next) 51760852Sdarrenr hm->hm_next->hm_pnext = hm->hm_pnext; 51860852Sdarrenr *hm->hm_pnext = hm->hm_next; 51960852Sdarrenr KFREE(hm); 52060852Sdarrenr } 52160852Sdarrenr} 52260852Sdarrenr 52360852Sdarrenr 524145522Sdarrenr/* ------------------------------------------------------------------------ */ 525145522Sdarrenr/* Function: fix_outcksum */ 526145522Sdarrenr/* Returns: Nil */ 527145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 528145522Sdarrenr/* sp(I) - location of 16bit checksum to update */ 529145522Sdarrenr/* n((I) - amount to adjust checksum by */ 530145522Sdarrenr/* */ 531145522Sdarrenr/* Adjusts the 16bit checksum by "n" for packets going out. */ 532145522Sdarrenr/* ------------------------------------------------------------------------ */ 53380482Sdarrenrvoid fix_outcksum(fin, sp, n) 53480482Sdarrenrfr_info_t *fin; 53553642Sguidou_short *sp; 53653642Sguidou_32_t n; 53753642Sguido{ 538145522Sdarrenr u_short sumshort; 539145522Sdarrenr u_32_t sum1; 54053642Sguido 541145522Sdarrenr if (n == 0) 54253642Sguido return; 543145522Sdarrenr 544145522Sdarrenr if (n & NAT_HW_CKSUM) { 54580482Sdarrenr n &= 0xffff; 54680482Sdarrenr n += fin->fin_dlen; 54780482Sdarrenr n = (n & 0xffff) + (n >> 16); 54855929Sguido *sp = n & 0xffff; 54955929Sguido return; 55055929Sguido } 55153642Sguido sum1 = (~ntohs(*sp)) & 0xffff; 55253642Sguido sum1 += (n); 55353642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 55453642Sguido /* Again */ 55553642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 55653642Sguido sumshort = ~(u_short)sum1; 55753642Sguido *(sp) = htons(sumshort); 55853642Sguido} 55953642Sguido 56053642Sguido 561145522Sdarrenr/* ------------------------------------------------------------------------ */ 562145522Sdarrenr/* Function: fix_incksum */ 563145522Sdarrenr/* Returns: Nil */ 564145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 565145522Sdarrenr/* sp(I) - location of 16bit checksum to update */ 566145522Sdarrenr/* n((I) - amount to adjust checksum by */ 567145522Sdarrenr/* */ 568145522Sdarrenr/* Adjusts the 16bit checksum by "n" for packets going in. */ 569145522Sdarrenr/* ------------------------------------------------------------------------ */ 57080482Sdarrenrvoid fix_incksum(fin, sp, n) 57180482Sdarrenrfr_info_t *fin; 57253642Sguidou_short *sp; 57353642Sguidou_32_t n; 57453642Sguido{ 575145522Sdarrenr u_short sumshort; 576145522Sdarrenr u_32_t sum1; 57753642Sguido 578145522Sdarrenr if (n == 0) 57953642Sguido return; 580145522Sdarrenr 581145522Sdarrenr if (n & NAT_HW_CKSUM) { 58280482Sdarrenr n &= 0xffff; 58380482Sdarrenr n += fin->fin_dlen; 58480482Sdarrenr n = (n & 0xffff) + (n >> 16); 58555929Sguido *sp = n & 0xffff; 58655929Sguido return; 58755929Sguido } 58853642Sguido sum1 = (~ntohs(*sp)) & 0xffff; 58953642Sguido sum1 += ~(n) & 0xffff; 59053642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 59153642Sguido /* Again */ 59253642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 59353642Sguido sumshort = ~(u_short)sum1; 59453642Sguido *(sp) = htons(sumshort); 59553642Sguido} 59653642Sguido 59753642Sguido 598145522Sdarrenr/* ------------------------------------------------------------------------ */ 599145522Sdarrenr/* Function: fix_datacksum */ 600145522Sdarrenr/* Returns: Nil */ 601145522Sdarrenr/* Parameters: sp(I) - location of 16bit checksum to update */ 602145522Sdarrenr/* n((I) - amount to adjust checksum by */ 603145522Sdarrenr/* */ 604145522Sdarrenr/* Fix_datacksum is used *only* for the adjustments of checksums in the */ 605145522Sdarrenr/* data section of an IP packet. */ 606145522Sdarrenr/* */ 607145522Sdarrenr/* The only situation in which you need to do this is when NAT'ing an */ 608145522Sdarrenr/* ICMP error message. Such a message, contains in its body the IP header */ 609145522Sdarrenr/* of the original IP packet, that causes the error. */ 610145522Sdarrenr/* */ 611145522Sdarrenr/* You can't use fix_incksum or fix_outcksum in that case, because for the */ 612145522Sdarrenr/* kernel the data section of the ICMP error is just data, and no special */ 613145522Sdarrenr/* processing like hardware cksum or ntohs processing have been done by the */ 614145522Sdarrenr/* kernel on the data section. */ 615145522Sdarrenr/* ------------------------------------------------------------------------ */ 61667614Sdarrenrvoid fix_datacksum(sp, n) 61767614Sdarrenru_short *sp; 61867614Sdarrenru_32_t n; 61967614Sdarrenr{ 620145522Sdarrenr u_short sumshort; 621145522Sdarrenr u_32_t sum1; 62267614Sdarrenr 623145522Sdarrenr if (n == 0) 62467614Sdarrenr return; 62567614Sdarrenr 62667614Sdarrenr sum1 = (~ntohs(*sp)) & 0xffff; 62767614Sdarrenr sum1 += (n); 62867614Sdarrenr sum1 = (sum1 >> 16) + (sum1 & 0xffff); 62967614Sdarrenr /* Again */ 63067614Sdarrenr sum1 = (sum1 >> 16) + (sum1 & 0xffff); 63167614Sdarrenr sumshort = ~(u_short)sum1; 63267614Sdarrenr *(sp) = htons(sumshort); 63367614Sdarrenr} 63467614Sdarrenr 63553642Sguido 636145522Sdarrenr/* ------------------------------------------------------------------------ */ 637145522Sdarrenr/* Function: fr_nat_ioctl */ 638145522Sdarrenr/* Returns: int - 0 == success, != 0 == failure */ 639145522Sdarrenr/* Parameters: data(I) - pointer to ioctl data */ 640145522Sdarrenr/* cmd(I) - ioctl command integer */ 641145522Sdarrenr/* mode(I) - file mode bits used with open */ 642145522Sdarrenr/* */ 643145522Sdarrenr/* Processes an ioctl call made to operate on the IP Filter NAT device. */ 644145522Sdarrenr/* ------------------------------------------------------------------------ */ 645170268Sdarrenrint fr_nat_ioctl(data, cmd, mode, uid, ctx) 646145522Sdarrenrioctlcmd_t cmd; 64753642Sguidocaddr_t data; 648170268Sdarrenrint mode, uid; 649170268Sdarrenrvoid *ctx; 65053642Sguido{ 651145522Sdarrenr ipnat_t *nat, *nt, *n = NULL, **np = NULL; 65295418Sdarrenr int error = 0, ret, arg, getlock; 65353642Sguido ipnat_t natd; 654170268Sdarrenr SPL_INT(s); 65553642Sguido 65653642Sguido#if (BSD >= 199306) && defined(_KERNEL) 657170268Sdarrenr# if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 399002000) 658170268Sdarrenr if ((mode & FWRITE) && 659170268Sdarrenr kauth_authorize_network(curlwp->l_cred, KAUTH_NETWORK_FIREWALL, 660170268Sdarrenr KAUTH_REQ_NETWORK_FIREWALL_FW, 661170268Sdarrenr NULL, NULL, NULL)) { 66253642Sguido return EPERM; 663170268Sdarrenr } 664170268Sdarrenr# else 665170268Sdarrenr if ((securelevel >= 3) && (mode & FWRITE)) { 666170268Sdarrenr return EPERM; 667170268Sdarrenr } 668170268Sdarrenr# endif 66953642Sguido#endif 67053642Sguido 671145522Sdarrenr#if defined(__osf__) && defined(_KERNEL) 672145522Sdarrenr getlock = 0; 673145522Sdarrenr#else 674145522Sdarrenr getlock = (mode & NAT_LOCKHELD) ? 0 : 1; 675145522Sdarrenr#endif 676145522Sdarrenr 67753642Sguido nat = NULL; /* XXX gcc -Wuninitialized */ 678145522Sdarrenr if (cmd == (ioctlcmd_t)SIOCADNAT) { 679145522Sdarrenr KMALLOC(nt, ipnat_t *); 680145522Sdarrenr } else { 681145522Sdarrenr nt = NULL; 682145522Sdarrenr } 683145522Sdarrenr 684145522Sdarrenr if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) { 68595418Sdarrenr if (mode & NAT_SYSSPACE) { 68695418Sdarrenr bcopy(data, (char *)&natd, sizeof(natd)); 68795418Sdarrenr error = 0; 68895418Sdarrenr } else { 689145522Sdarrenr error = fr_inobj(data, &natd, IPFOBJ_IPNAT); 69095418Sdarrenr } 69164580Sdarrenr } 69253642Sguido 693145522Sdarrenr if (error != 0) 69460852Sdarrenr goto done; 69560852Sdarrenr 69653642Sguido /* 69753642Sguido * For add/delete, look to see if the NAT entry is already present 69853642Sguido */ 699145522Sdarrenr if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) { 70053642Sguido nat = &natd; 701145522Sdarrenr if (nat->in_v == 0) /* For backward compat. */ 702145522Sdarrenr nat->in_v = 4; 70353642Sguido nat->in_flags &= IPN_USERFLAGS; 70453642Sguido if ((nat->in_redir & NAT_MAPBLK) == 0) { 70560852Sdarrenr if ((nat->in_flags & IPN_SPLIT) == 0) 70660852Sdarrenr nat->in_inip &= nat->in_inmsk; 70760852Sdarrenr if ((nat->in_flags & IPN_IPRANGE) == 0) 70853642Sguido nat->in_outip &= nat->in_outmsk; 70953642Sguido } 710145522Sdarrenr MUTEX_ENTER(&ipf_natio); 711145522Sdarrenr for (np = &nat_list; ((n = *np) != NULL); np = &n->in_next) 712170268Sdarrenr if (bcmp((char *)&nat->in_flags, (char *)&n->in_flags, 713170268Sdarrenr IPN_CMPSIZ) == 0) { 714170268Sdarrenr if (nat->in_redir == NAT_REDIRECT && 715170268Sdarrenr nat->in_pnext != n->in_pnext) 716170268Sdarrenr continue; 71753642Sguido break; 718170268Sdarrenr } 71953642Sguido } 72053642Sguido 72153642Sguido switch (cmd) 72253642Sguido { 72355929Sguido#ifdef IPFILTER_LOG 72455929Sguido case SIOCIPFFB : 72560852Sdarrenr { 72660852Sdarrenr int tmp; 72760852Sdarrenr 72855929Sguido if (!(mode & FWRITE)) 72955929Sguido error = EPERM; 73060852Sdarrenr else { 73160852Sdarrenr tmp = ipflog_clear(IPL_LOGNAT); 732170268Sdarrenr error = BCOPYOUT((char *)&tmp, (char *)data, 733170268Sdarrenr sizeof(tmp)); 734170268Sdarrenr if (error != 0) 735170268Sdarrenr error = EFAULT; 73660852Sdarrenr } 73755929Sguido break; 73860852Sdarrenr } 739170268Sdarrenr 740145522Sdarrenr case SIOCSETLG : 741145522Sdarrenr if (!(mode & FWRITE)) 742145522Sdarrenr error = EPERM; 743145522Sdarrenr else { 744170268Sdarrenr error = BCOPYIN((char *)data, (char *)&nat_logging, 745170268Sdarrenr sizeof(nat_logging)); 746170268Sdarrenr if (error != 0) 747170268Sdarrenr error = EFAULT; 748145522Sdarrenr } 749145522Sdarrenr break; 750170268Sdarrenr 751145522Sdarrenr case SIOCGETLG : 752170268Sdarrenr error = BCOPYOUT((char *)&nat_logging, (char *)data, 753170268Sdarrenr sizeof(nat_logging)); 754170268Sdarrenr if (error != 0) 755170268Sdarrenr error = EFAULT; 756145522Sdarrenr break; 757170268Sdarrenr 758145522Sdarrenr case FIONREAD : 759145522Sdarrenr arg = iplused[IPL_LOGNAT]; 760170268Sdarrenr error = BCOPYOUT(&arg, data, sizeof(arg)); 761170268Sdarrenr if (error != 0) 762170268Sdarrenr error = EFAULT; 763145522Sdarrenr break; 76455929Sguido#endif 76553642Sguido case SIOCADNAT : 76653642Sguido if (!(mode & FWRITE)) { 76753642Sguido error = EPERM; 768145522Sdarrenr } else if (n != NULL) { 76953642Sguido error = EEXIST; 770145522Sdarrenr } else if (nt == NULL) { 771145522Sdarrenr error = ENOMEM; 77253642Sguido } 773145522Sdarrenr if (error != 0) { 774145522Sdarrenr MUTEX_EXIT(&ipf_natio); 77553642Sguido break; 77653642Sguido } 777145522Sdarrenr bcopy((char *)nat, (char *)nt, sizeof(*n)); 778145522Sdarrenr error = nat_siocaddnat(nt, np, getlock); 779145522Sdarrenr MUTEX_EXIT(&ipf_natio); 780145522Sdarrenr if (error == 0) 781145522Sdarrenr nt = NULL; 78253642Sguido break; 783170268Sdarrenr 78453642Sguido case SIOCRMNAT : 78553642Sguido if (!(mode & FWRITE)) { 78653642Sguido error = EPERM; 78753642Sguido n = NULL; 788145522Sdarrenr } else if (n == NULL) { 789145522Sdarrenr error = ESRCH; 79053642Sguido } 791145522Sdarrenr 792145522Sdarrenr if (error != 0) { 793145522Sdarrenr MUTEX_EXIT(&ipf_natio); 79453642Sguido break; 79553642Sguido } 796145522Sdarrenr nat_siocdelnat(n, np, getlock); 797145522Sdarrenr 798145522Sdarrenr MUTEX_EXIT(&ipf_natio); 79953642Sguido n = NULL; 80053642Sguido break; 801170268Sdarrenr 80253642Sguido case SIOCGNATS : 80353642Sguido nat_stats.ns_table[0] = nat_table[0]; 80453642Sguido nat_stats.ns_table[1] = nat_table[1]; 80553642Sguido nat_stats.ns_list = nat_list; 806170268Sdarrenr nat_stats.ns_maptable = ipf_hm_maptable; 807170268Sdarrenr nat_stats.ns_maplist = ipf_hm_maplist; 80853642Sguido nat_stats.ns_nattab_sz = ipf_nattable_sz; 809145522Sdarrenr nat_stats.ns_nattab_max = ipf_nattable_max; 81053642Sguido nat_stats.ns_rultab_sz = ipf_natrules_sz; 81153642Sguido nat_stats.ns_rdrtab_sz = ipf_rdrrules_sz; 81280482Sdarrenr nat_stats.ns_hostmap_sz = ipf_hostmap_sz; 81353642Sguido nat_stats.ns_instances = nat_instances; 81453642Sguido nat_stats.ns_apslist = ap_sess_list; 815170268Sdarrenr nat_stats.ns_ticks = fr_ticks; 816145522Sdarrenr error = fr_outobj(data, &nat_stats, IPFOBJ_NATSTAT); 81753642Sguido break; 818170268Sdarrenr 81953642Sguido case SIOCGNATL : 82053642Sguido { 82153642Sguido natlookup_t nl; 82253642Sguido 823145522Sdarrenr if (getlock) { 824145522Sdarrenr READ_ENTER(&ipf_nat); 825145522Sdarrenr } 826145522Sdarrenr error = fr_inobj(data, &nl, IPFOBJ_NATLOOKUP); 827145522Sdarrenr if (error == 0) { 828145522Sdarrenr if (nat_lookupredir(&nl) != NULL) { 829145522Sdarrenr error = fr_outobj(data, &nl, IPFOBJ_NATLOOKUP); 830145522Sdarrenr } else { 831145522Sdarrenr error = ESRCH; 832145522Sdarrenr } 833145522Sdarrenr } 834145522Sdarrenr if (getlock) { 835145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 836145522Sdarrenr } 83753642Sguido break; 83853642Sguido } 839170268Sdarrenr 84060852Sdarrenr case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */ 84153642Sguido if (!(mode & FWRITE)) { 84253642Sguido error = EPERM; 84353642Sguido break; 84453642Sguido } 845145522Sdarrenr if (getlock) { 846145522Sdarrenr WRITE_ENTER(&ipf_nat); 847145522Sdarrenr } 848170268Sdarrenr 849170268Sdarrenr error = BCOPYIN(data, &arg, sizeof(arg)); 850170268Sdarrenr if (error != 0) 851170268Sdarrenr error = EFAULT; 852170268Sdarrenr else { 853170268Sdarrenr if (arg == 0) 854170268Sdarrenr ret = nat_flushtable(); 855170268Sdarrenr else if (arg == 1) 856170268Sdarrenr ret = nat_clearlist(); 857170268Sdarrenr else 858170268Sdarrenr ret = nat_extraflush(arg); 859170268Sdarrenr } 860170268Sdarrenr 861145522Sdarrenr if (getlock) { 862145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 86360852Sdarrenr } 864145522Sdarrenr if (error == 0) { 865170268Sdarrenr error = BCOPYOUT(&ret, data, sizeof(ret)); 866145522Sdarrenr } 86753642Sguido break; 868170268Sdarrenr 869145522Sdarrenr case SIOCPROXY : 870170268Sdarrenr error = appr_ioctl(data, cmd, mode, ctx); 871145522Sdarrenr break; 872170268Sdarrenr 87360852Sdarrenr case SIOCSTLCK : 874153876Sguido if (!(mode & FWRITE)) { 875153876Sguido error = EPERM; 876153876Sguido } else { 877172776Sdarrenr error = fr_lock(data, &fr_nat_lock); 878153876Sguido } 87953642Sguido break; 880170268Sdarrenr 88160852Sdarrenr case SIOCSTPUT : 882153876Sguido if ((mode & FWRITE) != 0) { 883145522Sdarrenr error = fr_natputent(data, getlock); 884145522Sdarrenr } else { 88560852Sdarrenr error = EACCES; 886145522Sdarrenr } 88760852Sdarrenr break; 888170268Sdarrenr 88960852Sdarrenr case SIOCSTGSZ : 890145522Sdarrenr if (fr_nat_lock) { 891145522Sdarrenr if (getlock) { 892145522Sdarrenr READ_ENTER(&ipf_nat); 893145522Sdarrenr } 89460852Sdarrenr error = fr_natgetsz(data); 895145522Sdarrenr if (getlock) { 896145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 897145522Sdarrenr } 898145522Sdarrenr } else 89960852Sdarrenr error = EACCES; 90060852Sdarrenr break; 901170268Sdarrenr 90260852Sdarrenr case SIOCSTGET : 903145522Sdarrenr if (fr_nat_lock) { 904145522Sdarrenr if (getlock) { 905145522Sdarrenr READ_ENTER(&ipf_nat); 906145522Sdarrenr } 90760852Sdarrenr error = fr_natgetent(data); 908145522Sdarrenr if (getlock) { 909145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 910145522Sdarrenr } 911145522Sdarrenr } else 91260852Sdarrenr error = EACCES; 91360852Sdarrenr break; 914170268Sdarrenr 915170268Sdarrenr case SIOCGENITER : 916170268Sdarrenr { 917170268Sdarrenr ipfgeniter_t iter; 918170268Sdarrenr ipftoken_t *token; 919170268Sdarrenr 920170268Sdarrenr SPL_SCHED(s); 921170268Sdarrenr error = fr_inobj(data, &iter, IPFOBJ_GENITER); 922170268Sdarrenr if (error == 0) { 923170268Sdarrenr token = ipf_findtoken(iter.igi_type, uid, ctx); 924170268Sdarrenr if (token != NULL) { 925170268Sdarrenr error = nat_iterator(token, &iter); 926170268Sdarrenr } 927170268Sdarrenr RWLOCK_EXIT(&ipf_tokens); 928170268Sdarrenr } 929170268Sdarrenr SPL_X(s); 930170268Sdarrenr break; 931170268Sdarrenr } 932170268Sdarrenr 933170268Sdarrenr case SIOCIPFDELTOK : 934170268Sdarrenr error = BCOPYIN((caddr_t)data, (caddr_t)&arg, sizeof(arg)); 935170268Sdarrenr if (error == 0) { 936170268Sdarrenr SPL_SCHED(s); 937170268Sdarrenr error = ipf_deltoken(arg, uid, ctx); 938170268Sdarrenr SPL_X(s); 939170268Sdarrenr } else { 940170268Sdarrenr error = EFAULT; 941170268Sdarrenr } 942170268Sdarrenr break; 943170268Sdarrenr 944170268Sdarrenr case SIOCGTQTAB : 945170268Sdarrenr error = fr_outobj(data, nat_tqb, IPFOBJ_STATETQTAB); 946170268Sdarrenr break; 947170268Sdarrenr 948172776Sdarrenr case SIOCGTABL : 949172776Sdarrenr error = nat_gettable(data); 950172776Sdarrenr break; 951172776Sdarrenr 95253642Sguido default : 95353642Sguido error = EINVAL; 95453642Sguido break; 95553642Sguido } 95660852Sdarrenrdone: 957170268Sdarrenr if (nt != NULL) 95853642Sguido KFREE(nt); 95953642Sguido return error; 96053642Sguido} 96153642Sguido 96253642Sguido 963145522Sdarrenr/* ------------------------------------------------------------------------ */ 964145522Sdarrenr/* Function: nat_siocaddnat */ 965145522Sdarrenr/* Returns: int - 0 == success, != 0 == failure */ 966145522Sdarrenr/* Parameters: n(I) - pointer to new NAT rule */ 967145522Sdarrenr/* np(I) - pointer to where to insert new NAT rule */ 968145522Sdarrenr/* getlock(I) - flag indicating if lock on ipf_nat is held */ 969145522Sdarrenr/* Mutex Locks: ipf_natio */ 970145522Sdarrenr/* */ 971145522Sdarrenr/* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 972145522Sdarrenr/* from information passed to the kernel, then add it to the appropriate */ 973145522Sdarrenr/* NAT rule table(s). */ 974145522Sdarrenr/* ------------------------------------------------------------------------ */ 975145522Sdarrenrstatic int nat_siocaddnat(n, np, getlock) 976145522Sdarrenripnat_t *n, **np; 977145522Sdarrenrint getlock; 978145522Sdarrenr{ 979145522Sdarrenr int error = 0, i, j; 980145522Sdarrenr 981161356Sguido if (nat_resolverule(n) != 0) 982161356Sguido return ENOENT; 983145522Sdarrenr 984145522Sdarrenr if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) 985145522Sdarrenr return EINVAL; 986145522Sdarrenr 987145522Sdarrenr n->in_use = 0; 988145522Sdarrenr if (n->in_redir & NAT_MAPBLK) 989145522Sdarrenr n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk); 990145522Sdarrenr else if (n->in_flags & IPN_AUTOPORTMAP) 991145522Sdarrenr n->in_space = USABLE_PORTS * ~ntohl(n->in_inmsk); 992145522Sdarrenr else if (n->in_flags & IPN_IPRANGE) 993145522Sdarrenr n->in_space = ntohl(n->in_outmsk) - ntohl(n->in_outip); 994145522Sdarrenr else if (n->in_flags & IPN_SPLIT) 995145522Sdarrenr n->in_space = 2; 996145522Sdarrenr else if (n->in_outmsk != 0) 997145522Sdarrenr n->in_space = ~ntohl(n->in_outmsk); 998145522Sdarrenr else 999145522Sdarrenr n->in_space = 1; 1000145522Sdarrenr 1001145522Sdarrenr /* 1002145522Sdarrenr * Calculate the number of valid IP addresses in the output 1003145522Sdarrenr * mapping range. In all cases, the range is inclusive of 1004145522Sdarrenr * the start and ending IP addresses. 1005145522Sdarrenr * If to a CIDR address, lose 2: broadcast + network address 1006145522Sdarrenr * (so subtract 1) 1007145522Sdarrenr * If to a range, add one. 1008145522Sdarrenr * If to a single IP address, set to 1. 1009145522Sdarrenr */ 1010145522Sdarrenr if (n->in_space) { 1011145522Sdarrenr if ((n->in_flags & IPN_IPRANGE) != 0) 1012145522Sdarrenr n->in_space += 1; 1013145522Sdarrenr else 1014145522Sdarrenr n->in_space -= 1; 1015145522Sdarrenr } else 1016145522Sdarrenr n->in_space = 1; 1017145522Sdarrenr 1018145522Sdarrenr if ((n->in_outmsk != 0xffffffff) && (n->in_outmsk != 0) && 1019145522Sdarrenr ((n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0)) 1020145522Sdarrenr n->in_nip = ntohl(n->in_outip) + 1; 1021145522Sdarrenr else if ((n->in_flags & IPN_SPLIT) && 1022145522Sdarrenr (n->in_redir & NAT_REDIRECT)) 1023145522Sdarrenr n->in_nip = ntohl(n->in_inip); 1024145522Sdarrenr else 1025145522Sdarrenr n->in_nip = ntohl(n->in_outip); 1026145522Sdarrenr if (n->in_redir & NAT_MAP) { 1027145522Sdarrenr n->in_pnext = ntohs(n->in_pmin); 1028145522Sdarrenr /* 1029145522Sdarrenr * Multiply by the number of ports made available. 1030145522Sdarrenr */ 1031145522Sdarrenr if (ntohs(n->in_pmax) >= ntohs(n->in_pmin)) { 1032145522Sdarrenr n->in_space *= (ntohs(n->in_pmax) - 1033145522Sdarrenr ntohs(n->in_pmin) + 1); 1034145522Sdarrenr /* 1035145522Sdarrenr * Because two different sources can map to 1036145522Sdarrenr * different destinations but use the same 1037145522Sdarrenr * local IP#/port #. 1038145522Sdarrenr * If the result is smaller than in_space, then 1039145522Sdarrenr * we may have wrapped around 32bits. 1040145522Sdarrenr */ 1041145522Sdarrenr i = n->in_inmsk; 1042145522Sdarrenr if ((i != 0) && (i != 0xffffffff)) { 1043145522Sdarrenr j = n->in_space * (~ntohl(i) + 1); 1044145522Sdarrenr if (j >= n->in_space) 1045145522Sdarrenr n->in_space = j; 1046145522Sdarrenr else 1047145522Sdarrenr n->in_space = 0xffffffff; 1048145522Sdarrenr } 1049145522Sdarrenr } 1050145522Sdarrenr /* 1051145522Sdarrenr * If no protocol is specified, multiple by 256 to allow for 1052145522Sdarrenr * at least one IP:IP mapping per protocol. 1053145522Sdarrenr */ 1054145522Sdarrenr if ((n->in_flags & IPN_TCPUDPICMP) == 0) { 1055145522Sdarrenr j = n->in_space * 256; 1056145522Sdarrenr if (j >= n->in_space) 1057145522Sdarrenr n->in_space = j; 1058145522Sdarrenr else 1059145522Sdarrenr n->in_space = 0xffffffff; 1060145522Sdarrenr } 1061145522Sdarrenr } 1062145522Sdarrenr 1063145522Sdarrenr /* Otherwise, these fields are preset */ 1064145522Sdarrenr 1065145522Sdarrenr if (getlock) { 1066145522Sdarrenr WRITE_ENTER(&ipf_nat); 1067145522Sdarrenr } 1068145522Sdarrenr n->in_next = NULL; 1069145522Sdarrenr *np = n; 1070145522Sdarrenr 1071145522Sdarrenr if (n->in_age[0] != 0) 1072145522Sdarrenr n->in_tqehead[0] = fr_addtimeoutqueue(&nat_utqe, n->in_age[0]); 1073145522Sdarrenr 1074145522Sdarrenr if (n->in_age[1] != 0) 1075145522Sdarrenr n->in_tqehead[1] = fr_addtimeoutqueue(&nat_utqe, n->in_age[1]); 1076145522Sdarrenr 1077145522Sdarrenr if (n->in_redir & NAT_REDIRECT) { 1078145522Sdarrenr n->in_flags &= ~IPN_NOTDST; 1079145522Sdarrenr nat_addrdr(n); 1080145522Sdarrenr } 1081145522Sdarrenr if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { 1082145522Sdarrenr n->in_flags &= ~IPN_NOTSRC; 1083145522Sdarrenr nat_addnat(n); 1084145522Sdarrenr } 1085170268Sdarrenr MUTEX_INIT(&n->in_lock, "ipnat rule lock"); 1086170268Sdarrenr 1087145522Sdarrenr n = NULL; 1088145522Sdarrenr nat_stats.ns_rules++; 1089172776Sdarrenr#if SOLARIS && !defined(_INET_IP_STACK_H) 1090145522Sdarrenr pfil_delayed_copy = 0; 1091145522Sdarrenr#endif 1092145522Sdarrenr if (getlock) { 1093145522Sdarrenr RWLOCK_EXIT(&ipf_nat); /* WRITE */ 1094145522Sdarrenr } 1095145522Sdarrenr 1096145522Sdarrenr return error; 1097145522Sdarrenr} 1098145522Sdarrenr 1099145522Sdarrenr 1100145522Sdarrenr/* ------------------------------------------------------------------------ */ 1101145522Sdarrenr/* Function: nat_resolvrule */ 1102145522Sdarrenr/* Returns: Nil */ 1103145522Sdarrenr/* Parameters: n(I) - pointer to NAT rule */ 1104145522Sdarrenr/* */ 1105145522Sdarrenr/* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 1106145522Sdarrenr/* from information passed to the kernel, then add it to the appropriate */ 1107145522Sdarrenr/* NAT rule table(s). */ 1108145522Sdarrenr/* ------------------------------------------------------------------------ */ 1109161356Sguidostatic int nat_resolverule(n) 1110145522Sdarrenripnat_t *n; 1111145522Sdarrenr{ 1112145522Sdarrenr n->in_ifnames[0][LIFNAMSIZ - 1] = '\0'; 1113145522Sdarrenr n->in_ifps[0] = fr_resolvenic(n->in_ifnames[0], 4); 1114145522Sdarrenr 1115145522Sdarrenr n->in_ifnames[1][LIFNAMSIZ - 1] = '\0'; 1116145522Sdarrenr if (n->in_ifnames[1][0] == '\0') { 1117145522Sdarrenr (void) strncpy(n->in_ifnames[1], n->in_ifnames[0], LIFNAMSIZ); 1118145522Sdarrenr n->in_ifps[1] = n->in_ifps[0]; 1119145522Sdarrenr } else { 1120161356Sguido n->in_ifps[1] = fr_resolvenic(n->in_ifnames[1], 4); 1121145522Sdarrenr } 1122145522Sdarrenr 1123145522Sdarrenr if (n->in_plabel[0] != '\0') { 1124145522Sdarrenr n->in_apr = appr_lookup(n->in_p, n->in_plabel); 1125161356Sguido if (n->in_apr == NULL) 1126161356Sguido return -1; 1127145522Sdarrenr } 1128161356Sguido return 0; 1129145522Sdarrenr} 1130145522Sdarrenr 1131145522Sdarrenr 1132145522Sdarrenr/* ------------------------------------------------------------------------ */ 1133145522Sdarrenr/* Function: nat_siocdelnat */ 1134145522Sdarrenr/* Returns: int - 0 == success, != 0 == failure */ 1135145522Sdarrenr/* Parameters: n(I) - pointer to new NAT rule */ 1136145522Sdarrenr/* np(I) - pointer to where to insert new NAT rule */ 1137145522Sdarrenr/* getlock(I) - flag indicating if lock on ipf_nat is held */ 1138145522Sdarrenr/* Mutex Locks: ipf_natio */ 1139145522Sdarrenr/* */ 1140145522Sdarrenr/* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 1141145522Sdarrenr/* from information passed to the kernel, then add it to the appropriate */ 1142145522Sdarrenr/* NAT rule table(s). */ 1143145522Sdarrenr/* ------------------------------------------------------------------------ */ 1144145522Sdarrenrstatic void nat_siocdelnat(n, np, getlock) 1145145522Sdarrenripnat_t *n, **np; 1146145522Sdarrenrint getlock; 1147145522Sdarrenr{ 1148145522Sdarrenr if (getlock) { 1149145522Sdarrenr WRITE_ENTER(&ipf_nat); 1150145522Sdarrenr } 1151145522Sdarrenr if (n->in_redir & NAT_REDIRECT) 1152145522Sdarrenr nat_delrdr(n); 1153145522Sdarrenr if (n->in_redir & (NAT_MAPBLK|NAT_MAP)) 1154145522Sdarrenr nat_delnat(n); 1155145522Sdarrenr if (nat_list == NULL) { 1156145522Sdarrenr nat_masks = 0; 1157145522Sdarrenr rdr_masks = 0; 1158145522Sdarrenr } 1159145522Sdarrenr 1160145522Sdarrenr if (n->in_tqehead[0] != NULL) { 1161145522Sdarrenr if (fr_deletetimeoutqueue(n->in_tqehead[0]) == 0) { 1162145522Sdarrenr fr_freetimeoutqueue(n->in_tqehead[1]); 1163145522Sdarrenr } 1164145522Sdarrenr } 1165145522Sdarrenr 1166145522Sdarrenr if (n->in_tqehead[1] != NULL) { 1167145522Sdarrenr if (fr_deletetimeoutqueue(n->in_tqehead[1]) == 0) { 1168145522Sdarrenr fr_freetimeoutqueue(n->in_tqehead[1]); 1169145522Sdarrenr } 1170145522Sdarrenr } 1171145522Sdarrenr 1172145522Sdarrenr *np = n->in_next; 1173145522Sdarrenr 1174145522Sdarrenr if (n->in_use == 0) { 1175145522Sdarrenr if (n->in_apr) 1176145522Sdarrenr appr_free(n->in_apr); 1177172776Sdarrenr MUTEX_DESTROY(&n->in_lock); 1178145522Sdarrenr KFREE(n); 1179145522Sdarrenr nat_stats.ns_rules--; 1180172776Sdarrenr#if SOLARIS && !defined(_INET_IP_STACK_H) 1181145522Sdarrenr if (nat_stats.ns_rules == 0) 1182145522Sdarrenr pfil_delayed_copy = 1; 1183145522Sdarrenr#endif 1184145522Sdarrenr } else { 1185145522Sdarrenr n->in_flags |= IPN_DELETE; 1186145522Sdarrenr n->in_next = NULL; 1187145522Sdarrenr } 1188145522Sdarrenr if (getlock) { 1189145522Sdarrenr RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ 1190145522Sdarrenr } 1191145522Sdarrenr} 1192145522Sdarrenr 1193145522Sdarrenr 1194145522Sdarrenr/* ------------------------------------------------------------------------ */ 1195145522Sdarrenr/* Function: fr_natgetsz */ 1196145522Sdarrenr/* Returns: int - 0 == success, != 0 is the error value. */ 1197145522Sdarrenr/* Parameters: data(I) - pointer to natget structure with kernel pointer */ 1198145522Sdarrenr/* get the size of. */ 1199145522Sdarrenr/* */ 1200145522Sdarrenr/* Handle SIOCSTGSZ. */ 1201145522Sdarrenr/* Return the size of the nat list entry to be copied back to user space. */ 1202145522Sdarrenr/* The size of the entry is stored in the ng_sz field and the enture natget */ 1203145522Sdarrenr/* structure is copied back to the user. */ 1204145522Sdarrenr/* ------------------------------------------------------------------------ */ 120560852Sdarrenrstatic int fr_natgetsz(data) 120660852Sdarrenrcaddr_t data; 120760852Sdarrenr{ 120860852Sdarrenr ap_session_t *aps; 120960852Sdarrenr nat_t *nat, *n; 121060852Sdarrenr natget_t ng; 121160852Sdarrenr 1212170268Sdarrenr if (BCOPYIN(data, &ng, sizeof(ng)) != 0) 1213170268Sdarrenr return EFAULT; 121460852Sdarrenr 121560852Sdarrenr nat = ng.ng_ptr; 121660852Sdarrenr if (!nat) { 121760852Sdarrenr nat = nat_instances; 121860852Sdarrenr ng.ng_sz = 0; 1219145522Sdarrenr /* 1220145522Sdarrenr * Empty list so the size returned is 0. Simple. 1221145522Sdarrenr */ 122260852Sdarrenr if (nat == NULL) { 1223170268Sdarrenr if (BCOPYOUT(&ng, data, sizeof(ng)) != 0) 1224170268Sdarrenr return EFAULT; 1225145522Sdarrenr return 0; 122660852Sdarrenr } 122760852Sdarrenr } else { 122860852Sdarrenr /* 122960852Sdarrenr * Make sure the pointer we're copying from exists in the 123060852Sdarrenr * current list of entries. Security precaution to prevent 123160852Sdarrenr * copying of random kernel data. 123260852Sdarrenr */ 123360852Sdarrenr for (n = nat_instances; n; n = n->nat_next) 123460852Sdarrenr if (n == nat) 123560852Sdarrenr break; 123660852Sdarrenr if (!n) 123760852Sdarrenr return ESRCH; 123860852Sdarrenr } 123960852Sdarrenr 1240145522Sdarrenr /* 1241145522Sdarrenr * Incluse any space required for proxy data structures. 1242145522Sdarrenr */ 124360852Sdarrenr ng.ng_sz = sizeof(nat_save_t); 124460852Sdarrenr aps = nat->nat_aps; 1245145522Sdarrenr if (aps != NULL) { 1246145522Sdarrenr ng.ng_sz += sizeof(ap_session_t) - 4; 1247145522Sdarrenr if (aps->aps_data != 0) 1248145522Sdarrenr ng.ng_sz += aps->aps_psiz; 124960852Sdarrenr } 125060852Sdarrenr 1251170268Sdarrenr if (BCOPYOUT(&ng, data, sizeof(ng)) != 0) 1252170268Sdarrenr return EFAULT; 1253145522Sdarrenr return 0; 125460852Sdarrenr} 125560852Sdarrenr 125660852Sdarrenr 1257145522Sdarrenr/* ------------------------------------------------------------------------ */ 1258145522Sdarrenr/* Function: fr_natgetent */ 1259145522Sdarrenr/* Returns: int - 0 == success, != 0 is the error value. */ 1260145522Sdarrenr/* Parameters: data(I) - pointer to natget structure with kernel pointer */ 1261145522Sdarrenr/* to NAT structure to copy out. */ 1262145522Sdarrenr/* */ 1263145522Sdarrenr/* Handle SIOCSTGET. */ 1264145522Sdarrenr/* Copies out NAT entry to user space. Any additional data held for a */ 1265145522Sdarrenr/* proxy is also copied, as to is the NAT rule which was responsible for it */ 1266145522Sdarrenr/* ------------------------------------------------------------------------ */ 126760852Sdarrenrstatic int fr_natgetent(data) 126860852Sdarrenrcaddr_t data; 126960852Sdarrenr{ 1270145522Sdarrenr int error, outsize; 127160852Sdarrenr ap_session_t *aps; 1272145522Sdarrenr nat_save_t *ipn, ipns; 1273145522Sdarrenr nat_t *n, *nat; 127460852Sdarrenr 1275145522Sdarrenr error = fr_inobj(data, &ipns, IPFOBJ_NATSAVE); 1276145522Sdarrenr if (error != 0) 1277145522Sdarrenr return error; 127860852Sdarrenr 1279145522Sdarrenr if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) 1280145522Sdarrenr return EINVAL; 1281145522Sdarrenr 1282145522Sdarrenr KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize); 1283145522Sdarrenr if (ipn == NULL) 1284145522Sdarrenr return ENOMEM; 1285145522Sdarrenr 1286145522Sdarrenr ipn->ipn_dsize = ipns.ipn_dsize; 1287145522Sdarrenr nat = ipns.ipn_next; 1288145522Sdarrenr if (nat == NULL) { 128960852Sdarrenr nat = nat_instances; 129060852Sdarrenr if (nat == NULL) { 129160852Sdarrenr if (nat_instances == NULL) 1292145522Sdarrenr error = ENOENT; 1293145522Sdarrenr goto finished; 129460852Sdarrenr } 129560852Sdarrenr } else { 129660852Sdarrenr /* 129760852Sdarrenr * Make sure the pointer we're copying from exists in the 129860852Sdarrenr * current list of entries. Security precaution to prevent 129960852Sdarrenr * copying of random kernel data. 130060852Sdarrenr */ 130160852Sdarrenr for (n = nat_instances; n; n = n->nat_next) 130260852Sdarrenr if (n == nat) 130360852Sdarrenr break; 1304145522Sdarrenr if (n == NULL) { 1305145522Sdarrenr error = ESRCH; 1306145522Sdarrenr goto finished; 1307145522Sdarrenr } 130860852Sdarrenr } 1309145522Sdarrenr ipn->ipn_next = nat->nat_next; 131060852Sdarrenr 1311145522Sdarrenr /* 1312145522Sdarrenr * Copy the NAT structure. 1313145522Sdarrenr */ 1314145522Sdarrenr bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat)); 131560852Sdarrenr 1316145522Sdarrenr /* 1317145522Sdarrenr * If we have a pointer to the NAT rule it belongs to, save that too. 1318145522Sdarrenr */ 1319145522Sdarrenr if (nat->nat_ptr != NULL) 1320145522Sdarrenr bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat, 1321145522Sdarrenr sizeof(ipn->ipn_ipnat)); 132260852Sdarrenr 1323145522Sdarrenr /* 1324145522Sdarrenr * If we also know the NAT entry has an associated filter rule, 1325145522Sdarrenr * save that too. 1326145522Sdarrenr */ 1327145522Sdarrenr if (nat->nat_fr != NULL) 1328145522Sdarrenr bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr, 1329145522Sdarrenr sizeof(ipn->ipn_fr)); 133060852Sdarrenr 1331145522Sdarrenr /* 1332145522Sdarrenr * Last but not least, if there is an application proxy session set 1333145522Sdarrenr * up for this NAT entry, then copy that out too, including any 1334145522Sdarrenr * private data saved along side it by the proxy. 1335145522Sdarrenr */ 1336145522Sdarrenr aps = nat->nat_aps; 1337145522Sdarrenr outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data); 1338145522Sdarrenr if (aps != NULL) { 1339145522Sdarrenr char *s; 134060852Sdarrenr 1341145522Sdarrenr if (outsize < sizeof(*aps)) { 1342145522Sdarrenr error = ENOBUFS; 1343145522Sdarrenr goto finished; 134460852Sdarrenr } 1345145522Sdarrenr 1346145522Sdarrenr s = ipn->ipn_data; 1347145522Sdarrenr bcopy((char *)aps, s, sizeof(*aps)); 1348145522Sdarrenr s += sizeof(*aps); 1349145522Sdarrenr outsize -= sizeof(*aps); 1350145522Sdarrenr if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz)) 1351145522Sdarrenr bcopy(aps->aps_data, s, aps->aps_psiz); 1352145522Sdarrenr else 1353145522Sdarrenr error = ENOBUFS; 135460852Sdarrenr } 1355145522Sdarrenr if (error == 0) { 1356145522Sdarrenr error = fr_outobjsz(data, ipn, IPFOBJ_NATSAVE, ipns.ipn_dsize); 1357145522Sdarrenr } 1358145522Sdarrenr 1359145522Sdarrenrfinished: 1360145522Sdarrenr if (ipn != NULL) { 1361145522Sdarrenr KFREES(ipn, ipns.ipn_dsize); 1362145522Sdarrenr } 136364580Sdarrenr return error; 136460852Sdarrenr} 136560852Sdarrenr 136660852Sdarrenr 1367145522Sdarrenr/* ------------------------------------------------------------------------ */ 1368145522Sdarrenr/* Function: fr_natputent */ 1369145522Sdarrenr/* Returns: int - 0 == success, != 0 is the error value. */ 1370145522Sdarrenr/* Parameters: data(I) - pointer to natget structure with NAT */ 1371145522Sdarrenr/* structure information to load into the kernel */ 1372145522Sdarrenr/* getlock(I) - flag indicating whether or not a write lock */ 1373145522Sdarrenr/* on ipf_nat is already held. */ 1374145522Sdarrenr/* */ 1375145522Sdarrenr/* Handle SIOCSTPUT. */ 1376145522Sdarrenr/* Loads a NAT table entry from user space, including a NAT rule, proxy and */ 1377145522Sdarrenr/* firewall rule data structures, if pointers to them indicate so. */ 1378145522Sdarrenr/* ------------------------------------------------------------------------ */ 1379145522Sdarrenrstatic int fr_natputent(data, getlock) 138060852Sdarrenrcaddr_t data; 1381145522Sdarrenrint getlock; 138260852Sdarrenr{ 1383145522Sdarrenr nat_save_t ipn, *ipnn; 138460852Sdarrenr ap_session_t *aps; 1385145522Sdarrenr nat_t *n, *nat; 138660852Sdarrenr frentry_t *fr; 1387145522Sdarrenr fr_info_t fin; 138860852Sdarrenr ipnat_t *in; 138960852Sdarrenr int error; 139060852Sdarrenr 1391145522Sdarrenr error = fr_inobj(data, &ipn, IPFOBJ_NATSAVE); 1392145522Sdarrenr if (error != 0) 1393145522Sdarrenr return error; 1394145522Sdarrenr 1395145522Sdarrenr /* 1396145522Sdarrenr * Initialise early because of code at junkput label. 1397145522Sdarrenr */ 1398145522Sdarrenr in = NULL; 1399145522Sdarrenr aps = NULL; 140064580Sdarrenr nat = NULL; 1401145522Sdarrenr ipnn = NULL; 1402170268Sdarrenr fr = NULL; 1403145522Sdarrenr 1404145522Sdarrenr /* 1405145522Sdarrenr * New entry, copy in the rest of the NAT entry if it's size is more 1406145522Sdarrenr * than just the nat_t structure. 1407145522Sdarrenr */ 1408145522Sdarrenr if (ipn.ipn_dsize > sizeof(ipn)) { 1409145522Sdarrenr if (ipn.ipn_dsize > 81920) { 1410145522Sdarrenr error = ENOMEM; 1411145522Sdarrenr goto junkput; 1412145522Sdarrenr } 1413145522Sdarrenr 1414145522Sdarrenr KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize); 141560852Sdarrenr if (ipnn == NULL) 141660852Sdarrenr return ENOMEM; 1417145522Sdarrenr 1418145522Sdarrenr error = fr_inobjsz(data, ipnn, IPFOBJ_NATSAVE, ipn.ipn_dsize); 1419145522Sdarrenr if (error != 0) { 142064580Sdarrenr error = EFAULT; 142164580Sdarrenr goto junkput; 142264580Sdarrenr } 142360852Sdarrenr } else 1424145522Sdarrenr ipnn = &ipn; 142560852Sdarrenr 142660852Sdarrenr KMALLOC(nat, nat_t *); 142764580Sdarrenr if (nat == NULL) { 1428145522Sdarrenr error = ENOMEM; 142964580Sdarrenr goto junkput; 143064580Sdarrenr } 143160852Sdarrenr 1432145522Sdarrenr bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat)); 143360852Sdarrenr /* 143460852Sdarrenr * Initialize all these so that nat_delete() doesn't cause a crash. 143560852Sdarrenr */ 1436145522Sdarrenr bzero((char *)nat, offsetof(struct nat, nat_tqe)); 1437145522Sdarrenr nat->nat_tqe.tqe_pnext = NULL; 1438145522Sdarrenr nat->nat_tqe.tqe_next = NULL; 1439145522Sdarrenr nat->nat_tqe.tqe_ifq = NULL; 1440145522Sdarrenr nat->nat_tqe.tqe_parent = nat; 144160852Sdarrenr 144260852Sdarrenr /* 144360852Sdarrenr * Restore the rule associated with this nat session 144460852Sdarrenr */ 1445145522Sdarrenr in = ipnn->ipn_nat.nat_ptr; 1446145522Sdarrenr if (in != NULL) { 144760852Sdarrenr KMALLOC(in, ipnat_t *); 1448145522Sdarrenr nat->nat_ptr = in; 144960852Sdarrenr if (in == NULL) { 145060852Sdarrenr error = ENOMEM; 145160852Sdarrenr goto junkput; 145260852Sdarrenr } 1453145522Sdarrenr bzero((char *)in, offsetof(struct ipnat, in_next6)); 1454145522Sdarrenr bcopy((char *)&ipnn->ipn_ipnat, (char *)in, sizeof(*in)); 145560852Sdarrenr in->in_use = 1; 145660852Sdarrenr in->in_flags |= IPN_DELETE; 1457145522Sdarrenr 1458145522Sdarrenr ATOMIC_INC(nat_stats.ns_rules); 1459145522Sdarrenr 1460161356Sguido if (nat_resolverule(in) != 0) { 1461161356Sguido error = ESRCH; 1462161356Sguido goto junkput; 1463161356Sguido } 1464145522Sdarrenr } 1465145522Sdarrenr 1466145522Sdarrenr /* 1467145522Sdarrenr * Check that the NAT entry doesn't already exist in the kernel. 1468161356Sguido * 1469161356Sguido * For NAT_OUTBOUND, we're lookup for a duplicate MAP entry. To do 1470161356Sguido * this, we check to see if the inbound combination of addresses and 1471161356Sguido * ports is already known. Similar logic is applied for NAT_INBOUND. 1472161356Sguido * 1473145522Sdarrenr */ 1474145522Sdarrenr bzero((char *)&fin, sizeof(fin)); 1475145522Sdarrenr fin.fin_p = nat->nat_p; 1476145522Sdarrenr if (nat->nat_dir == NAT_OUTBOUND) { 1477170268Sdarrenr fin.fin_ifp = nat->nat_ifps[0]; 1478145522Sdarrenr fin.fin_data[0] = ntohs(nat->nat_oport); 1479145522Sdarrenr fin.fin_data[1] = ntohs(nat->nat_outport); 1480153876Sguido if (getlock) { 1481153876Sguido READ_ENTER(&ipf_nat); 1482153876Sguido } 1483161356Sguido n = nat_inlookup(&fin, nat->nat_flags, fin.fin_p, 1484161356Sguido nat->nat_oip, nat->nat_inip); 1485153876Sguido if (getlock) { 1486153876Sguido RWLOCK_EXIT(&ipf_nat); 1487153876Sguido } 1488153876Sguido if (n != NULL) { 1489145522Sdarrenr error = EEXIST; 1490145522Sdarrenr goto junkput; 149160852Sdarrenr } 1492145522Sdarrenr } else if (nat->nat_dir == NAT_INBOUND) { 1493170268Sdarrenr fin.fin_ifp = nat->nat_ifps[0]; 1494145522Sdarrenr fin.fin_data[0] = ntohs(nat->nat_outport); 1495145522Sdarrenr fin.fin_data[1] = ntohs(nat->nat_oport); 1496153876Sguido if (getlock) { 1497153876Sguido READ_ENTER(&ipf_nat); 1498153876Sguido } 1499161356Sguido n = nat_outlookup(&fin, nat->nat_flags, fin.fin_p, 1500161356Sguido nat->nat_outip, nat->nat_oip); 1501153876Sguido if (getlock) { 1502153876Sguido RWLOCK_EXIT(&ipf_nat); 1503153876Sguido } 1504153876Sguido if (n != NULL) { 1505145522Sdarrenr error = EEXIST; 1506145522Sdarrenr goto junkput; 1507145522Sdarrenr } 1508145522Sdarrenr } else { 1509145522Sdarrenr error = EINVAL; 1510145522Sdarrenr goto junkput; 151160852Sdarrenr } 151260852Sdarrenr 151360852Sdarrenr /* 151460852Sdarrenr * Restore ap_session_t structure. Include the private data allocated 151560852Sdarrenr * if it was there. 151660852Sdarrenr */ 1517145522Sdarrenr aps = nat->nat_aps; 1518145522Sdarrenr if (aps != NULL) { 151960852Sdarrenr KMALLOC(aps, ap_session_t *); 1520145522Sdarrenr nat->nat_aps = aps; 152160852Sdarrenr if (aps == NULL) { 152260852Sdarrenr error = ENOMEM; 152360852Sdarrenr goto junkput; 152460852Sdarrenr } 152560852Sdarrenr bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps)); 1526145522Sdarrenr if (in != NULL) 152760852Sdarrenr aps->aps_apr = in->in_apr; 1528145522Sdarrenr else 1529145522Sdarrenr aps->aps_apr = NULL; 1530145522Sdarrenr if (aps->aps_psiz != 0) { 1531145522Sdarrenr if (aps->aps_psiz > 81920) { 1532145522Sdarrenr error = ENOMEM; 1533145522Sdarrenr goto junkput; 1534145522Sdarrenr } 153560852Sdarrenr KMALLOCS(aps->aps_data, void *, aps->aps_psiz); 153660852Sdarrenr if (aps->aps_data == NULL) { 153760852Sdarrenr error = ENOMEM; 153860852Sdarrenr goto junkput; 153960852Sdarrenr } 154060852Sdarrenr bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data, 154160852Sdarrenr aps->aps_psiz); 154260852Sdarrenr } else { 154360852Sdarrenr aps->aps_psiz = 0; 154460852Sdarrenr aps->aps_data = NULL; 154560852Sdarrenr } 154660852Sdarrenr } 154760852Sdarrenr 154860852Sdarrenr /* 154960852Sdarrenr * If there was a filtering rule associated with this entry then 155060852Sdarrenr * build up a new one. 155160852Sdarrenr */ 1552145522Sdarrenr fr = nat->nat_fr; 155360852Sdarrenr if (fr != NULL) { 1554145522Sdarrenr if ((nat->nat_flags & SI_NEWFR) != 0) { 155560852Sdarrenr KMALLOC(fr, frentry_t *); 155660852Sdarrenr nat->nat_fr = fr; 155760852Sdarrenr if (fr == NULL) { 155860852Sdarrenr error = ENOMEM; 155960852Sdarrenr goto junkput; 156060852Sdarrenr } 1561145522Sdarrenr ipnn->ipn_nat.nat_fr = fr; 1562145522Sdarrenr fr->fr_ref = 1; 1563145522Sdarrenr (void) fr_outobj(data, ipnn, IPFOBJ_NATSAVE); 1564145522Sdarrenr bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr)); 1565161356Sguido 1566161356Sguido fr->fr_ref = 1; 1567161356Sguido fr->fr_dsize = 0; 1568161356Sguido fr->fr_data = NULL; 1569161356Sguido fr->fr_type = FR_T_NONE; 1570161356Sguido 1571145522Sdarrenr MUTEX_NUKE(&fr->fr_lock); 1572145522Sdarrenr MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock"); 157360852Sdarrenr } else { 1574153876Sguido if (getlock) { 1575153876Sguido READ_ENTER(&ipf_nat); 1576153876Sguido } 157760852Sdarrenr for (n = nat_instances; n; n = n->nat_next) 157860852Sdarrenr if (n->nat_fr == fr) 157960852Sdarrenr break; 1580145522Sdarrenr 1581145522Sdarrenr if (n != NULL) { 1582145522Sdarrenr MUTEX_ENTER(&fr->fr_lock); 1583145522Sdarrenr fr->fr_ref++; 1584145522Sdarrenr MUTEX_EXIT(&fr->fr_lock); 1585145522Sdarrenr } 1586153876Sguido if (getlock) { 1587153876Sguido RWLOCK_EXIT(&ipf_nat); 1588153876Sguido } 1589145522Sdarrenr 159060852Sdarrenr if (!n) { 159160852Sdarrenr error = ESRCH; 159260852Sdarrenr goto junkput; 159360852Sdarrenr } 159460852Sdarrenr } 159560852Sdarrenr } 159660852Sdarrenr 1597145522Sdarrenr if (ipnn != &ipn) { 1598145522Sdarrenr KFREES(ipnn, ipn.ipn_dsize); 1599145522Sdarrenr ipnn = NULL; 1600145522Sdarrenr } 1601145522Sdarrenr 1602145522Sdarrenr if (getlock) { 1603145522Sdarrenr WRITE_ENTER(&ipf_nat); 1604145522Sdarrenr } 1605145522Sdarrenr error = nat_insert(nat, nat->nat_rev); 1606145522Sdarrenr if ((error == 0) && (aps != NULL)) { 1607145522Sdarrenr aps->aps_next = ap_sess_list; 1608145522Sdarrenr ap_sess_list = aps; 1609145522Sdarrenr } 1610145522Sdarrenr if (getlock) { 1611145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 1612145522Sdarrenr } 1613145522Sdarrenr 1614145522Sdarrenr if (error == 0) 1615145522Sdarrenr return 0; 1616145522Sdarrenr 1617145522Sdarrenr error = ENOMEM; 1618145522Sdarrenr 161960852Sdarrenrjunkput: 1620145522Sdarrenr if (fr != NULL) 1621170268Sdarrenr (void) fr_derefrule(&fr); 1622145522Sdarrenr 1623145522Sdarrenr if ((ipnn != NULL) && (ipnn != &ipn)) { 1624145522Sdarrenr KFREES(ipnn, ipn.ipn_dsize); 1625145522Sdarrenr } 1626145522Sdarrenr if (nat != NULL) { 1627145522Sdarrenr if (aps != NULL) { 1628145522Sdarrenr if (aps->aps_data != NULL) { 1629145522Sdarrenr KFREES(aps->aps_data, aps->aps_psiz); 1630145522Sdarrenr } 1631145522Sdarrenr KFREE(aps); 1632145522Sdarrenr } 1633145522Sdarrenr if (in != NULL) { 1634145522Sdarrenr if (in->in_apr) 1635145522Sdarrenr appr_free(in->in_apr); 1636145522Sdarrenr KFREE(in); 1637145522Sdarrenr } 1638145522Sdarrenr KFREE(nat); 1639145522Sdarrenr } 164060852Sdarrenr return error; 164160852Sdarrenr} 164260852Sdarrenr 164360852Sdarrenr 1644145522Sdarrenr/* ------------------------------------------------------------------------ */ 1645145522Sdarrenr/* Function: nat_delete */ 1646145522Sdarrenr/* Returns: Nil */ 1647145522Sdarrenr/* Parameters: natd(I) - pointer to NAT structure to delete */ 1648145522Sdarrenr/* logtype(I) - type of LOG record to create before deleting */ 1649145522Sdarrenr/* Write Lock: ipf_nat */ 1650145522Sdarrenr/* */ 1651145522Sdarrenr/* Delete a nat entry from the various lists and table. If NAT logging is */ 1652145522Sdarrenr/* enabled then generate a NAT log record for this event. */ 1653145522Sdarrenr/* ------------------------------------------------------------------------ */ 1654172776Sdarrenrvoid nat_delete(nat, logtype) 1655145522Sdarrenrstruct nat *nat; 1656145522Sdarrenrint logtype; 165753642Sguido{ 165853642Sguido struct ipnat *ipn; 1659172776Sdarrenr int removed = 0; 166053642Sguido 1661145522Sdarrenr if (logtype != 0 && nat_logging != 0) 1662145522Sdarrenr nat_log(nat, logtype); 166353642Sguido 1664145522Sdarrenr /* 1665145522Sdarrenr * Take it as a general indication that all the pointers are set if 1666145522Sdarrenr * nat_pnext is set. 1667145522Sdarrenr */ 1668145522Sdarrenr if (nat->nat_pnext != NULL) { 1669172776Sdarrenr removed = 1; 1670172776Sdarrenr 1671145522Sdarrenr nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--; 1672145522Sdarrenr nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--; 1673145522Sdarrenr 1674145522Sdarrenr *nat->nat_pnext = nat->nat_next; 1675145522Sdarrenr if (nat->nat_next != NULL) { 1676145522Sdarrenr nat->nat_next->nat_pnext = nat->nat_pnext; 1677145522Sdarrenr nat->nat_next = NULL; 1678145522Sdarrenr } 1679145522Sdarrenr nat->nat_pnext = NULL; 1680145522Sdarrenr 1681145522Sdarrenr *nat->nat_phnext[0] = nat->nat_hnext[0]; 1682145522Sdarrenr if (nat->nat_hnext[0] != NULL) { 1683145522Sdarrenr nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 1684145522Sdarrenr nat->nat_hnext[0] = NULL; 1685145522Sdarrenr } 1686145522Sdarrenr nat->nat_phnext[0] = NULL; 1687145522Sdarrenr 1688145522Sdarrenr *nat->nat_phnext[1] = nat->nat_hnext[1]; 1689145522Sdarrenr if (nat->nat_hnext[1] != NULL) { 1690145522Sdarrenr nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 1691145522Sdarrenr nat->nat_hnext[1] = NULL; 1692145522Sdarrenr } 1693145522Sdarrenr nat->nat_phnext[1] = NULL; 1694145522Sdarrenr 1695145522Sdarrenr if ((nat->nat_flags & SI_WILDP) != 0) 1696145522Sdarrenr nat_stats.ns_wilds--; 169753642Sguido } 169860852Sdarrenr 1699145522Sdarrenr if (nat->nat_me != NULL) { 1700145522Sdarrenr *nat->nat_me = NULL; 1701145522Sdarrenr nat->nat_me = NULL; 1702145522Sdarrenr } 170360852Sdarrenr 1704170268Sdarrenr if (nat->nat_tqe.tqe_ifq != NULL) 1705170268Sdarrenr fr_deletequeueentry(&nat->nat_tqe); 1706145522Sdarrenr 1707170268Sdarrenr if (logtype == NL_EXPIRE) 1708170268Sdarrenr nat_stats.ns_expire++; 1709170268Sdarrenr 1710172776Sdarrenr MUTEX_ENTER(&nat->nat_lock); 1711172776Sdarrenr /* 1712172776Sdarrenr * NL_DESTROY should only be passed in when we've got nat_ref >= 2. 1713172776Sdarrenr * This happens when a nat'd packet is blocked and we want to throw 1714172776Sdarrenr * away the NAT session. 1715172776Sdarrenr */ 1716172776Sdarrenr if (logtype == NL_DESTROY) { 1717172776Sdarrenr if (nat->nat_ref > 2) { 1718172776Sdarrenr nat->nat_ref -= 2; 1719172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 1720172776Sdarrenr if (removed) 1721172776Sdarrenr nat_stats.ns_orphans++; 1722172776Sdarrenr return; 1723172776Sdarrenr } 1724172776Sdarrenr } else if (nat->nat_ref > 1) { 1725172776Sdarrenr nat->nat_ref--; 1726172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 1727172776Sdarrenr if (removed) 1728172776Sdarrenr nat_stats.ns_orphans++; 1729145522Sdarrenr return; 1730145522Sdarrenr } 1731172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 1732170268Sdarrenr 1733161356Sguido /* 1734172776Sdarrenr * At this point, nat_ref is 1, doing "--" would make it 0.. 1735161356Sguido */ 1736172776Sdarrenr nat->nat_ref = 0; 1737172776Sdarrenr if (!removed) 1738172776Sdarrenr nat_stats.ns_orphans--; 1739145522Sdarrenr 1740145522Sdarrenr#ifdef IPFILTER_SYNC 1741145522Sdarrenr if (nat->nat_sync) 1742145522Sdarrenr ipfsync_del(nat->nat_sync); 1743145522Sdarrenr#endif 1744145522Sdarrenr 1745145522Sdarrenr if (nat->nat_fr != NULL) 1746170268Sdarrenr (void) fr_derefrule(&nat->nat_fr); 1747145522Sdarrenr 1748145522Sdarrenr if (nat->nat_hm != NULL) 1749170268Sdarrenr fr_hostmapdel(&nat->nat_hm); 1750145522Sdarrenr 175153642Sguido /* 175253642Sguido * If there is an active reference from the nat entry to its parent 175353642Sguido * rule, decrement the rule's reference count and free it too if no 175453642Sguido * longer being used. 175553642Sguido */ 1756145522Sdarrenr ipn = nat->nat_ptr; 175753642Sguido if (ipn != NULL) { 1758170268Sdarrenr fr_ipnatderef(&ipn); 175953642Sguido } 176053642Sguido 1761145522Sdarrenr MUTEX_DESTROY(&nat->nat_lock); 1762145522Sdarrenr 1763145522Sdarrenr aps_free(nat->nat_aps); 1764145522Sdarrenr nat_stats.ns_inuse--; 1765145522Sdarrenr 176653642Sguido /* 176753642Sguido * If there's a fragment table entry too for this nat entry, then 1768145522Sdarrenr * dereference that as well. This is after nat_lock is released 1769145522Sdarrenr * because of Tru64. 177053642Sguido */ 1771145522Sdarrenr fr_forgetnat((void *)nat); 1772145522Sdarrenr 1773145522Sdarrenr KFREE(nat); 177453642Sguido} 177553642Sguido 177653642Sguido 1777145522Sdarrenr/* ------------------------------------------------------------------------ */ 1778145522Sdarrenr/* Function: nat_flushtable */ 1779145522Sdarrenr/* Returns: int - number of NAT rules deleted */ 1780145522Sdarrenr/* Parameters: Nil */ 1781145522Sdarrenr/* */ 1782145522Sdarrenr/* Deletes all currently active NAT sessions. In deleting each NAT entry a */ 1783145522Sdarrenr/* log record should be emitted in nat_delete() if NAT logging is enabled. */ 1784145522Sdarrenr/* ------------------------------------------------------------------------ */ 178553642Sguido/* 178653642Sguido * nat_flushtable - clear the NAT table of all mapping entries. 178753642Sguido */ 178853642Sguidostatic int nat_flushtable() 178953642Sguido{ 1790145522Sdarrenr nat_t *nat; 1791145522Sdarrenr int j = 0; 179267614Sdarrenr 179353642Sguido /* 179453642Sguido * ALL NAT mappings deleted, so lets just make the deletions 179553642Sguido * quicker. 179653642Sguido */ 179753642Sguido if (nat_table[0] != NULL) 179853642Sguido bzero((char *)nat_table[0], 179953642Sguido sizeof(nat_table[0]) * ipf_nattable_sz); 180053642Sguido if (nat_table[1] != NULL) 180153642Sguido bzero((char *)nat_table[1], 180253642Sguido sizeof(nat_table[1]) * ipf_nattable_sz); 180353642Sguido 1804145522Sdarrenr while ((nat = nat_instances) != NULL) { 1805145522Sdarrenr nat_delete(nat, NL_FLUSH); 180653642Sguido j++; 180753642Sguido } 1808145522Sdarrenr 180953642Sguido nat_stats.ns_inuse = 0; 181053642Sguido return j; 181153642Sguido} 181253642Sguido 181353642Sguido 1814145522Sdarrenr/* ------------------------------------------------------------------------ */ 1815145522Sdarrenr/* Function: nat_clearlist */ 1816145522Sdarrenr/* Returns: int - number of NAT/RDR rules deleted */ 1817145522Sdarrenr/* Parameters: Nil */ 1818145522Sdarrenr/* */ 1819145522Sdarrenr/* Delete all rules in the current list of rules. There is nothing elegant */ 1820145522Sdarrenr/* about this cleanup: simply free all entries on the list of rules and */ 1821145522Sdarrenr/* clear out the tables used for hashed NAT rule lookups. */ 1822145522Sdarrenr/* ------------------------------------------------------------------------ */ 1823145522Sdarrenrstatic int nat_clearlist() 182453642Sguido{ 1825145522Sdarrenr ipnat_t *n, **np = &nat_list; 182653642Sguido int i = 0; 182753642Sguido 182853642Sguido if (nat_rules != NULL) 182953642Sguido bzero((char *)nat_rules, sizeof(*nat_rules) * ipf_natrules_sz); 183053642Sguido if (rdr_rules != NULL) 183153642Sguido bzero((char *)rdr_rules, sizeof(*rdr_rules) * ipf_rdrrules_sz); 183253642Sguido 1833145522Sdarrenr while ((n = *np) != NULL) { 183453642Sguido *np = n->in_next; 1835145522Sdarrenr if (n->in_use == 0) { 1836145522Sdarrenr if (n->in_apr != NULL) 183753642Sguido appr_free(n->in_apr); 1838172776Sdarrenr MUTEX_DESTROY(&n->in_lock); 183953642Sguido KFREE(n); 184053642Sguido nat_stats.ns_rules--; 184153642Sguido } else { 184253642Sguido n->in_flags |= IPN_DELETE; 184353642Sguido n->in_next = NULL; 184453642Sguido } 184553642Sguido i++; 184653642Sguido } 1847172776Sdarrenr#if SOLARIS && !defined(_INET_IP_STACK_H) 1848145522Sdarrenr pfil_delayed_copy = 1; 1849145522Sdarrenr#endif 185053642Sguido nat_masks = 0; 185153642Sguido rdr_masks = 0; 185253642Sguido return i; 185353642Sguido} 185453642Sguido 185553642Sguido 1856145522Sdarrenr/* ------------------------------------------------------------------------ */ 1857145522Sdarrenr/* Function: nat_newmap */ 1858145522Sdarrenr/* Returns: int - -1 == error, 0 == success */ 1859145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 1860145522Sdarrenr/* nat(I) - pointer to NAT entry */ 1861145522Sdarrenr/* ni(I) - pointer to structure with misc. information needed */ 1862145522Sdarrenr/* to create new NAT entry. */ 1863145522Sdarrenr/* */ 1864145522Sdarrenr/* Given an empty NAT structure, populate it with new information about a */ 1865145522Sdarrenr/* new NAT session, as defined by the matching NAT rule. */ 1866145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 1867145522Sdarrenr/* to the new IP address for the translation. */ 1868145522Sdarrenr/* ------------------------------------------------------------------------ */ 1869145522Sdarrenrstatic INLINE int nat_newmap(fin, nat, ni) 187092685Sdarrenrfr_info_t *fin; 1871145522Sdarrenrnat_t *nat; 1872145522Sdarrenrnatinfo_t *ni; 1873145522Sdarrenr{ 1874145522Sdarrenr u_short st_port, dport, sport, port, sp, dp; 1875145522Sdarrenr struct in_addr in, inb; 1876145522Sdarrenr hostmap_t *hm; 1877145522Sdarrenr u_32_t flags; 1878145522Sdarrenr u_32_t st_ip; 1879145522Sdarrenr ipnat_t *np; 1880145522Sdarrenr nat_t *natl; 1881145522Sdarrenr int l; 1882145522Sdarrenr 1883145522Sdarrenr /* 1884145522Sdarrenr * If it's an outbound packet which doesn't match any existing 1885145522Sdarrenr * record, then create a new port 1886145522Sdarrenr */ 1887145522Sdarrenr l = 0; 1888145522Sdarrenr hm = NULL; 1889145522Sdarrenr np = ni->nai_np; 1890145522Sdarrenr st_ip = np->in_nip; 1891145522Sdarrenr st_port = np->in_pnext; 1892145522Sdarrenr flags = ni->nai_flags; 1893145522Sdarrenr sport = ni->nai_sport; 1894145522Sdarrenr dport = ni->nai_dport; 1895145522Sdarrenr 1896145522Sdarrenr /* 1897145522Sdarrenr * Do a loop until we either run out of entries to try or we find 1898145522Sdarrenr * a NAT mapping that isn't currently being used. This is done 1899145522Sdarrenr * because the change to the source is not (usually) being fixed. 1900145522Sdarrenr */ 1901145522Sdarrenr do { 1902145522Sdarrenr port = 0; 1903145522Sdarrenr in.s_addr = htonl(np->in_nip); 1904145522Sdarrenr if (l == 0) { 1905145522Sdarrenr /* 1906145522Sdarrenr * Check to see if there is an existing NAT 1907145522Sdarrenr * setup for this IP address pair. 1908145522Sdarrenr */ 1909145522Sdarrenr hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, 1910145522Sdarrenr in, 0); 1911145522Sdarrenr if (hm != NULL) 1912145522Sdarrenr in.s_addr = hm->hm_mapip.s_addr; 1913145522Sdarrenr } else if ((l == 1) && (hm != NULL)) { 1914170268Sdarrenr fr_hostmapdel(&hm); 1915145522Sdarrenr } 1916145522Sdarrenr in.s_addr = ntohl(in.s_addr); 1917145522Sdarrenr 1918145522Sdarrenr nat->nat_hm = hm; 1919145522Sdarrenr 1920145522Sdarrenr if ((np->in_outmsk == 0xffffffff) && (np->in_pnext == 0)) { 1921145522Sdarrenr if (l > 0) 1922145522Sdarrenr return -1; 1923145522Sdarrenr } 1924145522Sdarrenr 1925145522Sdarrenr if (np->in_redir == NAT_BIMAP && 1926145522Sdarrenr np->in_inmsk == np->in_outmsk) { 1927145522Sdarrenr /* 1928145522Sdarrenr * map the address block in a 1:1 fashion 1929145522Sdarrenr */ 1930145522Sdarrenr in.s_addr = np->in_outip; 1931145522Sdarrenr in.s_addr |= fin->fin_saddr & ~np->in_inmsk; 1932145522Sdarrenr in.s_addr = ntohl(in.s_addr); 1933145522Sdarrenr 1934145522Sdarrenr } else if (np->in_redir & NAT_MAPBLK) { 1935145522Sdarrenr if ((l >= np->in_ppip) || ((l > 0) && 1936145522Sdarrenr !(flags & IPN_TCPUDP))) 1937145522Sdarrenr return -1; 1938145522Sdarrenr /* 1939145522Sdarrenr * map-block - Calculate destination address. 1940145522Sdarrenr */ 1941145522Sdarrenr in.s_addr = ntohl(fin->fin_saddr); 1942145522Sdarrenr in.s_addr &= ntohl(~np->in_inmsk); 1943145522Sdarrenr inb.s_addr = in.s_addr; 1944145522Sdarrenr in.s_addr /= np->in_ippip; 1945145522Sdarrenr in.s_addr &= ntohl(~np->in_outmsk); 1946145522Sdarrenr in.s_addr += ntohl(np->in_outip); 1947145522Sdarrenr /* 1948145522Sdarrenr * Calculate destination port. 1949145522Sdarrenr */ 1950145522Sdarrenr if ((flags & IPN_TCPUDP) && 1951145522Sdarrenr (np->in_ppip != 0)) { 1952145522Sdarrenr port = ntohs(sport) + l; 1953145522Sdarrenr port %= np->in_ppip; 1954145522Sdarrenr port += np->in_ppip * 1955145522Sdarrenr (inb.s_addr % np->in_ippip); 1956145522Sdarrenr port += MAPBLK_MINPORT; 1957145522Sdarrenr port = htons(port); 1958145522Sdarrenr } 1959145522Sdarrenr 1960145522Sdarrenr } else if ((np->in_outip == 0) && 1961145522Sdarrenr (np->in_outmsk == 0xffffffff)) { 1962145522Sdarrenr /* 1963145522Sdarrenr * 0/32 - use the interface's IP address. 1964145522Sdarrenr */ 1965145522Sdarrenr if ((l > 0) || 1966145522Sdarrenr fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp, 1967145522Sdarrenr &in, NULL) == -1) 1968145522Sdarrenr return -1; 1969145522Sdarrenr in.s_addr = ntohl(in.s_addr); 1970145522Sdarrenr 1971145522Sdarrenr } else if ((np->in_outip == 0) && (np->in_outmsk == 0)) { 1972145522Sdarrenr /* 1973145522Sdarrenr * 0/0 - use the original source address/port. 1974145522Sdarrenr */ 1975145522Sdarrenr if (l > 0) 1976145522Sdarrenr return -1; 1977145522Sdarrenr in.s_addr = ntohl(fin->fin_saddr); 1978145522Sdarrenr 1979145522Sdarrenr } else if ((np->in_outmsk != 0xffffffff) && 1980145522Sdarrenr (np->in_pnext == 0) && ((l > 0) || (hm == NULL))) 1981145522Sdarrenr np->in_nip++; 1982145522Sdarrenr 1983145522Sdarrenr natl = NULL; 1984145522Sdarrenr 1985145522Sdarrenr if ((flags & IPN_TCPUDP) && 1986145522Sdarrenr ((np->in_redir & NAT_MAPBLK) == 0) && 1987145522Sdarrenr (np->in_flags & IPN_AUTOPORTMAP)) { 1988145522Sdarrenr /* 1989145522Sdarrenr * "ports auto" (without map-block) 1990145522Sdarrenr */ 1991145522Sdarrenr if ((l > 0) && (l % np->in_ppip == 0)) { 1992145522Sdarrenr if (l > np->in_space) { 1993145522Sdarrenr return -1; 1994145522Sdarrenr } else if ((l > np->in_ppip) && 1995145522Sdarrenr np->in_outmsk != 0xffffffff) 1996145522Sdarrenr np->in_nip++; 1997145522Sdarrenr } 1998145522Sdarrenr if (np->in_ppip != 0) { 1999145522Sdarrenr port = ntohs(sport); 2000145522Sdarrenr port += (l % np->in_ppip); 2001145522Sdarrenr port %= np->in_ppip; 2002145522Sdarrenr port += np->in_ppip * 2003145522Sdarrenr (ntohl(fin->fin_saddr) % 2004145522Sdarrenr np->in_ippip); 2005145522Sdarrenr port += MAPBLK_MINPORT; 2006145522Sdarrenr port = htons(port); 2007145522Sdarrenr } 2008145522Sdarrenr 2009145522Sdarrenr } else if (((np->in_redir & NAT_MAPBLK) == 0) && 2010145522Sdarrenr (flags & IPN_TCPUDPICMP) && (np->in_pnext != 0)) { 2011145522Sdarrenr /* 2012145522Sdarrenr * Standard port translation. Select next port. 2013145522Sdarrenr */ 2014145522Sdarrenr port = htons(np->in_pnext++); 2015145522Sdarrenr 2016145522Sdarrenr if (np->in_pnext > ntohs(np->in_pmax)) { 2017145522Sdarrenr np->in_pnext = ntohs(np->in_pmin); 2018145522Sdarrenr if (np->in_outmsk != 0xffffffff) 2019145522Sdarrenr np->in_nip++; 2020145522Sdarrenr } 2021145522Sdarrenr } 2022145522Sdarrenr 2023145522Sdarrenr if (np->in_flags & IPN_IPRANGE) { 2024145522Sdarrenr if (np->in_nip > ntohl(np->in_outmsk)) 2025145522Sdarrenr np->in_nip = ntohl(np->in_outip); 2026145522Sdarrenr } else { 2027145522Sdarrenr if ((np->in_outmsk != 0xffffffff) && 2028145522Sdarrenr ((np->in_nip + 1) & ntohl(np->in_outmsk)) > 2029145522Sdarrenr ntohl(np->in_outip)) 2030145522Sdarrenr np->in_nip = ntohl(np->in_outip) + 1; 2031145522Sdarrenr } 2032145522Sdarrenr 2033145522Sdarrenr if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) 2034145522Sdarrenr port = sport; 2035145522Sdarrenr 2036145522Sdarrenr /* 2037145522Sdarrenr * Here we do a lookup of the connection as seen from 2038145522Sdarrenr * the outside. If an IP# pair already exists, try 2039145522Sdarrenr * again. So if you have A->B becomes C->B, you can 2040145522Sdarrenr * also have D->E become C->E but not D->B causing 2041145522Sdarrenr * another C->B. Also take protocol and ports into 2042145522Sdarrenr * account when determining whether a pre-existing 2043145522Sdarrenr * NAT setup will cause an external conflict where 2044145522Sdarrenr * this is appropriate. 2045145522Sdarrenr */ 2046145522Sdarrenr inb.s_addr = htonl(in.s_addr); 2047145522Sdarrenr sp = fin->fin_data[0]; 2048145522Sdarrenr dp = fin->fin_data[1]; 2049145522Sdarrenr fin->fin_data[0] = fin->fin_data[1]; 2050145522Sdarrenr fin->fin_data[1] = htons(port); 2051145522Sdarrenr natl = nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 2052145522Sdarrenr (u_int)fin->fin_p, fin->fin_dst, inb); 2053145522Sdarrenr fin->fin_data[0] = sp; 2054145522Sdarrenr fin->fin_data[1] = dp; 2055145522Sdarrenr 2056145522Sdarrenr /* 2057145522Sdarrenr * Has the search wrapped around and come back to the 2058145522Sdarrenr * start ? 2059145522Sdarrenr */ 2060145522Sdarrenr if ((natl != NULL) && 2061145522Sdarrenr (np->in_pnext != 0) && (st_port == np->in_pnext) && 2062145522Sdarrenr (np->in_nip != 0) && (st_ip == np->in_nip)) 2063145522Sdarrenr return -1; 2064145522Sdarrenr l++; 2065145522Sdarrenr } while (natl != NULL); 2066145522Sdarrenr 2067145522Sdarrenr if (np->in_space > 0) 2068145522Sdarrenr np->in_space--; 2069145522Sdarrenr 2070145522Sdarrenr /* Setup the NAT table */ 2071145522Sdarrenr nat->nat_inip = fin->fin_src; 2072145522Sdarrenr nat->nat_outip.s_addr = htonl(in.s_addr); 2073145522Sdarrenr nat->nat_oip = fin->fin_dst; 2074145522Sdarrenr if (nat->nat_hm == NULL) 2075145522Sdarrenr nat->nat_hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, 2076145522Sdarrenr nat->nat_outip, 0); 2077145522Sdarrenr 2078145522Sdarrenr /* 2079145522Sdarrenr * The ICMP checksum does not have a pseudo header containing 2080145522Sdarrenr * the IP addresses 2081145522Sdarrenr */ 2082145522Sdarrenr ni->nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr)); 2083145522Sdarrenr ni->nai_sum2 = LONG_SUM(in.s_addr); 2084145522Sdarrenr if ((flags & IPN_TCPUDP)) { 2085145522Sdarrenr ni->nai_sum1 += ntohs(sport); 2086145522Sdarrenr ni->nai_sum2 += ntohs(port); 2087145522Sdarrenr } 2088145522Sdarrenr 2089145522Sdarrenr if (flags & IPN_TCPUDP) { 2090145522Sdarrenr nat->nat_inport = sport; 2091145522Sdarrenr nat->nat_outport = port; /* sport */ 2092145522Sdarrenr nat->nat_oport = dport; 2093145522Sdarrenr ((tcphdr_t *)fin->fin_dp)->th_sport = port; 2094145522Sdarrenr } else if (flags & IPN_ICMPQUERY) { 2095145522Sdarrenr ((icmphdr_t *)fin->fin_dp)->icmp_id = port; 2096145522Sdarrenr nat->nat_inport = port; 2097145522Sdarrenr nat->nat_outport = port; 2098145522Sdarrenr } else if (fin->fin_p == IPPROTO_GRE) { 2099145522Sdarrenr#if 0 2100145522Sdarrenr nat->nat_gre.gs_flags = ((grehdr_t *)fin->fin_dp)->gr_flags; 2101145522Sdarrenr if (GRE_REV(nat->nat_gre.gs_flags) == 1) { 2102145522Sdarrenr nat->nat_oport = 0;/*fin->fin_data[1];*/ 2103145522Sdarrenr nat->nat_inport = 0;/*fin->fin_data[0];*/ 2104145522Sdarrenr nat->nat_outport = 0;/*fin->fin_data[0];*/ 2105145522Sdarrenr nat->nat_call[0] = fin->fin_data[0]; 2106145522Sdarrenr nat->nat_call[1] = fin->fin_data[0]; 2107145522Sdarrenr } 2108145522Sdarrenr#endif 2109145522Sdarrenr } 2110145522Sdarrenr ni->nai_ip.s_addr = in.s_addr; 2111145522Sdarrenr ni->nai_port = port; 2112145522Sdarrenr ni->nai_nport = dport; 2113145522Sdarrenr return 0; 2114145522Sdarrenr} 2115145522Sdarrenr 2116145522Sdarrenr 2117145522Sdarrenr/* ------------------------------------------------------------------------ */ 2118145522Sdarrenr/* Function: nat_newrdr */ 2119145522Sdarrenr/* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ 2120145522Sdarrenr/* allow rule to be moved if IPN_ROUNDR is set. */ 2121145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2122145522Sdarrenr/* nat(I) - pointer to NAT entry */ 2123145522Sdarrenr/* ni(I) - pointer to structure with misc. information needed */ 2124145522Sdarrenr/* to create new NAT entry. */ 2125145522Sdarrenr/* */ 2126145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 2127145522Sdarrenr/* to the new IP address for the translation. */ 2128145522Sdarrenr/* ------------------------------------------------------------------------ */ 2129145522Sdarrenrstatic INLINE int nat_newrdr(fin, nat, ni) 2130145522Sdarrenrfr_info_t *fin; 2131145522Sdarrenrnat_t *nat; 2132145522Sdarrenrnatinfo_t *ni; 2133145522Sdarrenr{ 2134145522Sdarrenr u_short nport, dport, sport; 2135170268Sdarrenr struct in_addr in, inb; 2136170268Sdarrenr u_short sp, dp; 2137145522Sdarrenr hostmap_t *hm; 2138145522Sdarrenr u_32_t flags; 2139145522Sdarrenr ipnat_t *np; 2140170268Sdarrenr nat_t *natl; 2141145522Sdarrenr int move; 2142145522Sdarrenr 2143145522Sdarrenr move = 1; 2144145522Sdarrenr hm = NULL; 2145145522Sdarrenr in.s_addr = 0; 2146145522Sdarrenr np = ni->nai_np; 2147145522Sdarrenr flags = ni->nai_flags; 2148145522Sdarrenr sport = ni->nai_sport; 2149145522Sdarrenr dport = ni->nai_dport; 2150145522Sdarrenr 2151145522Sdarrenr /* 2152145522Sdarrenr * If the matching rule has IPN_STICKY set, then we want to have the 2153145522Sdarrenr * same rule kick in as before. Why would this happen? If you have 2154145522Sdarrenr * a collection of rdr rules with "round-robin sticky", the current 2155145522Sdarrenr * packet might match a different one to the previous connection but 2156145522Sdarrenr * we want the same destination to be used. 2157145522Sdarrenr */ 2158153876Sguido if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) && 2159153876Sguido ((np->in_flags & IPN_STICKY) != 0)) { 2160145522Sdarrenr hm = nat_hostmap(NULL, fin->fin_src, fin->fin_dst, in, 2161145522Sdarrenr (u_32_t)dport); 2162145522Sdarrenr if (hm != NULL) { 2163145522Sdarrenr in.s_addr = ntohl(hm->hm_mapip.s_addr); 2164145522Sdarrenr np = hm->hm_ipnat; 2165145522Sdarrenr ni->nai_np = np; 2166145522Sdarrenr move = 0; 2167145522Sdarrenr } 2168145522Sdarrenr } 2169145522Sdarrenr 2170145522Sdarrenr /* 2171145522Sdarrenr * Otherwise, it's an inbound packet. Most likely, we don't 2172145522Sdarrenr * want to rewrite source ports and source addresses. Instead, 2173145522Sdarrenr * we want to rewrite to a fixed internal address and fixed 2174145522Sdarrenr * internal port. 2175145522Sdarrenr */ 2176145522Sdarrenr if (np->in_flags & IPN_SPLIT) { 2177145522Sdarrenr in.s_addr = np->in_nip; 2178145522Sdarrenr 2179145522Sdarrenr if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { 2180153876Sguido hm = nat_hostmap(NULL, fin->fin_src, fin->fin_dst, 2181145522Sdarrenr in, (u_32_t)dport); 2182145522Sdarrenr if (hm != NULL) { 2183145522Sdarrenr in.s_addr = hm->hm_mapip.s_addr; 2184145522Sdarrenr move = 0; 2185145522Sdarrenr } 2186145522Sdarrenr } 2187145522Sdarrenr 2188145522Sdarrenr if (hm == NULL || hm->hm_ref == 1) { 2189145522Sdarrenr if (np->in_inip == htonl(in.s_addr)) { 2190145522Sdarrenr np->in_nip = ntohl(np->in_inmsk); 2191145522Sdarrenr move = 0; 2192145522Sdarrenr } else { 2193145522Sdarrenr np->in_nip = ntohl(np->in_inip); 2194145522Sdarrenr } 2195145522Sdarrenr } 2196145522Sdarrenr 2197145522Sdarrenr } else if ((np->in_inip == 0) && (np->in_inmsk == 0xffffffff)) { 2198145522Sdarrenr /* 2199145522Sdarrenr * 0/32 - use the interface's IP address. 2200145522Sdarrenr */ 2201145522Sdarrenr if (fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp, &in, NULL) == -1) 2202145522Sdarrenr return -1; 2203145522Sdarrenr in.s_addr = ntohl(in.s_addr); 2204145522Sdarrenr 2205145522Sdarrenr } else if ((np->in_inip == 0) && (np->in_inmsk== 0)) { 2206145522Sdarrenr /* 2207145522Sdarrenr * 0/0 - use the original destination address/port. 2208145522Sdarrenr */ 2209145522Sdarrenr in.s_addr = ntohl(fin->fin_daddr); 2210145522Sdarrenr 2211145522Sdarrenr } else if (np->in_redir == NAT_BIMAP && 2212145522Sdarrenr np->in_inmsk == np->in_outmsk) { 2213145522Sdarrenr /* 2214145522Sdarrenr * map the address block in a 1:1 fashion 2215145522Sdarrenr */ 2216145522Sdarrenr in.s_addr = np->in_inip; 2217145522Sdarrenr in.s_addr |= fin->fin_daddr & ~np->in_inmsk; 2218145522Sdarrenr in.s_addr = ntohl(in.s_addr); 2219145522Sdarrenr } else { 2220145522Sdarrenr in.s_addr = ntohl(np->in_inip); 2221145522Sdarrenr } 2222145522Sdarrenr 2223145522Sdarrenr if ((np->in_pnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) 2224145522Sdarrenr nport = dport; 2225145522Sdarrenr else { 2226145522Sdarrenr /* 2227145522Sdarrenr * Whilst not optimized for the case where 2228145522Sdarrenr * pmin == pmax, the gain is not significant. 2229145522Sdarrenr */ 2230145522Sdarrenr if (((np->in_flags & IPN_FIXEDDPORT) == 0) && 2231145522Sdarrenr (np->in_pmin != np->in_pmax)) { 2232145522Sdarrenr nport = ntohs(dport) - ntohs(np->in_pmin) + 2233145522Sdarrenr ntohs(np->in_pnext); 2234145522Sdarrenr nport = htons(nport); 2235145522Sdarrenr } else 2236145522Sdarrenr nport = np->in_pnext; 2237145522Sdarrenr } 2238145522Sdarrenr 2239145522Sdarrenr /* 2240145522Sdarrenr * When the redirect-to address is set to 0.0.0.0, just 2241145522Sdarrenr * assume a blank `forwarding' of the packet. We don't 2242145522Sdarrenr * setup any translation for this either. 2243145522Sdarrenr */ 2244145522Sdarrenr if (in.s_addr == 0) { 2245145522Sdarrenr if (nport == dport) 2246145522Sdarrenr return -1; 2247145522Sdarrenr in.s_addr = ntohl(fin->fin_daddr); 2248145522Sdarrenr } 2249145522Sdarrenr 2250170268Sdarrenr /* 2251170268Sdarrenr * Check to see if this redirect mapping already exists and if 2252170268Sdarrenr * it does, return "failure" (allowing it to be created will just 2253170268Sdarrenr * cause one or both of these "connections" to stop working.) 2254170268Sdarrenr */ 2255170268Sdarrenr inb.s_addr = htonl(in.s_addr); 2256170268Sdarrenr sp = fin->fin_data[0]; 2257170268Sdarrenr dp = fin->fin_data[1]; 2258170268Sdarrenr fin->fin_data[1] = fin->fin_data[0]; 2259170268Sdarrenr fin->fin_data[0] = ntohs(nport); 2260170268Sdarrenr natl = nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 2261170268Sdarrenr (u_int)fin->fin_p, inb, fin->fin_src); 2262170268Sdarrenr fin->fin_data[0] = sp; 2263170268Sdarrenr fin->fin_data[1] = dp; 2264170268Sdarrenr if (natl != NULL) 2265170268Sdarrenr return -1; 2266170268Sdarrenr 2267145522Sdarrenr nat->nat_inip.s_addr = htonl(in.s_addr); 2268145522Sdarrenr nat->nat_outip = fin->fin_dst; 2269145522Sdarrenr nat->nat_oip = fin->fin_src; 2270153876Sguido if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0)) 2271153876Sguido nat->nat_hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, in, 2272153876Sguido (u_32_t)dport); 2273145522Sdarrenr 2274145522Sdarrenr ni->nai_sum1 = LONG_SUM(ntohl(fin->fin_daddr)) + ntohs(dport); 2275145522Sdarrenr ni->nai_sum2 = LONG_SUM(in.s_addr) + ntohs(nport); 2276145522Sdarrenr 2277145522Sdarrenr ni->nai_ip.s_addr = in.s_addr; 2278145522Sdarrenr ni->nai_nport = nport; 2279145522Sdarrenr ni->nai_port = sport; 2280145522Sdarrenr 2281145522Sdarrenr if (flags & IPN_TCPUDP) { 2282145522Sdarrenr nat->nat_inport = nport; 2283145522Sdarrenr nat->nat_outport = dport; 2284145522Sdarrenr nat->nat_oport = sport; 2285145522Sdarrenr ((tcphdr_t *)fin->fin_dp)->th_dport = nport; 2286145522Sdarrenr } else if (flags & IPN_ICMPQUERY) { 2287145522Sdarrenr ((icmphdr_t *)fin->fin_dp)->icmp_id = nport; 2288145522Sdarrenr nat->nat_inport = nport; 2289145522Sdarrenr nat->nat_outport = nport; 2290145522Sdarrenr } else if (fin->fin_p == IPPROTO_GRE) { 2291145522Sdarrenr#if 0 2292145522Sdarrenr nat->nat_gre.gs_flags = ((grehdr_t *)fin->fin_dp)->gr_flags; 2293145522Sdarrenr if (GRE_REV(nat->nat_gre.gs_flags) == 1) { 2294145522Sdarrenr nat->nat_call[0] = fin->fin_data[0]; 2295145522Sdarrenr nat->nat_call[1] = fin->fin_data[1]; 2296145522Sdarrenr nat->nat_oport = 0; /*fin->fin_data[0];*/ 2297145522Sdarrenr nat->nat_inport = 0; /*fin->fin_data[1];*/ 2298145522Sdarrenr nat->nat_outport = 0; /*fin->fin_data[1];*/ 2299145522Sdarrenr } 2300145522Sdarrenr#endif 2301145522Sdarrenr } 2302145522Sdarrenr 2303145522Sdarrenr return move; 2304145522Sdarrenr} 2305145522Sdarrenr 2306145522Sdarrenr/* ------------------------------------------------------------------------ */ 2307145522Sdarrenr/* Function: nat_new */ 2308145522Sdarrenr/* Returns: nat_t* - NULL == failure to create new NAT structure, */ 2309145522Sdarrenr/* else pointer to new NAT structure */ 2310145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2311145522Sdarrenr/* np(I) - pointer to NAT rule */ 2312145522Sdarrenr/* natsave(I) - pointer to where to store NAT struct pointer */ 2313145522Sdarrenr/* flags(I) - flags describing the current packet */ 2314145522Sdarrenr/* direction(I) - direction of packet (in/out) */ 2315145522Sdarrenr/* Write Lock: ipf_nat */ 2316145522Sdarrenr/* */ 2317145522Sdarrenr/* Attempts to create a new NAT entry. Does not actually change the packet */ 2318145522Sdarrenr/* in any way. */ 2319145522Sdarrenr/* */ 2320145522Sdarrenr/* This fucntion is in three main parts: (1) deal with creating a new NAT */ 2321145522Sdarrenr/* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */ 2322145522Sdarrenr/* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */ 2323145522Sdarrenr/* and (3) building that structure and putting it into the NAT table(s). */ 2324161356Sguido/* */ 2325161356Sguido/* NOTE: natsave should NOT be used top point back to an ipstate_t struct */ 2326161356Sguido/* as it can result in memory being corrupted. */ 2327145522Sdarrenr/* ------------------------------------------------------------------------ */ 2328145522Sdarrenrnat_t *nat_new(fin, np, natsave, flags, direction) 2329145522Sdarrenrfr_info_t *fin; 233053642Sguidoipnat_t *np; 233192685Sdarrenrnat_t **natsave; 233253642Sguidou_int flags; 233353642Sguidoint direction; 233453642Sguido{ 233553642Sguido u_short port = 0, sport = 0, dport = 0, nport = 0; 233653642Sguido tcphdr_t *tcp = NULL; 233760852Sdarrenr hostmap_t *hm = NULL; 2338145522Sdarrenr struct in_addr in; 233960852Sdarrenr nat_t *nat, *natl; 2340145522Sdarrenr u_int nflags; 2341145522Sdarrenr natinfo_t ni; 2342145522Sdarrenr u_32_t sumd; 2343145522Sdarrenr int move; 2344145522Sdarrenr#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC) 2345145522Sdarrenr qpktinfo_t *qpi = fin->fin_qpi; 234655929Sguido#endif 234753642Sguido 2348130886Sdarrenr if (nat_stats.ns_inuse >= ipf_nattable_max) { 2349130886Sdarrenr nat_stats.ns_memfail++; 2350170268Sdarrenr fr_nat_doflush = 1; 2351130886Sdarrenr return NULL; 2352130886Sdarrenr } 2353130886Sdarrenr 2354145522Sdarrenr move = 1; 2355145522Sdarrenr nflags = np->in_flags & flags; 2356145522Sdarrenr nflags &= NAT_FROMRULE; 235753642Sguido 2358145522Sdarrenr ni.nai_np = np; 2359145522Sdarrenr ni.nai_nflags = nflags; 2360145522Sdarrenr ni.nai_flags = flags; 2361170268Sdarrenr ni.nai_dport = 0; 2362170268Sdarrenr ni.nai_sport = 0; 2363145522Sdarrenr 236453642Sguido /* Give me a new nat */ 236553642Sguido KMALLOC(nat, nat_t *); 236660852Sdarrenr if (nat == NULL) { 236760852Sdarrenr nat_stats.ns_memfail++; 2368130886Sdarrenr /* 2369130886Sdarrenr * Try to automatically tune the max # of entries in the 2370130886Sdarrenr * table allowed to be less than what will cause kmem_alloc() 2371130886Sdarrenr * to fail and try to eliminate panics due to out of memory 2372130886Sdarrenr * conditions arising. 2373130886Sdarrenr */ 2374130886Sdarrenr if (ipf_nattable_max > ipf_nattable_sz) { 2375130886Sdarrenr ipf_nattable_max = nat_stats.ns_inuse - 100; 2376130886Sdarrenr printf("ipf_nattable_max reduced to %d\n", 2377130886Sdarrenr ipf_nattable_max); 2378130886Sdarrenr } 237953642Sguido return NULL; 238060852Sdarrenr } 238153642Sguido 2382145522Sdarrenr if (flags & IPN_TCPUDP) { 2383145522Sdarrenr tcp = fin->fin_dp; 2384145522Sdarrenr ni.nai_sport = htons(fin->fin_sport); 2385145522Sdarrenr ni.nai_dport = htons(fin->fin_dport); 2386145522Sdarrenr } else if (flags & IPN_ICMPQUERY) { 2387145522Sdarrenr /* 2388145522Sdarrenr * In the ICMP query NAT code, we translate the ICMP id fields 2389145522Sdarrenr * to make them unique. This is indepedent of the ICMP type 2390145522Sdarrenr * (e.g. in the unlikely event that a host sends an echo and 2391145522Sdarrenr * an tstamp request with the same id, both packets will have 2392145522Sdarrenr * their ip address/id field changed in the same way). 2393145522Sdarrenr */ 2394145522Sdarrenr /* The icmp_id field is used by the sender to identify the 2395145522Sdarrenr * process making the icmp request. (the receiver justs 2396145522Sdarrenr * copies it back in its response). So, it closely matches 2397145522Sdarrenr * the concept of source port. We overlay sport, so we can 2398145522Sdarrenr * maximally reuse the existing code. 2399145522Sdarrenr */ 2400145522Sdarrenr ni.nai_sport = ((icmphdr_t *)fin->fin_dp)->icmp_id; 2401145522Sdarrenr ni.nai_dport = ni.nai_sport; 2402145522Sdarrenr } 2403145522Sdarrenr 240453642Sguido bzero((char *)nat, sizeof(*nat)); 240553642Sguido nat->nat_flags = flags; 2406170268Sdarrenr nat->nat_redir = np->in_redir; 2407145522Sdarrenr 2408145522Sdarrenr if ((flags & NAT_SLAVE) == 0) { 2409145522Sdarrenr MUTEX_ENTER(&ipf_nat_new); 2410145522Sdarrenr } 2411145522Sdarrenr 241253642Sguido /* 241353642Sguido * Search the current table for a match. 241453642Sguido */ 241553642Sguido if (direction == NAT_OUTBOUND) { 241653642Sguido /* 2417145522Sdarrenr * We can now arrange to call this for the same connection 2418145522Sdarrenr * because ipf_nat_new doesn't protect the code path into 2419145522Sdarrenr * this function. 242053642Sguido */ 2421145522Sdarrenr natl = nat_outlookup(fin, nflags, (u_int)fin->fin_p, 2422145522Sdarrenr fin->fin_src, fin->fin_dst); 2423145522Sdarrenr if (natl != NULL) { 2424161356Sguido KFREE(nat); 2425145522Sdarrenr nat = natl; 2426145522Sdarrenr goto done; 2427145522Sdarrenr } 242853642Sguido 2429145522Sdarrenr move = nat_newmap(fin, nat, &ni); 2430145522Sdarrenr if (move == -1) 2431145522Sdarrenr goto badnat; 243253642Sguido 2433145522Sdarrenr np = ni.nai_np; 2434145522Sdarrenr in = ni.nai_ip; 243553642Sguido } else { 243653642Sguido /* 2437145522Sdarrenr * NAT_INBOUND is used only for redirects rules 243853642Sguido */ 2439145522Sdarrenr natl = nat_inlookup(fin, nflags, (u_int)fin->fin_p, 2440145522Sdarrenr fin->fin_src, fin->fin_dst); 2441145522Sdarrenr if (natl != NULL) { 2442161356Sguido KFREE(nat); 2443145522Sdarrenr nat = natl; 2444145522Sdarrenr goto done; 244560852Sdarrenr } 244653642Sguido 2447145522Sdarrenr move = nat_newrdr(fin, nat, &ni); 2448145522Sdarrenr if (move == -1) 2449145522Sdarrenr goto badnat; 245053642Sguido 2451145522Sdarrenr np = ni.nai_np; 2452145522Sdarrenr in = ni.nai_ip; 2453145522Sdarrenr } 2454145522Sdarrenr port = ni.nai_port; 2455145522Sdarrenr nport = ni.nai_nport; 245653642Sguido 2457145522Sdarrenr if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { 2458145522Sdarrenr if (np->in_redir == NAT_REDIRECT) { 2459145522Sdarrenr nat_delrdr(np); 2460145522Sdarrenr nat_addrdr(np); 2461145522Sdarrenr } else if (np->in_redir == NAT_MAP) { 2462145522Sdarrenr nat_delnat(np); 2463145522Sdarrenr nat_addnat(np); 246453642Sguido } 246553642Sguido } 246653642Sguido 2467145522Sdarrenr if (flags & IPN_TCPUDP) { 2468145522Sdarrenr sport = ni.nai_sport; 2469145522Sdarrenr dport = ni.nai_dport; 2470145522Sdarrenr } else if (flags & IPN_ICMPQUERY) { 2471145522Sdarrenr sport = ni.nai_sport; 2472145522Sdarrenr dport = 0; 2473145522Sdarrenr } 2474145522Sdarrenr 2475145522Sdarrenr CALC_SUMD(ni.nai_sum1, ni.nai_sum2, sumd); 247655929Sguido nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 2477145522Sdarrenr#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC) 2478130886Sdarrenr if ((flags & IPN_TCP) && dohwcksum && 2479145522Sdarrenr (((ill_t *)qpi->qpi_ill)->ill_ick.ick_magic == ICK_M_CTL_MAGIC)) { 248055929Sguido if (direction == NAT_OUTBOUND) 2481145522Sdarrenr ni.nai_sum1 = LONG_SUM(in.s_addr); 248255929Sguido else 2483145522Sdarrenr ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr)); 2484145522Sdarrenr ni.nai_sum1 += LONG_SUM(ntohl(fin->fin_daddr)); 2485145522Sdarrenr ni.nai_sum1 += 30; 2486145522Sdarrenr ni.nai_sum1 = (ni.nai_sum1 & 0xffff) + (ni.nai_sum1 >> 16); 2487145522Sdarrenr nat->nat_sumd[1] = NAT_HW_CKSUM|(ni.nai_sum1 & 0xffff); 248855929Sguido } else 248955929Sguido#endif 249055929Sguido nat->nat_sumd[1] = nat->nat_sumd[0]; 249153642Sguido 2492145522Sdarrenr if ((flags & IPN_TCPUDPICMP) && ((sport != port) || (dport != nport))) { 249353642Sguido if (direction == NAT_OUTBOUND) 2494145522Sdarrenr ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr)); 249553642Sguido else 2496145522Sdarrenr ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_daddr)); 249753642Sguido 2498145522Sdarrenr ni.nai_sum2 = LONG_SUM(in.s_addr); 249953642Sguido 2500145522Sdarrenr CALC_SUMD(ni.nai_sum1, ni.nai_sum2, sumd); 250153642Sguido nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); 2502145522Sdarrenr } else { 250355929Sguido nat->nat_ipsumd = nat->nat_sumd[0]; 2504145522Sdarrenr if (!(flags & IPN_TCPUDPICMP)) { 2505145522Sdarrenr nat->nat_sumd[0] = 0; 2506145522Sdarrenr nat->nat_sumd[1] = 0; 2507145522Sdarrenr } 2508145522Sdarrenr } 250953642Sguido 2510145522Sdarrenr if (nat_finalise(fin, nat, &ni, tcp, natsave, direction) == -1) { 2511172776Sdarrenr fr_nat_doflush = 1; 2512145522Sdarrenr goto badnat; 2513145522Sdarrenr } 2514145522Sdarrenr if (flags & SI_WILDP) 2515145522Sdarrenr nat_stats.ns_wilds++; 2516172776Sdarrenr fin->fin_flx |= FI_NEWNAT; 2517145522Sdarrenr goto done; 2518145522Sdarrenrbadnat: 2519145522Sdarrenr nat_stats.ns_badnat++; 2520145522Sdarrenr if ((hm = nat->nat_hm) != NULL) 2521170268Sdarrenr fr_hostmapdel(&hm); 2522145522Sdarrenr KFREE(nat); 2523145522Sdarrenr nat = NULL; 2524145522Sdarrenrdone: 2525145522Sdarrenr if ((flags & NAT_SLAVE) == 0) { 2526145522Sdarrenr MUTEX_EXIT(&ipf_nat_new); 2527145522Sdarrenr } 2528145522Sdarrenr return nat; 2529145522Sdarrenr} 253060852Sdarrenr 253160852Sdarrenr 2532145522Sdarrenr/* ------------------------------------------------------------------------ */ 2533145522Sdarrenr/* Function: nat_finalise */ 2534145522Sdarrenr/* Returns: int - 0 == sucess, -1 == failure */ 2535145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2536145522Sdarrenr/* nat(I) - pointer to NAT entry */ 2537145522Sdarrenr/* ni(I) - pointer to structure with misc. information needed */ 2538145522Sdarrenr/* to create new NAT entry. */ 2539145522Sdarrenr/* Write Lock: ipf_nat */ 2540145522Sdarrenr/* */ 2541145522Sdarrenr/* This is the tail end of constructing a new NAT entry and is the same */ 2542145522Sdarrenr/* for both IPv4 and IPv6. */ 2543145522Sdarrenr/* ------------------------------------------------------------------------ */ 2544145522Sdarrenr/*ARGSUSED*/ 2545153876Sguidostatic int nat_finalise(fin, nat, ni, tcp, natsave, direction) 2546145522Sdarrenrfr_info_t *fin; 2547145522Sdarrenrnat_t *nat; 2548145522Sdarrenrnatinfo_t *ni; 2549145522Sdarrenrtcphdr_t *tcp; 2550145522Sdarrenrnat_t **natsave; 2551145522Sdarrenrint direction; 2552145522Sdarrenr{ 2553145522Sdarrenr frentry_t *fr; 2554145522Sdarrenr ipnat_t *np; 2555145522Sdarrenr 2556145522Sdarrenr np = ni->nai_np; 2557145522Sdarrenr 2558161356Sguido if (np->in_ifps[0] != NULL) { 2559172776Sdarrenr COPYIFNAME(4, np->in_ifps[0], nat->nat_ifnames[0]); 2560161356Sguido } 2561161356Sguido if (np->in_ifps[1] != NULL) { 2562172776Sdarrenr COPYIFNAME(4, np->in_ifps[1], nat->nat_ifnames[1]); 2563161356Sguido } 2564145522Sdarrenr#ifdef IPFILTER_SYNC 2565145522Sdarrenr if ((nat->nat_flags & SI_CLONE) == 0) 2566145522Sdarrenr nat->nat_sync = ipfsync_new(SMC_NAT, fin, nat); 2567145522Sdarrenr#endif 2568145522Sdarrenr 256992685Sdarrenr nat->nat_me = natsave; 257053642Sguido nat->nat_dir = direction; 2571161356Sguido nat->nat_ifps[0] = np->in_ifps[0]; 2572161356Sguido nat->nat_ifps[1] = np->in_ifps[1]; 257353642Sguido nat->nat_ptr = np; 257492685Sdarrenr nat->nat_p = fin->fin_p; 2575110916Sdarrenr nat->nat_mssclamp = np->in_mssclamp; 2576172776Sdarrenr if (nat->nat_p == IPPROTO_TCP) 2577172776Sdarrenr nat->nat_seqnext[0] = ntohl(tcp->th_seq); 257892685Sdarrenr 2579145522Sdarrenr if ((np->in_apr != NULL) && ((ni->nai_flags & NAT_SLAVE) == 0)) 2580145522Sdarrenr if (appr_new(fin, nat) == -1) 2581145522Sdarrenr return -1; 258292685Sdarrenr 2583145522Sdarrenr if (nat_insert(nat, fin->fin_rev) == 0) { 2584145522Sdarrenr if (nat_logging) 2585145522Sdarrenr nat_log(nat, (u_int)np->in_redir); 2586145522Sdarrenr np->in_use++; 2587153876Sguido fr = fin->fin_fr; 2588153876Sguido nat->nat_fr = fr; 2589145522Sdarrenr if (fr != NULL) { 2590145522Sdarrenr MUTEX_ENTER(&fr->fr_lock); 2591145522Sdarrenr fr->fr_ref++; 2592145522Sdarrenr MUTEX_EXIT(&fr->fr_lock); 2593145522Sdarrenr } 2594145522Sdarrenr return 0; 2595145522Sdarrenr } 259692685Sdarrenr 2597145522Sdarrenr /* 2598145522Sdarrenr * nat_insert failed, so cleanup time... 2599145522Sdarrenr */ 2600145522Sdarrenr return -1; 260153642Sguido} 260253642Sguido 260353642Sguido 2604145522Sdarrenr/* ------------------------------------------------------------------------ */ 2605145522Sdarrenr/* Function: nat_insert */ 2606145522Sdarrenr/* Returns: int - 0 == sucess, -1 == failure */ 2607145522Sdarrenr/* Parameters: nat(I) - pointer to NAT structure */ 2608145522Sdarrenr/* rev(I) - flag indicating forward/reverse direction of packet */ 2609145522Sdarrenr/* Write Lock: ipf_nat */ 2610145522Sdarrenr/* */ 2611145522Sdarrenr/* Insert a NAT entry into the hash tables for searching and add it to the */ 2612145522Sdarrenr/* list of active NAT entries. Adjust global counters when complete. */ 2613145522Sdarrenr/* ------------------------------------------------------------------------ */ 2614145522Sdarrenrint nat_insert(nat, rev) 261560852Sdarrenrnat_t *nat; 2616145522Sdarrenrint rev; 261760852Sdarrenr{ 261880482Sdarrenr u_int hv1, hv2; 261960852Sdarrenr nat_t **natp; 262060852Sdarrenr 2621145522Sdarrenr /* 2622145522Sdarrenr * Try and return an error as early as possible, so calculate the hash 2623145522Sdarrenr * entry numbers first and then proceed. 2624145522Sdarrenr */ 2625145522Sdarrenr if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { 262680482Sdarrenr hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 262780482Sdarrenr 0xffffffff); 262880482Sdarrenr hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1 + nat->nat_oport, 262980482Sdarrenr ipf_nattable_sz); 263080482Sdarrenr hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, 263180482Sdarrenr 0xffffffff); 263280482Sdarrenr hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2 + nat->nat_oport, 2633145522Sdarrenr ipf_nattable_sz); 263480482Sdarrenr } else { 2635145522Sdarrenr hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, 0, 0xffffffff); 2636145522Sdarrenr hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1, ipf_nattable_sz); 2637145522Sdarrenr hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, 0, 0xffffffff); 2638145522Sdarrenr hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2, ipf_nattable_sz); 263980482Sdarrenr } 264080482Sdarrenr 2641145522Sdarrenr if (nat_stats.ns_bucketlen[0][hv1] >= fr_nat_maxbucket || 2642145522Sdarrenr nat_stats.ns_bucketlen[1][hv2] >= fr_nat_maxbucket) { 2643145522Sdarrenr return -1; 2644145522Sdarrenr } 2645145522Sdarrenr 2646145522Sdarrenr nat->nat_hv[0] = hv1; 2647145522Sdarrenr nat->nat_hv[1] = hv2; 2648145522Sdarrenr 2649145522Sdarrenr MUTEX_INIT(&nat->nat_lock, "nat entry lock"); 2650145522Sdarrenr 2651145522Sdarrenr nat->nat_rev = rev; 2652145522Sdarrenr nat->nat_ref = 1; 2653145522Sdarrenr nat->nat_bytes[0] = 0; 2654145522Sdarrenr nat->nat_pkts[0] = 0; 2655145522Sdarrenr nat->nat_bytes[1] = 0; 2656145522Sdarrenr nat->nat_pkts[1] = 0; 2657145522Sdarrenr 2658145522Sdarrenr nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; 2659145522Sdarrenr nat->nat_ifps[0] = fr_resolvenic(nat->nat_ifnames[0], 4); 2660145522Sdarrenr 2661161356Sguido if (nat->nat_ifnames[1][0] != '\0') { 2662145522Sdarrenr nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 2663145522Sdarrenr nat->nat_ifps[1] = fr_resolvenic(nat->nat_ifnames[1], 4); 2664145522Sdarrenr } else { 2665145522Sdarrenr (void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0], 2666145522Sdarrenr LIFNAMSIZ); 2667145522Sdarrenr nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 2668145522Sdarrenr nat->nat_ifps[1] = nat->nat_ifps[0]; 2669145522Sdarrenr } 2670145522Sdarrenr 2671145522Sdarrenr nat->nat_next = nat_instances; 2672145522Sdarrenr nat->nat_pnext = &nat_instances; 2673145522Sdarrenr if (nat_instances) 2674145522Sdarrenr nat_instances->nat_pnext = &nat->nat_next; 2675145522Sdarrenr nat_instances = nat; 2676145522Sdarrenr 267780482Sdarrenr natp = &nat_table[0][hv1]; 267867614Sdarrenr if (*natp) 267967614Sdarrenr (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 268067614Sdarrenr nat->nat_phnext[0] = natp; 268160852Sdarrenr nat->nat_hnext[0] = *natp; 268260852Sdarrenr *natp = nat; 2683145522Sdarrenr nat_stats.ns_bucketlen[0][hv1]++; 268467614Sdarrenr 268580482Sdarrenr natp = &nat_table[1][hv2]; 268667614Sdarrenr if (*natp) 268767614Sdarrenr (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 268867614Sdarrenr nat->nat_phnext[1] = natp; 268960852Sdarrenr nat->nat_hnext[1] = *natp; 269060852Sdarrenr *natp = nat; 2691145522Sdarrenr nat_stats.ns_bucketlen[1][hv2]++; 269260852Sdarrenr 2693145522Sdarrenr fr_setnatqueue(nat, rev); 2694145522Sdarrenr 269560852Sdarrenr nat_stats.ns_added++; 269660852Sdarrenr nat_stats.ns_inuse++; 2697145522Sdarrenr return 0; 269860852Sdarrenr} 269960852Sdarrenr 270060852Sdarrenr 2701145522Sdarrenr/* ------------------------------------------------------------------------ */ 2702145522Sdarrenr/* Function: nat_icmperrorlookup */ 2703145522Sdarrenr/* Returns: nat_t* - point to matching NAT structure */ 2704145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2705145522Sdarrenr/* dir(I) - direction of packet (in/out) */ 2706145522Sdarrenr/* */ 2707145522Sdarrenr/* Check if the ICMP error message is related to an existing TCP, UDP or */ 2708145522Sdarrenr/* ICMP query nat entry. It is assumed that the packet is already of the */ 2709145522Sdarrenr/* the required length. */ 2710145522Sdarrenr/* ------------------------------------------------------------------------ */ 2711145522Sdarrenrnat_t *nat_icmperrorlookup(fin, dir) 271253642Sguidofr_info_t *fin; 271360852Sdarrenrint dir; 271453642Sguido{ 2715145522Sdarrenr int flags = 0, type, minlen; 2716145522Sdarrenr icmphdr_t *icmp, *orgicmp; 271753642Sguido tcphdr_t *tcp = NULL; 2718145522Sdarrenr u_short data[2]; 2719145522Sdarrenr nat_t *nat; 272053642Sguido ip_t *oip; 2721145522Sdarrenr u_int p; 272253642Sguido 2723145522Sdarrenr icmp = fin->fin_dp; 2724145522Sdarrenr type = icmp->icmp_type; 272553642Sguido /* 272653642Sguido * Does it at least have the return (basic) IP header ? 272753642Sguido * Only a basic IP header (no options) should be with an ICMP error 2728145522Sdarrenr * header. Also, if it's not an error type, then return. 272953642Sguido */ 2730153876Sguido if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) 273153642Sguido return NULL; 2732145522Sdarrenr 273353642Sguido /* 2734145522Sdarrenr * Check packet size 273553642Sguido */ 273653642Sguido oip = (ip_t *)((char *)fin->fin_dp + 8); 2737145522Sdarrenr minlen = IP_HL(oip) << 2; 2738145522Sdarrenr if ((minlen < sizeof(ip_t)) || 2739145522Sdarrenr (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) 274053642Sguido return NULL; 274164580Sdarrenr /* 274264580Sdarrenr * Is the buffer big enough for all of it ? It's the size of the IP 274364580Sdarrenr * header claimed in the encapsulated part which is of concern. It 274464580Sdarrenr * may be too big to be in this buffer but not so big that it's 274564580Sdarrenr * outside the ICMP packet, leading to TCP deref's causing problems. 274664580Sdarrenr * This is possible because we don't know how big oip_hl is when we 274764580Sdarrenr * do the pullup early in fr_check() and thus can't gaurantee it is 274864580Sdarrenr * all here now. 274964580Sdarrenr */ 275064580Sdarrenr#ifdef _KERNEL 275164580Sdarrenr { 275264580Sdarrenr mb_t *m; 275364580Sdarrenr 2754145522Sdarrenr m = fin->fin_m; 2755145522Sdarrenr# if defined(MENTAT) 275664580Sdarrenr if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr) 275764580Sdarrenr return NULL; 275864580Sdarrenr# else 275964580Sdarrenr if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > 2760145522Sdarrenr (char *)fin->fin_ip + M_LEN(m)) 276164580Sdarrenr return NULL; 276264580Sdarrenr# endif 276364580Sdarrenr } 276464580Sdarrenr#endif 276564580Sdarrenr 2766145522Sdarrenr if (fin->fin_daddr != oip->ip_src.s_addr) 2767145522Sdarrenr return NULL; 2768145522Sdarrenr 2769145522Sdarrenr p = oip->ip_p; 2770145522Sdarrenr if (p == IPPROTO_TCP) 277153642Sguido flags = IPN_TCP; 2772145522Sdarrenr else if (p == IPPROTO_UDP) 277353642Sguido flags = IPN_UDP; 2774145522Sdarrenr else if (p == IPPROTO_ICMP) { 2775145522Sdarrenr orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 2776145522Sdarrenr 2777145522Sdarrenr /* see if this is related to an ICMP query */ 2778145522Sdarrenr if (nat_icmpquerytype4(orgicmp->icmp_type)) { 2779145522Sdarrenr data[0] = fin->fin_data[0]; 2780145522Sdarrenr data[1] = fin->fin_data[1]; 2781145522Sdarrenr fin->fin_data[0] = 0; 2782145522Sdarrenr fin->fin_data[1] = orgicmp->icmp_id; 2783145522Sdarrenr 2784145522Sdarrenr flags = IPN_ICMPERR|IPN_ICMPQUERY; 2785145522Sdarrenr /* 2786145522Sdarrenr * NOTE : dir refers to the direction of the original 2787145522Sdarrenr * ip packet. By definition the icmp error 2788145522Sdarrenr * message flows in the opposite direction. 2789145522Sdarrenr */ 2790145522Sdarrenr if (dir == NAT_INBOUND) 2791145522Sdarrenr nat = nat_inlookup(fin, flags, p, oip->ip_dst, 2792145522Sdarrenr oip->ip_src); 2793145522Sdarrenr else 2794145522Sdarrenr nat = nat_outlookup(fin, flags, p, oip->ip_dst, 2795145522Sdarrenr oip->ip_src); 2796145522Sdarrenr fin->fin_data[0] = data[0]; 2797145522Sdarrenr fin->fin_data[1] = data[1]; 2798145522Sdarrenr return nat; 2799145522Sdarrenr } 2800145522Sdarrenr } 2801145522Sdarrenr 280253642Sguido if (flags & IPN_TCPUDP) { 280364580Sdarrenr minlen += 8; /* + 64bits of data to get ports */ 2804145522Sdarrenr if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) 280564580Sdarrenr return NULL; 280692685Sdarrenr 280792685Sdarrenr data[0] = fin->fin_data[0]; 280892685Sdarrenr data[1] = fin->fin_data[1]; 2809145522Sdarrenr tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 281092685Sdarrenr fin->fin_data[0] = ntohs(tcp->th_dport); 281192685Sdarrenr fin->fin_data[1] = ntohs(tcp->th_sport); 281292685Sdarrenr 281392685Sdarrenr if (dir == NAT_INBOUND) { 2814145522Sdarrenr nat = nat_inlookup(fin, flags, p, oip->ip_dst, 2815145522Sdarrenr oip->ip_src); 281692685Sdarrenr } else { 2817145522Sdarrenr nat = nat_outlookup(fin, flags, p, oip->ip_dst, 2818145522Sdarrenr oip->ip_src); 281992685Sdarrenr } 282092685Sdarrenr fin->fin_data[0] = data[0]; 282192685Sdarrenr fin->fin_data[1] = data[1]; 282292685Sdarrenr return nat; 282353642Sguido } 282460852Sdarrenr if (dir == NAT_INBOUND) 2825145522Sdarrenr return nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src); 282660852Sdarrenr else 2827145522Sdarrenr return nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src); 282853642Sguido} 282953642Sguido 283053642Sguido 2831145522Sdarrenr/* ------------------------------------------------------------------------ */ 2832145522Sdarrenr/* Function: nat_icmperror */ 2833145522Sdarrenr/* Returns: nat_t* - point to matching NAT structure */ 2834145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2835145522Sdarrenr/* nflags(I) - NAT flags for this packet */ 2836145522Sdarrenr/* dir(I) - direction of packet (in/out) */ 2837145522Sdarrenr/* */ 2838145522Sdarrenr/* Fix up an ICMP packet which is an error message for an existing NAT */ 2839145522Sdarrenr/* session. This will correct both packet header data and checksums. */ 2840145522Sdarrenr/* */ 2841145522Sdarrenr/* This should *ONLY* be used for incoming ICMP error packets to make sure */ 2842145522Sdarrenr/* a NAT'd ICMP packet gets correctly recognised. */ 2843145522Sdarrenr/* ------------------------------------------------------------------------ */ 2844145522Sdarrenrnat_t *nat_icmperror(fin, nflags, dir) 284553642Sguidofr_info_t *fin; 284653642Sguidou_int *nflags; 284760852Sdarrenrint dir; 284853642Sguido{ 2849145522Sdarrenr u_32_t sum1, sum2, sumd, sumd2; 2850170268Sdarrenr struct in_addr a1, a2; 2851170268Sdarrenr int flags, dlen, odst; 2852145522Sdarrenr icmphdr_t *icmp; 2853145522Sdarrenr u_short *csump; 285495418Sdarrenr tcphdr_t *tcp; 285553642Sguido nat_t *nat; 285653642Sguido ip_t *oip; 2857145522Sdarrenr void *dp; 285853642Sguido 2859145522Sdarrenr if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) 286063523Sdarrenr return NULL; 286167614Sdarrenr /* 2862145522Sdarrenr * nat_icmperrorlookup() will return NULL for `defective' packets. 286367614Sdarrenr */ 2864145522Sdarrenr if ((fin->fin_v != 4) || !(nat = nat_icmperrorlookup(fin, dir))) 286553642Sguido return NULL; 286692685Sdarrenr 2867145522Sdarrenr tcp = NULL; 2868145522Sdarrenr csump = NULL; 286992685Sdarrenr flags = 0; 2870130886Sdarrenr sumd2 = 0; 287153642Sguido *nflags = IPN_ICMPERR; 2872145522Sdarrenr icmp = fin->fin_dp; 287353642Sguido oip = (ip_t *)&icmp->icmp_ip; 2874145522Sdarrenr dp = (((char *)oip) + (IP_HL(oip) << 2)); 2875145522Sdarrenr if (oip->ip_p == IPPROTO_TCP) { 2876145522Sdarrenr tcp = (tcphdr_t *)dp; 2877145522Sdarrenr csump = (u_short *)&tcp->th_sum; 287853642Sguido flags = IPN_TCP; 2879145522Sdarrenr } else if (oip->ip_p == IPPROTO_UDP) { 2880145522Sdarrenr udphdr_t *udp; 2881145522Sdarrenr 2882145522Sdarrenr udp = (udphdr_t *)dp; 2883145522Sdarrenr tcp = (tcphdr_t *)dp; 2884145522Sdarrenr csump = (u_short *)&udp->uh_sum; 288553642Sguido flags = IPN_UDP; 2886145522Sdarrenr } else if (oip->ip_p == IPPROTO_ICMP) 2887145522Sdarrenr flags = IPN_ICMPQUERY; 2888145522Sdarrenr dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip); 288995418Sdarrenr 289095418Sdarrenr /* 289153642Sguido * Need to adjust ICMP header to include the real IP#'s and 289253642Sguido * port #'s. Only apply a checksum change relative to the 2893145522Sdarrenr * IP address change as it will be modified again in fr_checknatout 289453642Sguido * for both address and port. Two checksum changes are 289553642Sguido * necessary for the two header address changes. Be careful 289653642Sguido * to only modify the checksum once for the port # and twice 289753642Sguido * for the IP#. 289853642Sguido */ 289960852Sdarrenr 290067614Sdarrenr /* 290167614Sdarrenr * Step 1 290267614Sdarrenr * Fix the IP addresses in the offending IP packet. You also need 2903170268Sdarrenr * to adjust the IP header checksum of that offending IP packet. 290467614Sdarrenr * 2905145522Sdarrenr * Normally, you would expect that the ICMP checksum of the 2906130886Sdarrenr * ICMP error message needs to be adjusted as well for the 2907130886Sdarrenr * IP address change in oip. 2908145522Sdarrenr * However, this is a NOP, because the ICMP checksum is 2909130886Sdarrenr * calculated over the complete ICMP packet, which includes the 2910145522Sdarrenr * changed oip IP addresses and oip->ip_sum. However, these 2911130886Sdarrenr * two changes cancel each other out (if the delta for 2912145522Sdarrenr * the IP address is x, then the delta for ip_sum is minus x), 2913130886Sdarrenr * so no change in the icmp_cksum is necessary. 2914130886Sdarrenr * 2915170268Sdarrenr * Inbound ICMP 2916170268Sdarrenr * ------------ 2917170268Sdarrenr * MAP rule, SRC=a,DST=b -> SRC=c,DST=b 2918170268Sdarrenr * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b) 2919170268Sdarrenr * - OIP_SRC(c)=nat_outip, OIP_DST(b)=nat_oip 2920170268Sdarrenr * 2921170268Sdarrenr * RDR rule, SRC=a,DST=b -> SRC=a,DST=c 2922170268Sdarrenr * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a) 2923170268Sdarrenr * - OIP_SRC(b)=nat_outip, OIP_DST(a)=nat_oip 2924170268Sdarrenr * 2925170268Sdarrenr * Outbound ICMP 2926170268Sdarrenr * ------------- 2927170268Sdarrenr * MAP rule, SRC=a,DST=b -> SRC=c,DST=b 2928170268Sdarrenr * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) 2929170268Sdarrenr * - OIP_SRC(a)=nat_oip, OIP_DST(c)=nat_inip 2930170268Sdarrenr * 2931170268Sdarrenr * RDR rule, SRC=a,DST=b -> SRC=a,DST=c 2932170268Sdarrenr * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c) 2933170268Sdarrenr * - OIP_SRC(a)=nat_oip, OIP_DST(c)=nat_inip 2934170268Sdarrenr * 2935130886Sdarrenr */ 2936170268Sdarrenr odst = (oip->ip_dst.s_addr == nat->nat_oip.s_addr) ? 1 : 0; 2937170268Sdarrenr if (odst == 1) { 2938170268Sdarrenr a1.s_addr = ntohl(nat->nat_inip.s_addr); 2939170268Sdarrenr a2.s_addr = ntohl(oip->ip_src.s_addr); 2940170268Sdarrenr oip->ip_src.s_addr = htonl(a1.s_addr); 2941170268Sdarrenr } else { 2942170268Sdarrenr a1.s_addr = ntohl(nat->nat_outip.s_addr); 2943170268Sdarrenr a2.s_addr = ntohl(oip->ip_dst.s_addr); 2944170268Sdarrenr oip->ip_dst.s_addr = htonl(a1.s_addr); 2945170268Sdarrenr } 2946130886Sdarrenr 2947170268Sdarrenr sumd = a2.s_addr - a1.s_addr; 2948170268Sdarrenr if (sumd != 0) { 2949170268Sdarrenr if (a1.s_addr > a2.s_addr) 2950170268Sdarrenr sumd--; 2951170268Sdarrenr sumd = ~sumd; 295253642Sguido 2953170268Sdarrenr fix_datacksum(&oip->ip_sum, sumd); 2954130886Sdarrenr } 295567614Sdarrenr 2956170268Sdarrenr sumd2 = sumd; 2957170268Sdarrenr sum1 = 0; 2958170268Sdarrenr sum2 = 0; 2959170268Sdarrenr 2960130886Sdarrenr /* 2961170268Sdarrenr * Fix UDP pseudo header checksum to compensate for the 2962170268Sdarrenr * IP address change. 2963130886Sdarrenr */ 2964130886Sdarrenr if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) { 296564580Sdarrenr /* 296667614Sdarrenr * Step 2 : 296767614Sdarrenr * For offending TCP/UDP IP packets, translate the ports as 296867614Sdarrenr * well, based on the NAT specification. Of course such 2969170268Sdarrenr * a change may be reflected in the ICMP checksum as well. 297067614Sdarrenr * 297167614Sdarrenr * Since the port fields are part of the TCP/UDP checksum 297267614Sdarrenr * of the offending IP packet, you need to adjust that checksum 2973161356Sguido * as well... except that the change in the port numbers should 2974170268Sdarrenr * be offset by the checksum change. However, the TCP/UDP 2975170268Sdarrenr * checksum will also need to change if there has been an 2976170268Sdarrenr * IP address change. 297767614Sdarrenr */ 2978170268Sdarrenr if (odst == 1) { 2979170268Sdarrenr sum1 = ntohs(nat->nat_inport); 2980170268Sdarrenr sum2 = ntohs(tcp->th_sport); 2981145522Sdarrenr 2982170268Sdarrenr tcp->th_sport = htons(sum1); 2983170268Sdarrenr } else { 2984145522Sdarrenr sum1 = ntohs(nat->nat_outport); 2985145522Sdarrenr sum2 = ntohs(tcp->th_dport); 2986170268Sdarrenr 2987170268Sdarrenr tcp->th_dport = htons(sum1); 2988145522Sdarrenr } 298967614Sdarrenr 2990170268Sdarrenr sumd += sum1 - sum2; 2991170268Sdarrenr if (sumd != 0 || sumd2 != 0) { 2992145522Sdarrenr /* 2993170268Sdarrenr * At this point, sumd is the delta to apply to the 2994170268Sdarrenr * TCP/UDP header, given the changes in both the IP 2995170268Sdarrenr * address and the ports and sumd2 is the delta to 2996170268Sdarrenr * apply to the ICMP header, given the IP address 2997170268Sdarrenr * change delta that may need to be applied to the 2998170268Sdarrenr * TCP/UDP checksum instead. 2999145522Sdarrenr * 3000170268Sdarrenr * If we will both the IP and TCP/UDP checksums 3001170268Sdarrenr * then the ICMP checksum changes by the address 3002170268Sdarrenr * delta applied to the TCP/UDP checksum. If we 3003170268Sdarrenr * do not change the TCP/UDP checksum them we 3004170268Sdarrenr * apply the delta in ports to the ICMP checksum. 3005145522Sdarrenr */ 3006161356Sguido if (oip->ip_p == IPPROTO_UDP) { 3007161356Sguido if ((dlen >= 8) && (*csump != 0)) { 3008161356Sguido fix_datacksum(csump, sumd); 3009161356Sguido } else { 3010170268Sdarrenr sumd2 = sum1 - sum2; 3011170268Sdarrenr if (sum2 > sum1) 3012170268Sdarrenr sumd2--; 3013161356Sguido } 3014170268Sdarrenr } else if (oip->ip_p == IPPROTO_TCP) { 3015145522Sdarrenr if (dlen >= 18) { 3016145522Sdarrenr fix_datacksum(csump, sumd); 3017145522Sdarrenr } else { 3018170268Sdarrenr sumd2 = sum2 - sum1; 3019170268Sdarrenr if (sum1 > sum2) 3020170268Sdarrenr sumd2--; 302167614Sdarrenr } 302253642Sguido } 3023130886Sdarrenr 3024170268Sdarrenr if (sumd2 != 0) { 3025172776Sdarrenr ipnat_t *np; 3026172776Sdarrenr 3027172776Sdarrenr np = nat->nat_ptr; 3028170268Sdarrenr sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 3029170268Sdarrenr sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 3030170268Sdarrenr sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 3031172776Sdarrenr 3032172776Sdarrenr if ((odst == 0) && (dir == NAT_OUTBOUND) && 3033172776Sdarrenr (fin->fin_rev == 0) && (np != NULL) && 3034172776Sdarrenr (np->in_redir & NAT_REDIRECT)) { 3035172776Sdarrenr fix_outcksum(fin, &icmp->icmp_cksum, 3036172776Sdarrenr sumd2); 3037172776Sdarrenr } else { 3038172776Sdarrenr fix_incksum(fin, &icmp->icmp_cksum, 3039172776Sdarrenr sumd2); 3040172776Sdarrenr } 3041130886Sdarrenr } 304253642Sguido } 3043145522Sdarrenr } else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) { 3044145522Sdarrenr icmphdr_t *orgicmp; 3045145522Sdarrenr 3046145522Sdarrenr /* 3047145522Sdarrenr * XXX - what if this is bogus hl and we go off the end ? 3048145522Sdarrenr * In this case, nat_icmperrorlookup() will have returned NULL. 3049145522Sdarrenr */ 3050145522Sdarrenr orgicmp = (icmphdr_t *)dp; 3051145522Sdarrenr 3052170268Sdarrenr if (odst == 1) { 3053145522Sdarrenr if (orgicmp->icmp_id != nat->nat_inport) { 3054145522Sdarrenr 3055145522Sdarrenr /* 3056145522Sdarrenr * Fix ICMP checksum (of the offening ICMP 3057145522Sdarrenr * query packet) to compensate the change 3058145522Sdarrenr * in the ICMP id of the offending ICMP 3059145522Sdarrenr * packet. 3060145522Sdarrenr * 3061145522Sdarrenr * Since you modify orgicmp->icmp_id with 3062145522Sdarrenr * a delta (say x) and you compensate that 3063145522Sdarrenr * in origicmp->icmp_cksum with a delta 3064145522Sdarrenr * minus x, you don't have to adjust the 3065145522Sdarrenr * overall icmp->icmp_cksum 3066145522Sdarrenr */ 3067145522Sdarrenr sum1 = ntohs(orgicmp->icmp_id); 3068145522Sdarrenr sum2 = ntohs(nat->nat_inport); 3069145522Sdarrenr CALC_SUMD(sum1, sum2, sumd); 3070145522Sdarrenr orgicmp->icmp_id = nat->nat_inport; 3071145522Sdarrenr fix_datacksum(&orgicmp->icmp_cksum, sumd); 3072145522Sdarrenr } 3073145522Sdarrenr } /* nat_dir == NAT_INBOUND is impossible for icmp queries */ 307453642Sguido } 307553642Sguido return nat; 307653642Sguido} 307753642Sguido 307853642Sguido 307953642Sguido/* 3080145522Sdarrenr * NB: these lookups don't lock access to the list, it assumed that it has 3081145522Sdarrenr * already been done! 308253642Sguido */ 3083145522Sdarrenr 3084145522Sdarrenr/* ------------------------------------------------------------------------ */ 3085145522Sdarrenr/* Function: nat_inlookup */ 3086145522Sdarrenr/* Returns: nat_t* - NULL == no match, */ 3087145522Sdarrenr/* else pointer to matching NAT entry */ 3088145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3089145522Sdarrenr/* flags(I) - NAT flags for this packet */ 3090145522Sdarrenr/* p(I) - protocol for this packet */ 3091145522Sdarrenr/* src(I) - source IP address */ 3092145522Sdarrenr/* mapdst(I) - destination IP address */ 3093145522Sdarrenr/* */ 3094145522Sdarrenr/* Lookup a nat entry based on the mapped destination ip address/port and */ 3095145522Sdarrenr/* real source address/port. We use this lookup when receiving a packet, */ 3096145522Sdarrenr/* we're looking for a table entry, based on the destination address. */ 3097145522Sdarrenr/* */ 3098145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 3099145522Sdarrenr/* */ 3100145522Sdarrenr/* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */ 3101145522Sdarrenr/* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 3102145522Sdarrenr/* */ 3103145522Sdarrenr/* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 3104145522Sdarrenr/* the packet is of said protocol */ 3105145522Sdarrenr/* ------------------------------------------------------------------------ */ 3106145522Sdarrenrnat_t *nat_inlookup(fin, flags, p, src, mapdst) 310792685Sdarrenrfr_info_t *fin; 3108145522Sdarrenru_int flags, p; 310953642Sguidostruct in_addr src , mapdst; 311053642Sguido{ 3111145522Sdarrenr u_short sport, dport; 3112145522Sdarrenr grehdr_t *gre; 311392685Sdarrenr ipnat_t *ipn; 3114145522Sdarrenr u_int sflags; 3115145522Sdarrenr nat_t *nat; 3116145522Sdarrenr int nflags; 3117145522Sdarrenr u_32_t dst; 311892685Sdarrenr void *ifp; 311953642Sguido u_int hv; 312053642Sguido 3121161356Sguido ifp = fin->fin_ifp; 3122145522Sdarrenr sport = 0; 3123145522Sdarrenr dport = 0; 3124145522Sdarrenr gre = NULL; 312567614Sdarrenr dst = mapdst.s_addr; 3126145522Sdarrenr sflags = flags & NAT_TCPUDPICMP; 3127145522Sdarrenr 3128145522Sdarrenr switch (p) 3129145522Sdarrenr { 3130145522Sdarrenr case IPPROTO_TCP : 3131145522Sdarrenr case IPPROTO_UDP : 313292685Sdarrenr sport = htons(fin->fin_data[0]); 313392685Sdarrenr dport = htons(fin->fin_data[1]); 3134145522Sdarrenr break; 3135145522Sdarrenr case IPPROTO_ICMP : 3136145522Sdarrenr if (flags & IPN_ICMPERR) 3137145522Sdarrenr sport = fin->fin_data[1]; 3138145522Sdarrenr else 3139145522Sdarrenr dport = fin->fin_data[1]; 3140145522Sdarrenr break; 3141145522Sdarrenr default : 3142145522Sdarrenr break; 314392685Sdarrenr } 314453642Sguido 3145145522Sdarrenr 3146145522Sdarrenr if ((flags & SI_WILDP) != 0) 3147145522Sdarrenr goto find_in_wild_ports; 3148145522Sdarrenr 314980482Sdarrenr hv = NAT_HASH_FN(dst, dport, 0xffffffff); 315080482Sdarrenr hv = NAT_HASH_FN(src.s_addr, hv + sport, ipf_nattable_sz); 315153642Sguido nat = nat_table[1][hv]; 315253642Sguido for (; nat; nat = nat->nat_hnext[1]) { 3153161356Sguido if (nat->nat_ifps[0] != NULL) { 3154161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 3155161356Sguido continue; 3156161356Sguido } else if (ifp != NULL) 3157161356Sguido nat->nat_ifps[0] = ifp; 3158161356Sguido 315953642Sguido nflags = nat->nat_flags; 3160145522Sdarrenr 3161145522Sdarrenr if (nat->nat_oip.s_addr == src.s_addr && 316267614Sdarrenr nat->nat_outip.s_addr == dst && 3163145522Sdarrenr (((p == 0) && 3164145522Sdarrenr (sflags == (nat->nat_flags & IPN_TCPUDPICMP))) 3165145522Sdarrenr || (p == nat->nat_p))) { 316692685Sdarrenr switch (p) 316792685Sdarrenr { 3168145522Sdarrenr#if 0 3169145522Sdarrenr case IPPROTO_GRE : 3170145522Sdarrenr if (nat->nat_call[1] != fin->fin_data[0]) 3171145522Sdarrenr continue; 3172145522Sdarrenr break; 3173145522Sdarrenr#endif 3174145522Sdarrenr case IPPROTO_ICMP : 3175145522Sdarrenr if ((flags & IPN_ICMPERR) != 0) { 3176145522Sdarrenr if (nat->nat_outport != sport) 3177145522Sdarrenr continue; 3178145522Sdarrenr } else { 3179145522Sdarrenr if (nat->nat_outport != dport) 3180145522Sdarrenr continue; 3181145522Sdarrenr } 3182145522Sdarrenr break; 318392685Sdarrenr case IPPROTO_TCP : 318492685Sdarrenr case IPPROTO_UDP : 318592685Sdarrenr if (nat->nat_oport != sport) 318692685Sdarrenr continue; 318792685Sdarrenr if (nat->nat_outport != dport) 318892685Sdarrenr continue; 318992685Sdarrenr break; 319092685Sdarrenr default : 319192685Sdarrenr break; 319292685Sdarrenr } 319392685Sdarrenr 319492685Sdarrenr ipn = nat->nat_ptr; 319592685Sdarrenr if ((ipn != NULL) && (nat->nat_aps != NULL)) 319692685Sdarrenr if (appr_match(fin, nat) != 0) 319792685Sdarrenr continue; 319853642Sguido return nat; 319992685Sdarrenr } 320053642Sguido } 3201145522Sdarrenr 3202145522Sdarrenr /* 3203145522Sdarrenr * So if we didn't find it but there are wildcard members in the hash 3204145522Sdarrenr * table, go back and look for them. We do this search and update here 3205145522Sdarrenr * because it is modifying the NAT table and we want to do this only 3206145522Sdarrenr * for the first packet that matches. The exception, of course, is 3207145522Sdarrenr * for "dummy" (FI_IGNORE) lookups. 3208145522Sdarrenr */ 3209145522Sdarrenrfind_in_wild_ports: 3210145522Sdarrenr if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) 321167614Sdarrenr return NULL; 3212145522Sdarrenr if (nat_stats.ns_wilds == 0) 3213145522Sdarrenr return NULL; 3214145522Sdarrenr 3215145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 3216145522Sdarrenr 321780482Sdarrenr hv = NAT_HASH_FN(dst, 0, 0xffffffff); 3218145522Sdarrenr hv = NAT_HASH_FN(src.s_addr, hv, ipf_nattable_sz); 3219145522Sdarrenr 3220145522Sdarrenr WRITE_ENTER(&ipf_nat); 3221145522Sdarrenr 322267614Sdarrenr nat = nat_table[1][hv]; 322367614Sdarrenr for (; nat; nat = nat->nat_hnext[1]) { 3224161356Sguido if (nat->nat_ifps[0] != NULL) { 3225161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 3226161356Sguido continue; 3227161356Sguido } else if (ifp != NULL) 3228161356Sguido nat->nat_ifps[0] = ifp; 3229145522Sdarrenr 3230145522Sdarrenr if (nat->nat_p != fin->fin_p) 323167614Sdarrenr continue; 323267614Sdarrenr if (nat->nat_oip.s_addr != src.s_addr || 323367614Sdarrenr nat->nat_outip.s_addr != dst) 323467614Sdarrenr continue; 3235145522Sdarrenr 3236145522Sdarrenr nflags = nat->nat_flags; 3237145522Sdarrenr if (!(nflags & (NAT_TCPUDP|SI_WILDP))) 3238145522Sdarrenr continue; 3239145522Sdarrenr 3240145522Sdarrenr if (nat_wildok(nat, (int)sport, (int)dport, nflags, 3241145522Sdarrenr NAT_INBOUND) == 1) { 3242145522Sdarrenr if ((fin->fin_flx & FI_IGNORE) != 0) 3243145522Sdarrenr break; 3244145522Sdarrenr if ((nflags & SI_CLONE) != 0) { 3245145522Sdarrenr nat = fr_natclone(fin, nat); 3246145522Sdarrenr if (nat == NULL) 3247145522Sdarrenr break; 3248145522Sdarrenr } else { 3249145522Sdarrenr MUTEX_ENTER(&ipf_nat_new); 3250145522Sdarrenr nat_stats.ns_wilds--; 3251145522Sdarrenr MUTEX_EXIT(&ipf_nat_new); 3252145522Sdarrenr } 3253145522Sdarrenr nat->nat_oport = sport; 3254145522Sdarrenr nat->nat_outport = dport; 3255145522Sdarrenr nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 3256145522Sdarrenr nat_tabmove(nat); 325767614Sdarrenr break; 325867614Sdarrenr } 325967614Sdarrenr } 3260145522Sdarrenr 3261145522Sdarrenr MUTEX_DOWNGRADE(&ipf_nat); 3262145522Sdarrenr 326367614Sdarrenr return nat; 326453642Sguido} 326553642Sguido 326653642Sguido 3267145522Sdarrenr/* ------------------------------------------------------------------------ */ 3268145522Sdarrenr/* Function: nat_tabmove */ 3269145522Sdarrenr/* Returns: Nil */ 3270145522Sdarrenr/* Parameters: nat(I) - pointer to NAT structure */ 3271145522Sdarrenr/* Write Lock: ipf_nat */ 3272145522Sdarrenr/* */ 3273145522Sdarrenr/* This function is only called for TCP/UDP NAT table entries where the */ 3274145522Sdarrenr/* original was placed in the table without hashing on the ports and we now */ 3275145522Sdarrenr/* want to include hashing on port numbers. */ 3276145522Sdarrenr/* ------------------------------------------------------------------------ */ 3277145522Sdarrenrstatic void nat_tabmove(nat) 327867614Sdarrenrnat_t *nat; 327967614Sdarrenr{ 328067614Sdarrenr nat_t **natp; 3281145522Sdarrenr u_int hv; 328267614Sdarrenr 3283145522Sdarrenr if (nat->nat_flags & SI_CLONE) 3284145522Sdarrenr return; 328572006Sdarrenr 328667614Sdarrenr /* 328767614Sdarrenr * Remove the NAT entry from the old location 328867614Sdarrenr */ 328967614Sdarrenr if (nat->nat_hnext[0]) 329067614Sdarrenr nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 329167614Sdarrenr *nat->nat_phnext[0] = nat->nat_hnext[0]; 3292145522Sdarrenr nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--; 329367614Sdarrenr 329467614Sdarrenr if (nat->nat_hnext[1]) 329567853Sdarrenr nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 329667614Sdarrenr *nat->nat_phnext[1] = nat->nat_hnext[1]; 3297145522Sdarrenr nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--; 329867614Sdarrenr 329967853Sdarrenr /* 330067853Sdarrenr * Add into the NAT table in the new position 330167853Sdarrenr */ 3302145522Sdarrenr hv = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 0xffffffff); 3303145522Sdarrenr hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport, 3304145522Sdarrenr ipf_nattable_sz); 3305145522Sdarrenr nat->nat_hv[0] = hv; 330667614Sdarrenr natp = &nat_table[0][hv]; 330767614Sdarrenr if (*natp) 330867614Sdarrenr (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 330967614Sdarrenr nat->nat_phnext[0] = natp; 331067614Sdarrenr nat->nat_hnext[0] = *natp; 331167614Sdarrenr *natp = nat; 3312145522Sdarrenr nat_stats.ns_bucketlen[0][hv]++; 331367614Sdarrenr 3314145522Sdarrenr hv = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, 0xffffffff); 3315145522Sdarrenr hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport, 3316145522Sdarrenr ipf_nattable_sz); 3317145522Sdarrenr nat->nat_hv[1] = hv; 331867614Sdarrenr natp = &nat_table[1][hv]; 331967614Sdarrenr if (*natp) 332067614Sdarrenr (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 332167614Sdarrenr nat->nat_phnext[1] = natp; 332267614Sdarrenr nat->nat_hnext[1] = *natp; 332367614Sdarrenr *natp = nat; 3324145522Sdarrenr nat_stats.ns_bucketlen[1][hv]++; 332567614Sdarrenr} 332667614Sdarrenr 332767614Sdarrenr 3328145522Sdarrenr/* ------------------------------------------------------------------------ */ 3329145522Sdarrenr/* Function: nat_outlookup */ 3330145522Sdarrenr/* Returns: nat_t* - NULL == no match, */ 3331145522Sdarrenr/* else pointer to matching NAT entry */ 3332145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3333145522Sdarrenr/* flags(I) - NAT flags for this packet */ 3334145522Sdarrenr/* p(I) - protocol for this packet */ 3335145522Sdarrenr/* src(I) - source IP address */ 3336145522Sdarrenr/* dst(I) - destination IP address */ 3337145522Sdarrenr/* rw(I) - 1 == write lock on ipf_nat held, 0 == read lock. */ 3338145522Sdarrenr/* */ 3339145522Sdarrenr/* Lookup a nat entry based on the source 'real' ip address/port and */ 3340145522Sdarrenr/* destination address/port. We use this lookup when sending a packet out, */ 3341145522Sdarrenr/* we're looking for a table entry, based on the source address. */ 3342145522Sdarrenr/* */ 3343145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 3344145522Sdarrenr/* */ 3345145522Sdarrenr/* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */ 3346145522Sdarrenr/* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 3347145522Sdarrenr/* */ 3348145522Sdarrenr/* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 3349145522Sdarrenr/* the packet is of said protocol */ 3350145522Sdarrenr/* ------------------------------------------------------------------------ */ 3351145522Sdarrenrnat_t *nat_outlookup(fin, flags, p, src, dst) 335292685Sdarrenrfr_info_t *fin; 3353145522Sdarrenru_int flags, p; 335453642Sguidostruct in_addr src , dst; 335553642Sguido{ 3356145522Sdarrenr u_short sport, dport; 3357145522Sdarrenr u_int sflags; 335892685Sdarrenr ipnat_t *ipn; 335967614Sdarrenr u_32_t srcip; 3360145522Sdarrenr nat_t *nat; 3361145522Sdarrenr int nflags; 336292685Sdarrenr void *ifp; 336353642Sguido u_int hv; 336453642Sguido 336592685Sdarrenr ifp = fin->fin_ifp; 336667614Sdarrenr srcip = src.s_addr; 3367145522Sdarrenr sflags = flags & IPN_TCPUDPICMP; 3368145522Sdarrenr sport = 0; 3369145522Sdarrenr dport = 0; 3370145522Sdarrenr 3371145522Sdarrenr switch (p) 3372145522Sdarrenr { 3373145522Sdarrenr case IPPROTO_TCP : 3374145522Sdarrenr case IPPROTO_UDP : 3375145522Sdarrenr sport = htons(fin->fin_data[0]); 3376145522Sdarrenr dport = htons(fin->fin_data[1]); 3377145522Sdarrenr break; 3378145522Sdarrenr case IPPROTO_ICMP : 3379145522Sdarrenr if (flags & IPN_ICMPERR) 3380145522Sdarrenr sport = fin->fin_data[1]; 3381145522Sdarrenr else 3382145522Sdarrenr dport = fin->fin_data[1]; 3383145522Sdarrenr break; 3384145522Sdarrenr default : 3385145522Sdarrenr break; 338692685Sdarrenr } 338753642Sguido 3388145522Sdarrenr if ((flags & SI_WILDP) != 0) 3389145522Sdarrenr goto find_out_wild_ports; 3390145522Sdarrenr 339180482Sdarrenr hv = NAT_HASH_FN(srcip, sport, 0xffffffff); 339280482Sdarrenr hv = NAT_HASH_FN(dst.s_addr, hv + dport, ipf_nattable_sz); 339353642Sguido nat = nat_table[0][hv]; 339453642Sguido for (; nat; nat = nat->nat_hnext[0]) { 3395161356Sguido if (nat->nat_ifps[1] != NULL) { 3396161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 3397161356Sguido continue; 3398161356Sguido } else if (ifp != NULL) 3399161356Sguido nat->nat_ifps[1] = ifp; 3400161356Sguido 340153642Sguido nflags = nat->nat_flags; 340253642Sguido 3403145522Sdarrenr if (nat->nat_inip.s_addr == srcip && 340453642Sguido nat->nat_oip.s_addr == dst.s_addr && 3405145522Sdarrenr (((p == 0) && (sflags == (nflags & NAT_TCPUDPICMP))) 3406145522Sdarrenr || (p == nat->nat_p))) { 340792685Sdarrenr switch (p) 340892685Sdarrenr { 3409145522Sdarrenr#if 0 3410145522Sdarrenr case IPPROTO_GRE : 3411145522Sdarrenr if (nat->nat_call[1] != fin->fin_data[0]) 3412145522Sdarrenr continue; 3413145522Sdarrenr break; 3414145522Sdarrenr#endif 341592685Sdarrenr case IPPROTO_TCP : 341692685Sdarrenr case IPPROTO_UDP : 341792685Sdarrenr if (nat->nat_oport != dport) 341892685Sdarrenr continue; 341992685Sdarrenr if (nat->nat_inport != sport) 342092685Sdarrenr continue; 342192685Sdarrenr break; 342292685Sdarrenr default : 342392685Sdarrenr break; 342492685Sdarrenr } 342592685Sdarrenr 342692685Sdarrenr ipn = nat->nat_ptr; 342792685Sdarrenr if ((ipn != NULL) && (nat->nat_aps != NULL)) 342892685Sdarrenr if (appr_match(fin, nat) != 0) 342992685Sdarrenr continue; 343053642Sguido return nat; 343192685Sdarrenr } 343253642Sguido } 3433145522Sdarrenr 3434145522Sdarrenr /* 3435145522Sdarrenr * So if we didn't find it but there are wildcard members in the hash 3436145522Sdarrenr * table, go back and look for them. We do this search and update here 3437145522Sdarrenr * because it is modifying the NAT table and we want to do this only 3438145522Sdarrenr * for the first packet that matches. The exception, of course, is 3439145522Sdarrenr * for "dummy" (FI_IGNORE) lookups. 3440145522Sdarrenr */ 3441145522Sdarrenrfind_out_wild_ports: 3442145522Sdarrenr if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) 344367614Sdarrenr return NULL; 3444145522Sdarrenr if (nat_stats.ns_wilds == 0) 3445145522Sdarrenr return NULL; 344692685Sdarrenr 3447145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 3448145522Sdarrenr 3449145522Sdarrenr hv = NAT_HASH_FN(srcip, 0, 0xffffffff); 3450145522Sdarrenr hv = NAT_HASH_FN(dst.s_addr, hv, ipf_nattable_sz); 3451145522Sdarrenr 3452145522Sdarrenr WRITE_ENTER(&ipf_nat); 3453145522Sdarrenr 345467614Sdarrenr nat = nat_table[0][hv]; 345567614Sdarrenr for (; nat; nat = nat->nat_hnext[0]) { 3456161356Sguido if (nat->nat_ifps[1] != NULL) { 3457161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 3458161356Sguido continue; 3459161356Sguido } else if (ifp != NULL) 3460161356Sguido nat->nat_ifps[1] = ifp; 3461145522Sdarrenr 3462145522Sdarrenr if (nat->nat_p != fin->fin_p) 346367614Sdarrenr continue; 346467614Sdarrenr if ((nat->nat_inip.s_addr != srcip) || 346567614Sdarrenr (nat->nat_oip.s_addr != dst.s_addr)) 346667614Sdarrenr continue; 3467145522Sdarrenr 3468145522Sdarrenr nflags = nat->nat_flags; 3469145522Sdarrenr if (!(nflags & (NAT_TCPUDP|SI_WILDP))) 3470145522Sdarrenr continue; 3471145522Sdarrenr 3472145522Sdarrenr if (nat_wildok(nat, (int)sport, (int)dport, nflags, 3473145522Sdarrenr NAT_OUTBOUND) == 1) { 3474145522Sdarrenr if ((fin->fin_flx & FI_IGNORE) != 0) 3475145522Sdarrenr break; 3476145522Sdarrenr if ((nflags & SI_CLONE) != 0) { 3477145522Sdarrenr nat = fr_natclone(fin, nat); 3478145522Sdarrenr if (nat == NULL) 3479145522Sdarrenr break; 3480145522Sdarrenr } else { 3481145522Sdarrenr MUTEX_ENTER(&ipf_nat_new); 3482145522Sdarrenr nat_stats.ns_wilds--; 3483145522Sdarrenr MUTEX_EXIT(&ipf_nat_new); 3484145522Sdarrenr } 3485145522Sdarrenr nat->nat_inport = sport; 3486145522Sdarrenr nat->nat_oport = dport; 3487145522Sdarrenr if (nat->nat_outport == 0) 3488145522Sdarrenr nat->nat_outport = sport; 3489145522Sdarrenr nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 3490145522Sdarrenr nat_tabmove(nat); 349167614Sdarrenr break; 349267614Sdarrenr } 349367614Sdarrenr } 3494145522Sdarrenr 3495145522Sdarrenr MUTEX_DOWNGRADE(&ipf_nat); 3496145522Sdarrenr 349767614Sdarrenr return nat; 349853642Sguido} 349953642Sguido 350053642Sguido 3501145522Sdarrenr/* ------------------------------------------------------------------------ */ 3502145522Sdarrenr/* Function: nat_lookupredir */ 3503145522Sdarrenr/* Returns: nat_t* - NULL == no match, */ 3504145522Sdarrenr/* else pointer to matching NAT entry */ 3505145522Sdarrenr/* Parameters: np(I) - pointer to description of packet to find NAT table */ 3506145522Sdarrenr/* entry for. */ 3507145522Sdarrenr/* */ 3508145522Sdarrenr/* Lookup the NAT tables to search for a matching redirect */ 3509161356Sguido/* The contents of natlookup_t should imitate those found in a packet that */ 3510161356Sguido/* would be translated - ie a packet coming in for RDR or going out for MAP.*/ 3511161356Sguido/* We can do the lookup in one of two ways, imitating an inbound or */ 3512161356Sguido/* outbound packet. By default we assume outbound, unless IPN_IN is set. */ 3513161356Sguido/* For IN, the fields are set as follows: */ 3514161356Sguido/* nl_real* = source information */ 3515161356Sguido/* nl_out* = destination information (translated) */ 3516161356Sguido/* For an out packet, the fields are set like this: */ 3517161356Sguido/* nl_in* = source information (untranslated) */ 3518161356Sguido/* nl_out* = destination information (translated) */ 3519145522Sdarrenr/* ------------------------------------------------------------------------ */ 352053642Sguidonat_t *nat_lookupredir(np) 3521145522Sdarrenrnatlookup_t *np; 352253642Sguido{ 3523145522Sdarrenr fr_info_t fi; 352453642Sguido nat_t *nat; 352553642Sguido 352692685Sdarrenr bzero((char *)&fi, sizeof(fi)); 3527145522Sdarrenr if (np->nl_flags & IPN_IN) { 3528145522Sdarrenr fi.fin_data[0] = ntohs(np->nl_realport); 3529145522Sdarrenr fi.fin_data[1] = ntohs(np->nl_outport); 3530145522Sdarrenr } else { 3531145522Sdarrenr fi.fin_data[0] = ntohs(np->nl_inport); 3532145522Sdarrenr fi.fin_data[1] = ntohs(np->nl_outport); 3533145522Sdarrenr } 3534145522Sdarrenr if (np->nl_flags & IPN_TCP) 3535145522Sdarrenr fi.fin_p = IPPROTO_TCP; 3536145522Sdarrenr else if (np->nl_flags & IPN_UDP) 3537145522Sdarrenr fi.fin_p = IPPROTO_UDP; 3538145522Sdarrenr else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) 3539145522Sdarrenr fi.fin_p = IPPROTO_ICMP; 354092685Sdarrenr 354153642Sguido /* 3542145522Sdarrenr * We can do two sorts of lookups: 3543145522Sdarrenr * - IPN_IN: we have the `real' and `out' address, look for `in'. 3544145522Sdarrenr * - default: we have the `in' and `out' address, look for `real'. 354553642Sguido */ 3546145522Sdarrenr if (np->nl_flags & IPN_IN) { 3547145522Sdarrenr if ((nat = nat_inlookup(&fi, np->nl_flags, fi.fin_p, 3548145522Sdarrenr np->nl_realip, np->nl_outip))) { 3549145522Sdarrenr np->nl_inip = nat->nat_inip; 3550145522Sdarrenr np->nl_inport = nat->nat_inport; 3551145522Sdarrenr } 3552145522Sdarrenr } else { 3553145522Sdarrenr /* 3554145522Sdarrenr * If nl_inip is non null, this is a lookup based on the real 3555145522Sdarrenr * ip address. Else, we use the fake. 3556145522Sdarrenr */ 3557145522Sdarrenr if ((nat = nat_outlookup(&fi, np->nl_flags, fi.fin_p, 3558145522Sdarrenr np->nl_inip, np->nl_outip))) { 3559145522Sdarrenr 3560145522Sdarrenr if ((np->nl_flags & IPN_FINDFORWARD) != 0) { 3561145522Sdarrenr fr_info_t fin; 3562145522Sdarrenr bzero((char *)&fin, sizeof(fin)); 3563145522Sdarrenr fin.fin_p = nat->nat_p; 3564145522Sdarrenr fin.fin_data[0] = ntohs(nat->nat_outport); 3565145522Sdarrenr fin.fin_data[1] = ntohs(nat->nat_oport); 3566145522Sdarrenr if (nat_inlookup(&fin, np->nl_flags, fin.fin_p, 3567145522Sdarrenr nat->nat_outip, 3568145522Sdarrenr nat->nat_oip) != NULL) { 3569145522Sdarrenr np->nl_flags &= ~IPN_FINDFORWARD; 3570145522Sdarrenr } 3571145522Sdarrenr } 3572145522Sdarrenr 3573145522Sdarrenr np->nl_realip = nat->nat_outip; 3574145522Sdarrenr np->nl_realport = nat->nat_outport; 3575145522Sdarrenr } 3576145522Sdarrenr } 3577145522Sdarrenr 357853642Sguido return nat; 357953642Sguido} 358053642Sguido 358153642Sguido 3582145522Sdarrenr/* ------------------------------------------------------------------------ */ 3583145522Sdarrenr/* Function: nat_match */ 3584145522Sdarrenr/* Returns: int - 0 == no match, 1 == match */ 3585145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3586145522Sdarrenr/* np(I) - pointer to NAT rule */ 3587145522Sdarrenr/* */ 3588145522Sdarrenr/* Pull the matching of a packet against a NAT rule out of that complex */ 3589145522Sdarrenr/* loop inside fr_checknatin() and lay it out properly in its own function. */ 3590145522Sdarrenr/* ------------------------------------------------------------------------ */ 3591145522Sdarrenrstatic int nat_match(fin, np) 359260852Sdarrenrfr_info_t *fin; 359360852Sdarrenripnat_t *np; 359460852Sdarrenr{ 359560852Sdarrenr frtuc_t *ft; 359660852Sdarrenr 3597145522Sdarrenr if (fin->fin_v != 4) 359860852Sdarrenr return 0; 359960852Sdarrenr 360092685Sdarrenr if (np->in_p && fin->fin_p != np->in_p) 360160852Sdarrenr return 0; 3602145522Sdarrenr 360360852Sdarrenr if (fin->fin_out) { 360463523Sdarrenr if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK))) 360560852Sdarrenr return 0; 360663523Sdarrenr if (((fin->fin_fi.fi_saddr & np->in_inmsk) != np->in_inip) 360763523Sdarrenr ^ ((np->in_flags & IPN_NOTSRC) != 0)) 360860852Sdarrenr return 0; 360963523Sdarrenr if (((fin->fin_fi.fi_daddr & np->in_srcmsk) != np->in_srcip) 361063523Sdarrenr ^ ((np->in_flags & IPN_NOTDST) != 0)) 361160852Sdarrenr return 0; 361260852Sdarrenr } else { 361363523Sdarrenr if (!(np->in_redir & NAT_REDIRECT)) 361460852Sdarrenr return 0; 361563523Sdarrenr if (((fin->fin_fi.fi_saddr & np->in_srcmsk) != np->in_srcip) 361663523Sdarrenr ^ ((np->in_flags & IPN_NOTSRC) != 0)) 361763523Sdarrenr return 0; 361863523Sdarrenr if (((fin->fin_fi.fi_daddr & np->in_outmsk) != np->in_outip) 361963523Sdarrenr ^ ((np->in_flags & IPN_NOTDST) != 0)) 362063523Sdarrenr return 0; 362160852Sdarrenr } 362260852Sdarrenr 362360852Sdarrenr ft = &np->in_tuc; 3624145522Sdarrenr if (!(fin->fin_flx & FI_TCPUDP) || 3625145522Sdarrenr (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { 362660852Sdarrenr if (ft->ftu_scmp || ft->ftu_dcmp) 362760852Sdarrenr return 0; 362860852Sdarrenr return 1; 362960852Sdarrenr } 363060852Sdarrenr 3631145522Sdarrenr return fr_tcpudpchk(fin, ft); 363260852Sdarrenr} 363360852Sdarrenr 363460852Sdarrenr 3635145522Sdarrenr/* ------------------------------------------------------------------------ */ 3636145522Sdarrenr/* Function: nat_update */ 3637145522Sdarrenr/* Returns: Nil */ 3638145522Sdarrenr/* Parameters: nat(I) - pointer to NAT structure */ 3639145522Sdarrenr/* np(I) - pointer to NAT rule */ 3640145522Sdarrenr/* */ 3641145522Sdarrenr/* Updates the lifetime of a NAT table entry for non-TCP packets. Must be */ 3642145522Sdarrenr/* called with fin_rev updated - i.e. after calling nat_proto(). */ 3643145522Sdarrenr/* ------------------------------------------------------------------------ */ 3644145522Sdarrenrvoid nat_update(fin, nat, np) 364553642Sguidofr_info_t *fin; 3646145522Sdarrenrnat_t *nat; 3647145522Sdarrenripnat_t *np; 364853642Sguido{ 3649145522Sdarrenr ipftq_t *ifq, *ifq2; 3650145522Sdarrenr ipftqent_t *tqe; 3651145522Sdarrenr 3652145522Sdarrenr MUTEX_ENTER(&nat->nat_lock); 3653145522Sdarrenr tqe = &nat->nat_tqe; 3654145522Sdarrenr ifq = tqe->tqe_ifq; 3655145522Sdarrenr 3656145522Sdarrenr /* 3657145522Sdarrenr * We allow over-riding of NAT timeouts from NAT rules, even for 3658145522Sdarrenr * TCP, however, if it is TCP and there is no rule timeout set, 3659145522Sdarrenr * then do not update the timeout here. 3660145522Sdarrenr */ 3661145522Sdarrenr if (np != NULL) 3662145522Sdarrenr ifq2 = np->in_tqehead[fin->fin_rev]; 3663145522Sdarrenr else 3664145522Sdarrenr ifq2 = NULL; 3665145522Sdarrenr 3666145522Sdarrenr if (nat->nat_p == IPPROTO_TCP && ifq2 == NULL) { 3667172776Sdarrenr u_32_t end, ack; 3668172776Sdarrenr u_char tcpflags; 3669172776Sdarrenr tcphdr_t *tcp; 3670172776Sdarrenr int dsize; 3671172776Sdarrenr 3672172776Sdarrenr tcp = fin->fin_dp; 3673172776Sdarrenr tcpflags = tcp->th_flags; 3674172776Sdarrenr dsize = fin->fin_dlen - (TCP_OFF(tcp) << 2) + 3675172776Sdarrenr ((tcpflags & TH_SYN) ? 1 : 0) + 3676172776Sdarrenr ((tcpflags & TH_FIN) ? 1 : 0); 3677172776Sdarrenr 3678172776Sdarrenr ack = ntohl(tcp->th_ack); 3679172776Sdarrenr end = ntohl(tcp->th_seq) + dsize; 3680172776Sdarrenr 3681172776Sdarrenr if (SEQ_GT(ack, nat->nat_seqnext[1 - fin->fin_rev])) 3682172776Sdarrenr nat->nat_seqnext[1 - fin->fin_rev] = ack; 3683172776Sdarrenr 3684172776Sdarrenr if (nat->nat_seqnext[fin->fin_rev] == 0) 3685172776Sdarrenr nat->nat_seqnext[fin->fin_rev] = end; 3686172776Sdarrenr 3687145522Sdarrenr (void) fr_tcp_age(&nat->nat_tqe, fin, nat_tqb, 0); 3688145522Sdarrenr } else { 3689145522Sdarrenr if (ifq2 == NULL) { 3690145522Sdarrenr if (nat->nat_p == IPPROTO_UDP) 3691145522Sdarrenr ifq2 = &nat_udptq; 3692145522Sdarrenr else if (nat->nat_p == IPPROTO_ICMP) 3693145522Sdarrenr ifq2 = &nat_icmptq; 3694145522Sdarrenr else 3695145522Sdarrenr ifq2 = &nat_iptq; 3696145522Sdarrenr } 3697145522Sdarrenr 3698145522Sdarrenr fr_movequeue(tqe, ifq, ifq2); 3699145522Sdarrenr } 3700145522Sdarrenr MUTEX_EXIT(&nat->nat_lock); 3701145522Sdarrenr} 3702145522Sdarrenr 3703145522Sdarrenr 3704145522Sdarrenr/* ------------------------------------------------------------------------ */ 3705145522Sdarrenr/* Function: fr_checknatout */ 3706145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 3707145522Sdarrenr/* 0 == no packet translation occurred, */ 3708145522Sdarrenr/* 1 == packet was successfully translated. */ 3709145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3710145522Sdarrenr/* passp(I) - pointer to filtering result flags */ 3711145522Sdarrenr/* */ 3712145522Sdarrenr/* Check to see if an outcoming packet should be changed. ICMP packets are */ 3713145522Sdarrenr/* first checked to see if they match an existing entry (if an error), */ 3714145522Sdarrenr/* otherwise a search of the current NAT table is made. If neither results */ 3715145522Sdarrenr/* in a match then a search for a matching NAT rule is made. Create a new */ 3716145522Sdarrenr/* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 3717145522Sdarrenr/* packet header(s) as required. */ 3718145522Sdarrenr/* ------------------------------------------------------------------------ */ 3719145522Sdarrenrint fr_checknatout(fin, passp) 3720145522Sdarrenrfr_info_t *fin; 3721145522Sdarrenru_32_t *passp; 3722145522Sdarrenr{ 3723145522Sdarrenr struct ifnet *ifp, *sifp; 3724145522Sdarrenr icmphdr_t *icmp = NULL; 372553642Sguido tcphdr_t *tcp = NULL; 3726145522Sdarrenr int rval, natfailed; 3727145522Sdarrenr ipnat_t *np = NULL; 3728145522Sdarrenr u_int nflags = 0; 3729145522Sdarrenr u_32_t ipa, iph; 3730145522Sdarrenr int natadd = 1; 373153642Sguido frentry_t *fr; 373253642Sguido nat_t *nat; 373353642Sguido 3734145522Sdarrenr if (nat_stats.ns_rules == 0 || fr_nat_lock != 0) 373553642Sguido return 0; 373653642Sguido 3737145522Sdarrenr natfailed = 0; 3738145522Sdarrenr fr = fin->fin_fr; 3739145522Sdarrenr sifp = fin->fin_ifp; 3740170268Sdarrenr if (fr != NULL) { 3741170268Sdarrenr ifp = fr->fr_tifs[fin->fin_rev].fd_ifp; 3742170268Sdarrenr if ((ifp != NULL) && (ifp != (void *)-1)) 3743170268Sdarrenr fin->fin_ifp = ifp; 3744170268Sdarrenr } 374592685Sdarrenr ifp = fin->fin_ifp; 374653642Sguido 3747145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 3748145522Sdarrenr switch (fin->fin_p) 3749145522Sdarrenr { 3750145522Sdarrenr case IPPROTO_TCP : 375153642Sguido nflags = IPN_TCP; 3752145522Sdarrenr break; 3753145522Sdarrenr case IPPROTO_UDP : 375453642Sguido nflags = IPN_UDP; 3755145522Sdarrenr break; 3756145522Sdarrenr case IPPROTO_ICMP : 3757145522Sdarrenr icmp = fin->fin_dp; 3758145522Sdarrenr 3759145522Sdarrenr /* 3760145522Sdarrenr * This is an incoming packet, so the destination is 3761145522Sdarrenr * the icmp_id and the source port equals 0 3762145522Sdarrenr */ 3763145522Sdarrenr if (nat_icmpquerytype4(icmp->icmp_type)) 3764145522Sdarrenr nflags = IPN_ICMPQUERY; 3765145522Sdarrenr break; 3766145522Sdarrenr default : 3767145522Sdarrenr break; 376853642Sguido } 3769145522Sdarrenr 3770145522Sdarrenr if ((nflags & IPN_TCPUDP)) 3771145522Sdarrenr tcp = fin->fin_dp; 377253642Sguido } 377353642Sguido 377492685Sdarrenr ipa = fin->fin_saddr; 377553642Sguido 377653642Sguido READ_ENTER(&ipf_nat); 377760852Sdarrenr 3778145522Sdarrenr if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && 3779145522Sdarrenr (nat = nat_icmperror(fin, &nflags, NAT_OUTBOUND))) 3780145522Sdarrenr /*EMPTY*/; 3781145522Sdarrenr else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin))) 378253642Sguido natadd = 0; 3783145522Sdarrenr else if ((nat = nat_outlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, 3784145522Sdarrenr fin->fin_src, fin->fin_dst))) { 378553642Sguido nflags = nat->nat_flags; 378653642Sguido } else { 3787145522Sdarrenr u_32_t hv, msk, nmsk; 378892685Sdarrenr 378953642Sguido /* 379053642Sguido * If there is no current entry in the nat table for this IP#, 379153642Sguido * create one for it (if there is a matching rule). 379253642Sguido */ 3793145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 3794145522Sdarrenr msk = 0xffffffff; 3795145522Sdarrenr nmsk = nat_masks; 3796145522Sdarrenr WRITE_ENTER(&ipf_nat); 379753642Sguidomaskloop: 379853642Sguido iph = ipa & htonl(msk); 379960852Sdarrenr hv = NAT_HASH_FN(iph, 0, ipf_natrules_sz); 380053642Sguido for (np = nat_rules[hv]; np; np = np->in_mnext) 380153642Sguido { 3802161356Sguido if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) 380360852Sdarrenr continue; 3804145522Sdarrenr if (np->in_v != fin->fin_v) 380560852Sdarrenr continue; 3806145522Sdarrenr if (np->in_p && (np->in_p != fin->fin_p)) 3807145522Sdarrenr continue; 3808145522Sdarrenr if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) 3809145522Sdarrenr continue; 381060852Sdarrenr if (np->in_flags & IPN_FILTER) { 3811145522Sdarrenr if (!nat_match(fin, np)) 381260852Sdarrenr continue; 381360852Sdarrenr } else if ((ipa & np->in_inmsk) != np->in_inip) 381460852Sdarrenr continue; 3815145522Sdarrenr 3816145522Sdarrenr if ((fr != NULL) && 3817145522Sdarrenr !fr_matchtag(&np->in_tag, &fr->fr_nattag)) 381892685Sdarrenr continue; 3819145522Sdarrenr 3820145522Sdarrenr if (*np->in_plabel != '\0') { 3821145522Sdarrenr if (((np->in_flags & IPN_FILTER) == 0) && 3822145522Sdarrenr (np->in_dport != tcp->th_dport)) 3823145522Sdarrenr continue; 3824145522Sdarrenr if (appr_ok(fin, tcp, np) == 0) 3825145522Sdarrenr continue; 3826145522Sdarrenr } 3827145522Sdarrenr 3828145522Sdarrenr if ((nat = nat_new(fin, np, NULL, nflags, 3829145522Sdarrenr NAT_OUTBOUND))) { 383092685Sdarrenr np->in_hits++; 383192685Sdarrenr break; 3832145522Sdarrenr } else 3833145522Sdarrenr natfailed = -1; 383453642Sguido } 3835145522Sdarrenr if ((np == NULL) && (nmsk != 0)) { 3836145522Sdarrenr while (nmsk) { 383753642Sguido msk <<= 1; 3838145522Sdarrenr if (nmsk & 0x80000000) 3839145522Sdarrenr break; 3840145522Sdarrenr nmsk <<= 1; 3841145522Sdarrenr } 3842145522Sdarrenr if (nmsk != 0) { 3843145522Sdarrenr nmsk <<= 1; 384453642Sguido goto maskloop; 3845145522Sdarrenr } 384653642Sguido } 384753642Sguido MUTEX_DOWNGRADE(&ipf_nat); 384853642Sguido } 384953642Sguido 3850145522Sdarrenr if (nat != NULL) { 3851145522Sdarrenr rval = fr_natout(fin, nat, natadd, nflags); 3852145522Sdarrenr if (rval == 1) { 3853145522Sdarrenr MUTEX_ENTER(&nat->nat_lock); 3854145522Sdarrenr nat->nat_ref++; 3855145522Sdarrenr MUTEX_EXIT(&nat->nat_lock); 3856170268Sdarrenr nat->nat_touched = fr_ticks; 3857145522Sdarrenr fin->fin_nat = nat; 3858145522Sdarrenr } 3859145522Sdarrenr } else 3860145522Sdarrenr rval = natfailed; 3861145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 3862145522Sdarrenr 3863145522Sdarrenr if (rval == -1) { 3864145522Sdarrenr if (passp != NULL) 3865145522Sdarrenr *passp = FR_BLOCK; 3866145522Sdarrenr fin->fin_flx |= FI_BADNAT; 3867145522Sdarrenr } 3868145522Sdarrenr fin->fin_ifp = sifp; 3869145522Sdarrenr return rval; 3870145522Sdarrenr} 3871145522Sdarrenr 3872145522Sdarrenr/* ------------------------------------------------------------------------ */ 3873145522Sdarrenr/* Function: fr_natout */ 3874145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 3875145522Sdarrenr/* 1 == packet was successfully translated. */ 3876145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3877145522Sdarrenr/* nat(I) - pointer to NAT structure */ 3878145522Sdarrenr/* natadd(I) - flag indicating if it is safe to add frag cache */ 3879145522Sdarrenr/* nflags(I) - NAT flags set for this packet */ 3880145522Sdarrenr/* */ 3881145522Sdarrenr/* Translate a packet coming "out" on an interface. */ 3882145522Sdarrenr/* ------------------------------------------------------------------------ */ 3883145522Sdarrenrint fr_natout(fin, nat, natadd, nflags) 3884145522Sdarrenrfr_info_t *fin; 3885145522Sdarrenrnat_t *nat; 3886145522Sdarrenrint natadd; 3887145522Sdarrenru_32_t nflags; 3888145522Sdarrenr{ 3889145522Sdarrenr icmphdr_t *icmp; 3890145522Sdarrenr u_short *csump; 3891145522Sdarrenr tcphdr_t *tcp; 3892145522Sdarrenr ipnat_t *np; 3893145522Sdarrenr int i; 3894145522Sdarrenr 3895145522Sdarrenr tcp = NULL; 3896145522Sdarrenr icmp = NULL; 3897145522Sdarrenr csump = NULL; 3898145522Sdarrenr np = nat->nat_ptr; 3899145522Sdarrenr 3900145522Sdarrenr if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL)) 3901145522Sdarrenr (void) fr_nat_newfrag(fin, 0, nat); 3902145522Sdarrenr 3903145522Sdarrenr MUTEX_ENTER(&nat->nat_lock); 3904145522Sdarrenr nat->nat_bytes[1] += fin->fin_plen; 3905145522Sdarrenr nat->nat_pkts[1]++; 3906145522Sdarrenr MUTEX_EXIT(&nat->nat_lock); 3907145522Sdarrenr 390872006Sdarrenr /* 3909145522Sdarrenr * Fix up checksums, not by recalculating them, but 3910145522Sdarrenr * simply computing adjustments. 3911145522Sdarrenr * This is only done for STREAMS based IP implementations where the 3912145522Sdarrenr * checksum has already been calculated by IP. In all other cases, 3913145522Sdarrenr * IPFilter is called before the checksum needs calculating so there 3914145522Sdarrenr * is no call to modify whatever is in the header now. 391572006Sdarrenr */ 3916145522Sdarrenr if (fin->fin_v == 4) { 391763523Sdarrenr if (nflags == IPN_ICMPERR) { 391863523Sdarrenr u_32_t s1, s2, sumd; 391963523Sdarrenr 392092685Sdarrenr s1 = LONG_SUM(ntohl(fin->fin_saddr)); 392163523Sdarrenr s2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)); 392263523Sdarrenr CALC_SUMD(s1, s2, sumd); 3923145522Sdarrenr fix_outcksum(fin, &fin->fin_ip->ip_sum, sumd); 392463523Sdarrenr } 3925153876Sguido#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 3926153876Sguido defined(linux) || defined(BRIDGE_IPF) 392763523Sdarrenr else { 3928153876Sguido /* 3929153876Sguido * Strictly speaking, this isn't necessary on BSD 3930153876Sguido * kernels because they do checksum calculation after 3931153876Sguido * this code has run BUT if ipfilter is being used 3932153876Sguido * to do NAT as a bridge, that code doesn't exist. 3933153876Sguido */ 393463523Sdarrenr if (nat->nat_dir == NAT_OUTBOUND) 3935145522Sdarrenr fix_outcksum(fin, &fin->fin_ip->ip_sum, 3936145522Sdarrenr nat->nat_ipsumd); 393763523Sdarrenr else 3938145522Sdarrenr fix_incksum(fin, &fin->fin_ip->ip_sum, 3939145522Sdarrenr nat->nat_ipsumd); 394063523Sdarrenr } 394153642Sguido#endif 3942145522Sdarrenr } 394353642Sguido 3944145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 3945145522Sdarrenr if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) { 3946145522Sdarrenr tcp = fin->fin_dp; 394753642Sguido 3948145522Sdarrenr tcp->th_sport = nat->nat_outport; 3949145522Sdarrenr fin->fin_data[0] = ntohs(nat->nat_outport); 3950145522Sdarrenr } 395153642Sguido 3952145522Sdarrenr if ((nat->nat_outport != 0) && (nflags & IPN_ICMPQUERY)) { 3953145522Sdarrenr icmp = fin->fin_dp; 3954145522Sdarrenr icmp->icmp_id = nat->nat_outport; 3955145522Sdarrenr } 3956110916Sdarrenr 3957145522Sdarrenr csump = nat_proto(fin, nat, nflags); 3958145522Sdarrenr } 3959110916Sdarrenr 3960145522Sdarrenr fin->fin_ip->ip_src = nat->nat_outip; 396153642Sguido 3962145522Sdarrenr nat_update(fin, nat, np); 396363523Sdarrenr 3964145522Sdarrenr /* 3965145522Sdarrenr * The above comments do not hold for layer 4 (or higher) checksums... 3966145522Sdarrenr */ 3967145522Sdarrenr if (csump != NULL) { 3968145522Sdarrenr if (nat->nat_dir == NAT_OUTBOUND) 3969145522Sdarrenr fix_outcksum(fin, csump, nat->nat_sumd[1]); 3970145522Sdarrenr else 3971145522Sdarrenr fix_incksum(fin, csump, nat->nat_sumd[1]); 3972145522Sdarrenr } 3973145522Sdarrenr#ifdef IPFILTER_SYNC 3974145522Sdarrenr ipfsync_update(SMC_NAT, fin, nat->nat_sync); 3975145522Sdarrenr#endif 3976145522Sdarrenr /* ------------------------------------------------------------- */ 3977145522Sdarrenr /* A few quick notes: */ 3978145522Sdarrenr /* Following are test conditions prior to calling the */ 3979145522Sdarrenr /* appr_check routine. */ 3980145522Sdarrenr /* */ 3981145522Sdarrenr /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 3982145522Sdarrenr /* with a redirect rule, we attempt to match the packet's */ 3983145522Sdarrenr /* source port against in_dport, otherwise we'd compare the */ 3984145522Sdarrenr /* packet's destination. */ 3985145522Sdarrenr /* ------------------------------------------------------------- */ 3986145522Sdarrenr if ((np != NULL) && (np->in_apr != NULL)) { 3987145522Sdarrenr i = appr_check(fin, nat); 3988145522Sdarrenr if (i == 0) 398960852Sdarrenr i = 1; 3990145522Sdarrenr } else 3991145522Sdarrenr i = 1; 3992145522Sdarrenr ATOMIC_INCL(nat_stats.ns_mapped[1]); 3993145522Sdarrenr fin->fin_flx |= FI_NATED; 3994145522Sdarrenr return i; 399553642Sguido} 399653642Sguido 399753642Sguido 3998145522Sdarrenr/* ------------------------------------------------------------------------ */ 3999145522Sdarrenr/* Function: fr_checknatin */ 4000145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 4001145522Sdarrenr/* 0 == no packet translation occurred, */ 4002145522Sdarrenr/* 1 == packet was successfully translated. */ 4003145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4004145522Sdarrenr/* passp(I) - pointer to filtering result flags */ 4005145522Sdarrenr/* */ 4006145522Sdarrenr/* Check to see if an incoming packet should be changed. ICMP packets are */ 4007145522Sdarrenr/* first checked to see if they match an existing entry (if an error), */ 4008145522Sdarrenr/* otherwise a search of the current NAT table is made. If neither results */ 4009145522Sdarrenr/* in a match then a search for a matching NAT rule is made. Create a new */ 4010145522Sdarrenr/* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 4011145522Sdarrenr/* packet header(s) as required. */ 4012145522Sdarrenr/* ------------------------------------------------------------------------ */ 4013145522Sdarrenrint fr_checknatin(fin, passp) 401453642Sguidofr_info_t *fin; 4015145522Sdarrenru_32_t *passp; 401653642Sguido{ 4017145522Sdarrenr u_int nflags, natadd; 4018145522Sdarrenr int rval, natfailed; 4019145522Sdarrenr struct ifnet *ifp; 4020145522Sdarrenr struct in_addr in; 4021145522Sdarrenr icmphdr_t *icmp; 4022145522Sdarrenr tcphdr_t *tcp; 4023145522Sdarrenr u_short dport; 4024145522Sdarrenr ipnat_t *np; 402553642Sguido nat_t *nat; 402653642Sguido u_32_t iph; 402753642Sguido 4028145522Sdarrenr if (nat_stats.ns_rules == 0 || fr_nat_lock != 0) 402953642Sguido return 0; 403053642Sguido 4031145522Sdarrenr tcp = NULL; 4032145522Sdarrenr icmp = NULL; 4033145522Sdarrenr dport = 0; 4034145522Sdarrenr natadd = 1; 4035145522Sdarrenr nflags = 0; 4036145522Sdarrenr natfailed = 0; 4037145522Sdarrenr ifp = fin->fin_ifp; 4038145522Sdarrenr 4039145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 4040145522Sdarrenr switch (fin->fin_p) 4041145522Sdarrenr { 4042145522Sdarrenr case IPPROTO_TCP : 404353642Sguido nflags = IPN_TCP; 4044145522Sdarrenr break; 4045145522Sdarrenr case IPPROTO_UDP : 404653642Sguido nflags = IPN_UDP; 4047145522Sdarrenr break; 4048145522Sdarrenr case IPPROTO_ICMP : 4049145522Sdarrenr icmp = fin->fin_dp; 4050145522Sdarrenr 4051145522Sdarrenr /* 4052145522Sdarrenr * This is an incoming packet, so the destination is 4053145522Sdarrenr * the icmp_id and the source port equals 0 4054145522Sdarrenr */ 4055145522Sdarrenr if (nat_icmpquerytype4(icmp->icmp_type)) { 4056145522Sdarrenr nflags = IPN_ICMPQUERY; 4057145522Sdarrenr dport = icmp->icmp_id; 4058145522Sdarrenr } break; 4059145522Sdarrenr default : 4060145522Sdarrenr break; 4061145522Sdarrenr } 4062145522Sdarrenr 406353642Sguido if ((nflags & IPN_TCPUDP)) { 4064145522Sdarrenr tcp = fin->fin_dp; 406553642Sguido dport = tcp->th_dport; 406653642Sguido } 406753642Sguido } 406853642Sguido 406992685Sdarrenr in = fin->fin_dst; 407053642Sguido 407153642Sguido READ_ENTER(&ipf_nat); 407253642Sguido 4073145522Sdarrenr if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && 4074145522Sdarrenr (nat = nat_icmperror(fin, &nflags, NAT_INBOUND))) 4075145522Sdarrenr /*EMPTY*/; 4076145522Sdarrenr else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin))) 407753642Sguido natadd = 0; 4078145522Sdarrenr else if ((nat = nat_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, 4079145522Sdarrenr fin->fin_src, in))) { 408053642Sguido nflags = nat->nat_flags; 408153642Sguido } else { 4082145522Sdarrenr u_32_t hv, msk, rmsk; 4083145522Sdarrenr 408453642Sguido RWLOCK_EXIT(&ipf_nat); 4085145522Sdarrenr rmsk = rdr_masks; 408692685Sdarrenr msk = 0xffffffff; 408753642Sguido WRITE_ENTER(&ipf_nat); 408853642Sguido /* 408953642Sguido * If there is no current entry in the nat table for this IP#, 409053642Sguido * create one for it (if there is a matching rule). 409153642Sguido */ 409253642Sguidomaskloop: 409353642Sguido iph = in.s_addr & htonl(msk); 409460852Sdarrenr hv = NAT_HASH_FN(iph, 0, ipf_rdrrules_sz); 409560852Sdarrenr for (np = rdr_rules[hv]; np; np = np->in_rnext) { 4096145522Sdarrenr if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) 409760852Sdarrenr continue; 4098145522Sdarrenr if (np->in_v != fin->fin_v) 4099138947Sdarrenr continue; 4100145522Sdarrenr if (np->in_p && (np->in_p != fin->fin_p)) 4101145522Sdarrenr continue; 4102145522Sdarrenr if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) 4103145522Sdarrenr continue; 410460852Sdarrenr if (np->in_flags & IPN_FILTER) { 4105145522Sdarrenr if (!nat_match(fin, np)) 410660852Sdarrenr continue; 4107145522Sdarrenr } else { 4108145522Sdarrenr if ((in.s_addr & np->in_outmsk) != np->in_outip) 4109145522Sdarrenr continue; 4110145522Sdarrenr if (np->in_pmin && 4111145522Sdarrenr ((ntohs(np->in_pmax) < ntohs(dport)) || 4112145522Sdarrenr (ntohs(dport) < ntohs(np->in_pmin)))) 4113145522Sdarrenr continue; 4114145522Sdarrenr } 4115145522Sdarrenr 4116145522Sdarrenr if (*np->in_plabel != '\0') { 4117145522Sdarrenr if (!appr_ok(fin, tcp, np)) { 4118145522Sdarrenr continue; 411953642Sguido } 4120145522Sdarrenr } 4121145522Sdarrenr 4122145522Sdarrenr nat = nat_new(fin, np, NULL, nflags, NAT_INBOUND); 4123145522Sdarrenr if (nat != NULL) { 4124145522Sdarrenr np->in_hits++; 4125145522Sdarrenr break; 4126145522Sdarrenr } else 4127145522Sdarrenr natfailed = -1; 412860852Sdarrenr } 412960852Sdarrenr 4130145522Sdarrenr if ((np == NULL) && (rmsk != 0)) { 4131145522Sdarrenr while (rmsk) { 413253642Sguido msk <<= 1; 4133145522Sdarrenr if (rmsk & 0x80000000) 4134145522Sdarrenr break; 4135145522Sdarrenr rmsk <<= 1; 4136145522Sdarrenr } 4137145522Sdarrenr if (rmsk != 0) { 4138145522Sdarrenr rmsk <<= 1; 413953642Sguido goto maskloop; 4140145522Sdarrenr } 414153642Sguido } 414253642Sguido MUTEX_DOWNGRADE(&ipf_nat); 414353642Sguido } 4144145522Sdarrenr if (nat != NULL) { 4145145522Sdarrenr rval = fr_natin(fin, nat, natadd, nflags); 4146145522Sdarrenr if (rval == 1) { 4147145522Sdarrenr MUTEX_ENTER(&nat->nat_lock); 4148145522Sdarrenr nat->nat_ref++; 4149145522Sdarrenr MUTEX_EXIT(&nat->nat_lock); 4150170268Sdarrenr nat->nat_touched = fr_ticks; 4151145522Sdarrenr fin->fin_nat = nat; 4152145522Sdarrenr } 4153145522Sdarrenr } else 4154145522Sdarrenr rval = natfailed; 4155145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 415672006Sdarrenr 4157145522Sdarrenr if (rval == -1) { 4158145522Sdarrenr if (passp != NULL) 4159145522Sdarrenr *passp = FR_BLOCK; 4160145522Sdarrenr fin->fin_flx |= FI_BADNAT; 4161145522Sdarrenr } 4162145522Sdarrenr return rval; 4163145522Sdarrenr} 4164145522Sdarrenr 4165145522Sdarrenr 4166145522Sdarrenr/* ------------------------------------------------------------------------ */ 4167145522Sdarrenr/* Function: fr_natin */ 4168145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 4169145522Sdarrenr/* 1 == packet was successfully translated. */ 4170145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4171145522Sdarrenr/* nat(I) - pointer to NAT structure */ 4172145522Sdarrenr/* natadd(I) - flag indicating if it is safe to add frag cache */ 4173145522Sdarrenr/* nflags(I) - NAT flags set for this packet */ 4174145522Sdarrenr/* Locks Held: ipf_nat (READ) */ 4175145522Sdarrenr/* */ 4176145522Sdarrenr/* Translate a packet coming "in" on an interface. */ 4177145522Sdarrenr/* ------------------------------------------------------------------------ */ 4178145522Sdarrenrint fr_natin(fin, nat, natadd, nflags) 4179145522Sdarrenrfr_info_t *fin; 4180145522Sdarrenrnat_t *nat; 4181145522Sdarrenrint natadd; 4182145522Sdarrenru_32_t nflags; 4183145522Sdarrenr{ 4184145522Sdarrenr icmphdr_t *icmp; 4185145522Sdarrenr u_short *csump; 4186145522Sdarrenr tcphdr_t *tcp; 4187145522Sdarrenr ipnat_t *np; 4188145522Sdarrenr int i; 4189145522Sdarrenr 4190145522Sdarrenr tcp = NULL; 4191145522Sdarrenr csump = NULL; 4192145522Sdarrenr np = nat->nat_ptr; 4193145522Sdarrenr fin->fin_fr = nat->nat_fr; 4194145522Sdarrenr 4195145522Sdarrenr if (np != NULL) { 4196145522Sdarrenr if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) 4197145522Sdarrenr (void) fr_nat_newfrag(fin, 0, nat); 4198145522Sdarrenr 4199145522Sdarrenr /* ------------------------------------------------------------- */ 4200145522Sdarrenr /* A few quick notes: */ 4201145522Sdarrenr /* Following are test conditions prior to calling the */ 4202145522Sdarrenr /* appr_check routine. */ 4203145522Sdarrenr /* */ 4204145522Sdarrenr /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 4205145522Sdarrenr /* with a map rule, we attempt to match the packet's */ 4206145522Sdarrenr /* source port against in_dport, otherwise we'd compare the */ 4207145522Sdarrenr /* packet's destination. */ 4208145522Sdarrenr /* ------------------------------------------------------------- */ 4209145522Sdarrenr if (np->in_apr != NULL) { 4210145522Sdarrenr i = appr_check(fin, nat); 421160852Sdarrenr if (i == -1) { 4212145522Sdarrenr return -1; 421360852Sdarrenr } 421460852Sdarrenr } 4215145522Sdarrenr } 421653642Sguido 4217145522Sdarrenr#ifdef IPFILTER_SYNC 4218145522Sdarrenr ipfsync_update(SMC_NAT, fin, nat->nat_sync); 4219145522Sdarrenr#endif 4220145522Sdarrenr 4221145522Sdarrenr MUTEX_ENTER(&nat->nat_lock); 4222145522Sdarrenr nat->nat_bytes[0] += fin->fin_plen; 4223145522Sdarrenr nat->nat_pkts[0]++; 4224145522Sdarrenr MUTEX_EXIT(&nat->nat_lock); 4225145522Sdarrenr 4226145522Sdarrenr fin->fin_ip->ip_dst = nat->nat_inip; 4227145522Sdarrenr fin->fin_fi.fi_daddr = nat->nat_inip.s_addr; 4228145522Sdarrenr if (nflags & IPN_TCPUDP) 4229145522Sdarrenr tcp = fin->fin_dp; 4230145522Sdarrenr 4231145522Sdarrenr /* 4232145522Sdarrenr * Fix up checksums, not by recalculating them, but 4233145522Sdarrenr * simply computing adjustments. 4234145522Sdarrenr * Why only do this for some platforms on inbound packets ? 4235145522Sdarrenr * Because for those that it is done, IP processing is yet to happen 4236145522Sdarrenr * and so the IPv4 header checksum has not yet been evaluated. 4237145522Sdarrenr * Perhaps it should always be done for the benefit of things like 4238145522Sdarrenr * fast forwarding (so that it doesn't need to be recomputed) but with 4239145522Sdarrenr * header checksum offloading, perhaps it is a moot point. 4240145522Sdarrenr */ 4241145522Sdarrenr#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 4242145522Sdarrenr defined(__osf__) || defined(linux) 4243145522Sdarrenr if (nat->nat_dir == NAT_OUTBOUND) 4244145522Sdarrenr fix_incksum(fin, &fin->fin_ip->ip_sum, nat->nat_ipsumd); 4245145522Sdarrenr else 4246145522Sdarrenr fix_outcksum(fin, &fin->fin_ip->ip_sum, nat->nat_ipsumd); 4247145522Sdarrenr#endif 4248145522Sdarrenr 4249145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 4250145522Sdarrenr if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) { 4251145522Sdarrenr tcp->th_dport = nat->nat_inport; 4252145522Sdarrenr fin->fin_data[1] = ntohs(nat->nat_inport); 425392685Sdarrenr } 425453642Sguido 4255145522Sdarrenr 4256145522Sdarrenr if ((nat->nat_inport != 0) && (nflags & IPN_ICMPQUERY)) { 4257145522Sdarrenr icmp = fin->fin_dp; 4258145522Sdarrenr 4259145522Sdarrenr icmp->icmp_id = nat->nat_inport; 4260145522Sdarrenr } 4261145522Sdarrenr 4262145522Sdarrenr csump = nat_proto(fin, nat, nflags); 4263145522Sdarrenr } 4264145522Sdarrenr 4265145522Sdarrenr nat_update(fin, nat, np); 4266145522Sdarrenr 4267145522Sdarrenr /* 4268145522Sdarrenr * The above comments do not hold for layer 4 (or higher) checksums... 4269145522Sdarrenr */ 4270145522Sdarrenr if (csump != NULL) { 427153642Sguido if (nat->nat_dir == NAT_OUTBOUND) 4272145522Sdarrenr fix_incksum(fin, csump, nat->nat_sumd[0]); 427353642Sguido else 4274145522Sdarrenr fix_outcksum(fin, csump, nat->nat_sumd[0]); 4275145522Sdarrenr } 4276145522Sdarrenr ATOMIC_INCL(nat_stats.ns_mapped[0]); 4277145522Sdarrenr fin->fin_flx |= FI_NATED; 4278145522Sdarrenr if (np != NULL && np->in_tag.ipt_num[0] != 0) 4279145522Sdarrenr fin->fin_nattag = &np->in_tag; 4280145522Sdarrenr return 1; 4281145522Sdarrenr} 4282130886Sdarrenr 4283130886Sdarrenr 4284145522Sdarrenr/* ------------------------------------------------------------------------ */ 4285145522Sdarrenr/* Function: nat_proto */ 4286145522Sdarrenr/* Returns: u_short* - pointer to transport header checksum to update, */ 4287145522Sdarrenr/* NULL if the transport protocol is not recognised */ 4288145522Sdarrenr/* as needing a checksum update. */ 4289145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4290145522Sdarrenr/* nat(I) - pointer to NAT structure */ 4291145522Sdarrenr/* nflags(I) - NAT flags set for this packet */ 4292145522Sdarrenr/* */ 4293145522Sdarrenr/* Return the pointer to the checksum field for each protocol so understood.*/ 4294145522Sdarrenr/* If support for making other changes to a protocol header is required, */ 4295145522Sdarrenr/* that is not strictly 'address' translation, such as clamping the MSS in */ 4296145522Sdarrenr/* TCP down to a specific value, then do it from here. */ 4297145522Sdarrenr/* ------------------------------------------------------------------------ */ 4298145522Sdarrenru_short *nat_proto(fin, nat, nflags) 4299145522Sdarrenrfr_info_t *fin; 4300145522Sdarrenrnat_t *nat; 4301145522Sdarrenru_int nflags; 4302145522Sdarrenr{ 4303145522Sdarrenr icmphdr_t *icmp; 4304145522Sdarrenr u_short *csump; 4305145522Sdarrenr tcphdr_t *tcp; 4306145522Sdarrenr udphdr_t *udp; 430753642Sguido 4308145522Sdarrenr csump = NULL; 4309145522Sdarrenr if (fin->fin_out == 0) { 4310145522Sdarrenr fin->fin_rev = (nat->nat_dir == NAT_OUTBOUND); 4311145522Sdarrenr } else { 4312145522Sdarrenr fin->fin_rev = (nat->nat_dir == NAT_INBOUND); 4313145522Sdarrenr } 431453642Sguido 4315145522Sdarrenr switch (fin->fin_p) 4316145522Sdarrenr { 4317145522Sdarrenr case IPPROTO_TCP : 4318145522Sdarrenr tcp = fin->fin_dp; 4319110916Sdarrenr 4320145522Sdarrenr csump = &tcp->th_sum; 432153642Sguido 4322145522Sdarrenr /* 4323145522Sdarrenr * Do a MSS CLAMPING on a SYN packet, 4324145522Sdarrenr * only deal IPv4 for now. 4325145522Sdarrenr */ 4326145522Sdarrenr if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0) 4327145522Sdarrenr nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump); 432860852Sdarrenr 4329145522Sdarrenr break; 4330145522Sdarrenr 4331145522Sdarrenr case IPPROTO_UDP : 4332145522Sdarrenr udp = fin->fin_dp; 4333145522Sdarrenr 4334145522Sdarrenr if (udp->uh_sum) 4335145522Sdarrenr csump = &udp->uh_sum; 4336145522Sdarrenr break; 4337145522Sdarrenr 4338145522Sdarrenr case IPPROTO_ICMP : 4339145522Sdarrenr icmp = fin->fin_dp; 4340145522Sdarrenr 4341145522Sdarrenr if ((nflags & IPN_ICMPQUERY) != 0) { 4342145522Sdarrenr if (icmp->icmp_cksum != 0) 4343145522Sdarrenr csump = &icmp->icmp_cksum; 434453642Sguido } 4345145522Sdarrenr break; 434653642Sguido } 4347145522Sdarrenr return csump; 434853642Sguido} 434953642Sguido 435053642Sguido 4351145522Sdarrenr/* ------------------------------------------------------------------------ */ 4352145522Sdarrenr/* Function: fr_natunload */ 4353145522Sdarrenr/* Returns: Nil */ 4354145522Sdarrenr/* Parameters: Nil */ 4355145522Sdarrenr/* */ 4356145522Sdarrenr/* Free all memory used by NAT structures allocated at runtime. */ 4357145522Sdarrenr/* ------------------------------------------------------------------------ */ 4358145522Sdarrenrvoid fr_natunload() 435953642Sguido{ 4360145522Sdarrenr ipftq_t *ifq, *ifqnext; 4361145522Sdarrenr 436253642Sguido (void) nat_clearlist(); 436353642Sguido (void) nat_flushtable(); 436453642Sguido 4365145522Sdarrenr /* 4366145522Sdarrenr * Proxy timeout queues are not cleaned here because although they 4367145522Sdarrenr * exist on the NAT list, appr_unload is called after fr_natunload 4368145522Sdarrenr * and the proxies actually are responsible for them being created. 4369145522Sdarrenr * Should the proxy timeouts have their own list? There's no real 4370145522Sdarrenr * justification as this is the only complication. 4371145522Sdarrenr */ 4372145522Sdarrenr for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) { 4373145522Sdarrenr ifqnext = ifq->ifq_next; 4374145522Sdarrenr if (((ifq->ifq_flags & IFQF_PROXY) == 0) && 4375145522Sdarrenr (fr_deletetimeoutqueue(ifq) == 0)) 4376145522Sdarrenr fr_freetimeoutqueue(ifq); 4377145522Sdarrenr } 4378145522Sdarrenr 437953642Sguido if (nat_table[0] != NULL) { 438053642Sguido KFREES(nat_table[0], sizeof(nat_t *) * ipf_nattable_sz); 438153642Sguido nat_table[0] = NULL; 438253642Sguido } 438353642Sguido if (nat_table[1] != NULL) { 438453642Sguido KFREES(nat_table[1], sizeof(nat_t *) * ipf_nattable_sz); 438553642Sguido nat_table[1] = NULL; 438653642Sguido } 438753642Sguido if (nat_rules != NULL) { 438853642Sguido KFREES(nat_rules, sizeof(ipnat_t *) * ipf_natrules_sz); 438953642Sguido nat_rules = NULL; 439053642Sguido } 439153642Sguido if (rdr_rules != NULL) { 439253642Sguido KFREES(rdr_rules, sizeof(ipnat_t *) * ipf_rdrrules_sz); 439353642Sguido rdr_rules = NULL; 439453642Sguido } 4395170268Sdarrenr if (ipf_hm_maptable != NULL) { 4396170268Sdarrenr KFREES(ipf_hm_maptable, sizeof(hostmap_t *) * ipf_hostmap_sz); 4397170268Sdarrenr ipf_hm_maptable = NULL; 439860852Sdarrenr } 4399145522Sdarrenr if (nat_stats.ns_bucketlen[0] != NULL) { 4400145522Sdarrenr KFREES(nat_stats.ns_bucketlen[0], 4401145522Sdarrenr sizeof(u_long *) * ipf_nattable_sz); 4402145522Sdarrenr nat_stats.ns_bucketlen[0] = NULL; 4403145522Sdarrenr } 4404145522Sdarrenr if (nat_stats.ns_bucketlen[1] != NULL) { 4405145522Sdarrenr KFREES(nat_stats.ns_bucketlen[1], 4406145522Sdarrenr sizeof(u_long *) * ipf_nattable_sz); 4407145522Sdarrenr nat_stats.ns_bucketlen[1] = NULL; 4408145522Sdarrenr } 4409145522Sdarrenr 4410145522Sdarrenr if (fr_nat_maxbucket_reset == 1) 4411145522Sdarrenr fr_nat_maxbucket = 0; 4412145522Sdarrenr 4413145522Sdarrenr if (fr_nat_init == 1) { 4414145522Sdarrenr fr_nat_init = 0; 4415145522Sdarrenr fr_sttab_destroy(nat_tqb); 4416145522Sdarrenr 4417145522Sdarrenr RW_DESTROY(&ipf_natfrag); 4418145522Sdarrenr RW_DESTROY(&ipf_nat); 4419145522Sdarrenr 4420145522Sdarrenr MUTEX_DESTROY(&ipf_nat_new); 4421145522Sdarrenr MUTEX_DESTROY(&ipf_natio); 4422145522Sdarrenr 4423145522Sdarrenr MUTEX_DESTROY(&nat_udptq.ifq_lock); 4424145522Sdarrenr MUTEX_DESTROY(&nat_icmptq.ifq_lock); 4425145522Sdarrenr MUTEX_DESTROY(&nat_iptq.ifq_lock); 4426145522Sdarrenr } 442753642Sguido} 442853642Sguido 442953642Sguido 4430145522Sdarrenr/* ------------------------------------------------------------------------ */ 4431145522Sdarrenr/* Function: fr_natexpire */ 4432145522Sdarrenr/* Returns: Nil */ 4433145522Sdarrenr/* Parameters: Nil */ 4434145522Sdarrenr/* */ 4435145522Sdarrenr/* Check all of the timeout queues for entries at the top which need to be */ 4436145522Sdarrenr/* expired. */ 4437145522Sdarrenr/* ------------------------------------------------------------------------ */ 4438145522Sdarrenrvoid fr_natexpire() 443953642Sguido{ 4440145522Sdarrenr ipftq_t *ifq, *ifqnext; 4441145522Sdarrenr ipftqent_t *tqe, *tqn; 4442161356Sguido int i; 4443153876Sguido SPL_INT(s); 444453642Sguido 444553642Sguido SPL_NET(s); 444653642Sguido WRITE_ENTER(&ipf_nat); 4447145522Sdarrenr for (ifq = nat_tqb, i = 0; ifq != NULL; ifq = ifq->ifq_next) { 4448145522Sdarrenr for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { 4449145522Sdarrenr if (tqe->tqe_die > fr_ticks) 4450145522Sdarrenr break; 4451145522Sdarrenr tqn = tqe->tqe_next; 4452145522Sdarrenr nat_delete(tqe->tqe_parent, NL_EXPIRE); 445353642Sguido } 445453642Sguido } 4455145522Sdarrenr 4456145522Sdarrenr for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) { 4457145522Sdarrenr ifqnext = ifq->ifq_next; 4458145522Sdarrenr 4459145522Sdarrenr for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { 4460145522Sdarrenr if (tqe->tqe_die > fr_ticks) 4461145522Sdarrenr break; 4462145522Sdarrenr tqn = tqe->tqe_next; 4463145522Sdarrenr nat_delete(tqe->tqe_parent, NL_EXPIRE); 4464145522Sdarrenr } 4465145522Sdarrenr } 4466145522Sdarrenr 4467145522Sdarrenr for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) { 4468145522Sdarrenr ifqnext = ifq->ifq_next; 4469145522Sdarrenr 4470145522Sdarrenr if (((ifq->ifq_flags & IFQF_DELETE) != 0) && 4471145522Sdarrenr (ifq->ifq_ref == 0)) { 4472145522Sdarrenr fr_freetimeoutqueue(ifq); 4473145522Sdarrenr } 4474145522Sdarrenr } 4475145522Sdarrenr 4476170268Sdarrenr if (fr_nat_doflush != 0) { 4477170268Sdarrenr nat_extraflush(2); 4478170268Sdarrenr fr_nat_doflush = 0; 4479170268Sdarrenr } 4480170268Sdarrenr 448153642Sguido RWLOCK_EXIT(&ipf_nat); 448253642Sguido SPL_X(s); 448353642Sguido} 448453642Sguido 448553642Sguido 4486145522Sdarrenr/* ------------------------------------------------------------------------ */ 4487145522Sdarrenr/* Function: fr_natsync */ 4488145522Sdarrenr/* Returns: Nil */ 4489145522Sdarrenr/* Parameters: ifp(I) - pointer to network interface */ 4490145522Sdarrenr/* */ 4491145522Sdarrenr/* Walk through all of the currently active NAT sessions, looking for those */ 4492145522Sdarrenr/* which need to have their translated address updated. */ 4493145522Sdarrenr/* ------------------------------------------------------------------------ */ 4494145522Sdarrenrvoid fr_natsync(ifp) 449553642Sguidovoid *ifp; 449653642Sguido{ 4497145522Sdarrenr u_32_t sum1, sum2, sumd; 449853642Sguido struct in_addr in; 4499145522Sdarrenr ipnat_t *n; 4500145522Sdarrenr nat_t *nat; 450153642Sguido void *ifp2; 4502153876Sguido SPL_INT(s); 450353642Sguido 4504145522Sdarrenr if (fr_running <= 0) 4505145522Sdarrenr return; 4506145522Sdarrenr 450753642Sguido /* 450853642Sguido * Change IP addresses for NAT sessions for any protocol except TCP 4509145522Sdarrenr * since it will break the TCP connection anyway. The only rules 4510145522Sdarrenr * which will get changed are those which are "map ... -> 0/32", 4511145522Sdarrenr * where the rule specifies the address is taken from the interface. 451253642Sguido */ 451353642Sguido SPL_NET(s); 451453642Sguido WRITE_ENTER(&ipf_nat); 4515145522Sdarrenr 4516145522Sdarrenr if (fr_running <= 0) { 4517145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 4518145522Sdarrenr return; 4519145522Sdarrenr } 4520145522Sdarrenr 4521145522Sdarrenr for (nat = nat_instances; nat; nat = nat->nat_next) { 4522145522Sdarrenr if ((nat->nat_flags & IPN_TCP) != 0) 4523145522Sdarrenr continue; 4524145522Sdarrenr n = nat->nat_ptr; 4525145522Sdarrenr if ((n == NULL) || 4526145522Sdarrenr (n->in_outip != 0) || (n->in_outmsk != 0xffffffff)) 4527145522Sdarrenr continue; 4528145522Sdarrenr if (((ifp == NULL) || (ifp == nat->nat_ifps[0]) || 4529145522Sdarrenr (ifp == nat->nat_ifps[1]))) { 4530145522Sdarrenr nat->nat_ifps[0] = GETIFP(nat->nat_ifnames[0], 4); 4531145522Sdarrenr if (nat->nat_ifnames[1][0] != '\0') { 4532145522Sdarrenr nat->nat_ifps[1] = GETIFP(nat->nat_ifnames[1], 4533145522Sdarrenr 4); 4534145522Sdarrenr } else 4535145522Sdarrenr nat->nat_ifps[1] = nat->nat_ifps[0]; 4536145522Sdarrenr ifp2 = nat->nat_ifps[0]; 4537145522Sdarrenr if (ifp2 == NULL) 4538145522Sdarrenr continue; 4539145522Sdarrenr 454053642Sguido /* 454153642Sguido * Change the map-to address to be the same as the 454253642Sguido * new one. 454353642Sguido */ 454453642Sguido sum1 = nat->nat_outip.s_addr; 4545145522Sdarrenr if (fr_ifpaddr(4, FRI_NORMAL, ifp2, &in, NULL) != -1) 454655929Sguido nat->nat_outip = in; 454753642Sguido sum2 = nat->nat_outip.s_addr; 454853642Sguido 454953642Sguido if (sum1 == sum2) 455053642Sguido continue; 455153642Sguido /* 455253642Sguido * Readjust the checksum adjustment to take into 455353642Sguido * account the new IP#. 455453642Sguido */ 455553642Sguido CALC_SUMD(sum1, sum2, sumd); 455655929Sguido /* XXX - dont change for TCP when solaris does 455755929Sguido * hardware checksumming. 455855929Sguido */ 455955929Sguido sumd += nat->nat_sumd[0]; 456055929Sguido nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 456155929Sguido nat->nat_sumd[1] = nat->nat_sumd[0]; 456253642Sguido } 4563145522Sdarrenr } 456453642Sguido 4565145522Sdarrenr for (n = nat_list; (n != NULL); n = n->in_next) { 4566145522Sdarrenr if ((ifp == NULL) || (n->in_ifps[0] == ifp)) 4567145522Sdarrenr n->in_ifps[0] = fr_resolvenic(n->in_ifnames[0], 4); 4568145522Sdarrenr if ((ifp == NULL) || (n->in_ifps[1] == ifp)) 4569145522Sdarrenr n->in_ifps[1] = fr_resolvenic(n->in_ifnames[1], 4); 4570145522Sdarrenr } 457160852Sdarrenr RWLOCK_EXIT(&ipf_nat); 457253642Sguido SPL_X(s); 457353642Sguido} 457453642Sguido 457553642Sguido 4576145522Sdarrenr/* ------------------------------------------------------------------------ */ 4577145522Sdarrenr/* Function: nat_icmpquerytype4 */ 4578145522Sdarrenr/* Returns: int - 1 == success, 0 == failure */ 4579145522Sdarrenr/* Parameters: icmptype(I) - ICMP type number */ 4580145522Sdarrenr/* */ 4581145522Sdarrenr/* Tests to see if the ICMP type number passed is a query/response type or */ 4582145522Sdarrenr/* not. */ 4583145522Sdarrenr/* ------------------------------------------------------------------------ */ 4584153876Sguidostatic int nat_icmpquerytype4(icmptype) 4585145522Sdarrenrint icmptype; 4586145522Sdarrenr{ 4587145522Sdarrenr 4588145522Sdarrenr /* 4589145522Sdarrenr * For the ICMP query NAT code, it is essential that both the query 4590145522Sdarrenr * and the reply match on the NAT rule. Because the NAT structure 4591145522Sdarrenr * does not keep track of the icmptype, and a single NAT structure 4592145522Sdarrenr * is used for all icmp types with the same src, dest and id, we 4593145522Sdarrenr * simply define the replies as queries as well. The funny thing is, 4594145522Sdarrenr * altough it seems silly to call a reply a query, this is exactly 4595145522Sdarrenr * as it is defined in the IPv4 specification 4596145522Sdarrenr */ 4597145522Sdarrenr 4598145522Sdarrenr switch (icmptype) 4599145522Sdarrenr { 4600145522Sdarrenr 4601145522Sdarrenr case ICMP_ECHOREPLY: 4602145522Sdarrenr case ICMP_ECHO: 4603145522Sdarrenr /* route aedvertisement/solliciation is currently unsupported: */ 4604145522Sdarrenr /* it would require rewriting the ICMP data section */ 4605145522Sdarrenr case ICMP_TSTAMP: 4606145522Sdarrenr case ICMP_TSTAMPREPLY: 4607145522Sdarrenr case ICMP_IREQ: 4608145522Sdarrenr case ICMP_IREQREPLY: 4609145522Sdarrenr case ICMP_MASKREQ: 4610145522Sdarrenr case ICMP_MASKREPLY: 4611145522Sdarrenr return 1; 4612145522Sdarrenr default: 4613145522Sdarrenr return 0; 4614145522Sdarrenr } 4615145522Sdarrenr} 4616145522Sdarrenr 4617145522Sdarrenr 4618145522Sdarrenr/* ------------------------------------------------------------------------ */ 4619145522Sdarrenr/* Function: nat_log */ 4620145522Sdarrenr/* Returns: Nil */ 4621145522Sdarrenr/* Parameters: nat(I) - pointer to NAT structure */ 4622145522Sdarrenr/* type(I) - type of log entry to create */ 4623145522Sdarrenr/* */ 4624145522Sdarrenr/* Creates a NAT log entry. */ 4625145522Sdarrenr/* ------------------------------------------------------------------------ */ 462653642Sguidovoid nat_log(nat, type) 462753642Sguidostruct nat *nat; 462853642Sguidou_int type; 462953642Sguido{ 4630145522Sdarrenr#ifdef IPFILTER_LOG 4631139005Smlaier# ifndef LARGE_NAT 463253642Sguido struct ipnat *np; 4633138979Sdarrenr int rulen; 4634138979Sdarrenr# endif 463553642Sguido struct natlog natl; 463653642Sguido void *items[1]; 463753642Sguido size_t sizes[1]; 4638138979Sdarrenr int types[1]; 463953642Sguido 464053642Sguido natl.nl_inip = nat->nat_inip; 464153642Sguido natl.nl_outip = nat->nat_outip; 464253642Sguido natl.nl_origip = nat->nat_oip; 4643145522Sdarrenr natl.nl_bytes[0] = nat->nat_bytes[0]; 4644145522Sdarrenr natl.nl_bytes[1] = nat->nat_bytes[1]; 4645145522Sdarrenr natl.nl_pkts[0] = nat->nat_pkts[0]; 4646145522Sdarrenr natl.nl_pkts[1] = nat->nat_pkts[1]; 464753642Sguido natl.nl_origport = nat->nat_oport; 464853642Sguido natl.nl_inport = nat->nat_inport; 464953642Sguido natl.nl_outport = nat->nat_outport; 465057096Sguido natl.nl_p = nat->nat_p; 465153642Sguido natl.nl_type = type; 465253642Sguido natl.nl_rule = -1; 4653145522Sdarrenr# ifndef LARGE_NAT 465453642Sguido if (nat->nat_ptr != NULL) { 465553642Sguido for (rulen = 0, np = nat_list; np; np = np->in_next, rulen++) 465653642Sguido if (np == nat->nat_ptr) { 465753642Sguido natl.nl_rule = rulen; 465853642Sguido break; 465953642Sguido } 466053642Sguido } 4661145522Sdarrenr# endif 466253642Sguido items[0] = &natl; 466353642Sguido sizes[0] = sizeof(natl); 466453642Sguido types[0] = 0; 466553642Sguido 466653642Sguido (void) ipllog(IPL_LOGNAT, NULL, items, sizes, types, 1); 4667145522Sdarrenr#endif 466853642Sguido} 466992685Sdarrenr 467092685Sdarrenr 467192685Sdarrenr#if defined(__OpenBSD__) 4672145522Sdarrenr/* ------------------------------------------------------------------------ */ 4673145522Sdarrenr/* Function: nat_ifdetach */ 4674145522Sdarrenr/* Returns: Nil */ 4675145522Sdarrenr/* Parameters: ifp(I) - pointer to network interface */ 4676145522Sdarrenr/* */ 4677145522Sdarrenr/* Compatibility interface for OpenBSD to trigger the correct updating of */ 4678145522Sdarrenr/* interface references within IPFilter. */ 4679145522Sdarrenr/* ------------------------------------------------------------------------ */ 468092685Sdarrenrvoid nat_ifdetach(ifp) 468192685Sdarrenrvoid *ifp; 468292685Sdarrenr{ 4683145522Sdarrenr frsync(ifp); 468492685Sdarrenr return; 468592685Sdarrenr} 468692685Sdarrenr#endif 4687110916Sdarrenr 4688110916Sdarrenr 4689145522Sdarrenr/* ------------------------------------------------------------------------ */ 4690170268Sdarrenr/* Function: fr_ipnatderef */ 4691170268Sdarrenr/* Returns: Nil */ 4692170268Sdarrenr/* Parameters: isp(I) - pointer to pointer to NAT rule */ 4693170268Sdarrenr/* Write Locks: ipf_nat */ 4694170268Sdarrenr/* */ 4695170268Sdarrenr/* ------------------------------------------------------------------------ */ 4696170268Sdarrenrvoid fr_ipnatderef(inp) 4697170268Sdarrenripnat_t **inp; 4698170268Sdarrenr{ 4699170268Sdarrenr ipnat_t *in; 4700170268Sdarrenr 4701170268Sdarrenr in = *inp; 4702170268Sdarrenr *inp = NULL; 4703170268Sdarrenr in->in_space++; 4704170268Sdarrenr in->in_use--; 4705170268Sdarrenr if (in->in_use == 0 && (in->in_flags & IPN_DELETE)) { 4706170268Sdarrenr if (in->in_apr) 4707170268Sdarrenr appr_free(in->in_apr); 4708172776Sdarrenr MUTEX_DESTROY(&in->in_lock); 4709170268Sdarrenr KFREE(in); 4710170268Sdarrenr nat_stats.ns_rules--; 4711172776Sdarrenr#if SOLARIS && !defined(_INET_IP_STACK_H) 4712170268Sdarrenr if (nat_stats.ns_rules == 0) 4713170268Sdarrenr pfil_delayed_copy = 1; 4714170268Sdarrenr#endif 4715170268Sdarrenr } 4716170268Sdarrenr} 4717170268Sdarrenr 4718170268Sdarrenr 4719170268Sdarrenr/* ------------------------------------------------------------------------ */ 4720145522Sdarrenr/* Function: fr_natderef */ 4721145522Sdarrenr/* Returns: Nil */ 4722145522Sdarrenr/* Parameters: isp(I) - pointer to pointer to NAT table entry */ 4723145522Sdarrenr/* */ 4724145522Sdarrenr/* Decrement the reference counter for this NAT table entry and free it if */ 4725145522Sdarrenr/* there are no more things using it. */ 4726172776Sdarrenr/* */ 4727172776Sdarrenr/* IF nat_ref == 1 when this function is called, then we have an orphan nat */ 4728172776Sdarrenr/* structure *because* it only gets called on paths _after_ nat_ref has been*/ 4729172776Sdarrenr/* incremented. If nat_ref == 1 then we shouldn't decrement it here */ 4730172776Sdarrenr/* because nat_delete() will do that and send nat_ref to -1. */ 4731172776Sdarrenr/* */ 4732172776Sdarrenr/* Holding the lock on nat_lock is required to serialise nat_delete() being */ 4733172776Sdarrenr/* called from a NAT flush ioctl with a deref happening because of a packet.*/ 4734145522Sdarrenr/* ------------------------------------------------------------------------ */ 4735145522Sdarrenrvoid fr_natderef(natp) 4736145522Sdarrenrnat_t **natp; 4737145522Sdarrenr{ 4738145522Sdarrenr nat_t *nat; 4739145522Sdarrenr 4740145522Sdarrenr nat = *natp; 4741145522Sdarrenr *natp = NULL; 4742172776Sdarrenr 4743172776Sdarrenr MUTEX_ENTER(&nat->nat_lock); 4744172776Sdarrenr if (nat->nat_ref > 1) { 4745172776Sdarrenr nat->nat_ref--; 4746172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 4747172776Sdarrenr return; 4748172776Sdarrenr } 4749172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 4750172776Sdarrenr 4751145522Sdarrenr WRITE_ENTER(&ipf_nat); 4752172776Sdarrenr nat_delete(nat, NL_EXPIRE); 4753145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 4754145522Sdarrenr} 4755145522Sdarrenr 4756145522Sdarrenr 4757145522Sdarrenr/* ------------------------------------------------------------------------ */ 4758145522Sdarrenr/* Function: fr_natclone */ 4759145522Sdarrenr/* Returns: ipstate_t* - NULL == cloning failed, */ 4760145522Sdarrenr/* else pointer to new state structure */ 4761145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4762145522Sdarrenr/* is(I) - pointer to master state structure */ 4763145522Sdarrenr/* Write Lock: ipf_nat */ 4764145522Sdarrenr/* */ 4765145522Sdarrenr/* Create a "duplcate" state table entry from the master. */ 4766145522Sdarrenr/* ------------------------------------------------------------------------ */ 4767145522Sdarrenrstatic nat_t *fr_natclone(fin, nat) 4768145522Sdarrenrfr_info_t *fin; 4769145522Sdarrenrnat_t *nat; 4770145522Sdarrenr{ 4771145522Sdarrenr frentry_t *fr; 4772145522Sdarrenr nat_t *clone; 4773145522Sdarrenr ipnat_t *np; 4774145522Sdarrenr 4775145522Sdarrenr KMALLOC(clone, nat_t *); 4776145522Sdarrenr if (clone == NULL) 4777145522Sdarrenr return NULL; 4778145522Sdarrenr bcopy((char *)nat, (char *)clone, sizeof(*clone)); 4779145522Sdarrenr 4780145522Sdarrenr MUTEX_NUKE(&clone->nat_lock); 4781145522Sdarrenr 4782153876Sguido clone->nat_aps = NULL; 4783153876Sguido /* 4784153876Sguido * Initialize all these so that nat_delete() doesn't cause a crash. 4785153876Sguido */ 4786153876Sguido clone->nat_tqe.tqe_pnext = NULL; 4787153876Sguido clone->nat_tqe.tqe_next = NULL; 4788153876Sguido clone->nat_tqe.tqe_ifq = NULL; 4789153876Sguido clone->nat_tqe.tqe_parent = clone; 4790153876Sguido 4791145522Sdarrenr clone->nat_flags &= ~SI_CLONE; 4792145522Sdarrenr clone->nat_flags |= SI_CLONED; 4793145522Sdarrenr 4794153876Sguido if (clone->nat_hm) 4795153876Sguido clone->nat_hm->hm_ref++; 4796145522Sdarrenr 4797145522Sdarrenr if (nat_insert(clone, fin->fin_rev) == -1) { 4798145522Sdarrenr KFREE(clone); 4799145522Sdarrenr return NULL; 4800145522Sdarrenr } 4801145522Sdarrenr np = clone->nat_ptr; 4802145522Sdarrenr if (np != NULL) { 4803145522Sdarrenr if (nat_logging) 4804145522Sdarrenr nat_log(clone, (u_int)np->in_redir); 4805145522Sdarrenr np->in_use++; 4806145522Sdarrenr } 4807145522Sdarrenr fr = clone->nat_fr; 4808145522Sdarrenr if (fr != NULL) { 4809145522Sdarrenr MUTEX_ENTER(&fr->fr_lock); 4810145522Sdarrenr fr->fr_ref++; 4811145522Sdarrenr MUTEX_EXIT(&fr->fr_lock); 4812145522Sdarrenr } 4813145522Sdarrenr 4814145522Sdarrenr /* 4815145522Sdarrenr * Because the clone is created outside the normal loop of things and 4816145522Sdarrenr * TCP has special needs in terms of state, initialise the timeout 4817145522Sdarrenr * state of the new NAT from here. 4818145522Sdarrenr */ 4819145522Sdarrenr if (clone->nat_p == IPPROTO_TCP) { 4820153876Sguido (void) fr_tcp_age(&clone->nat_tqe, fin, nat_tqb, 4821145522Sdarrenr clone->nat_flags); 4822145522Sdarrenr } 4823145522Sdarrenr#ifdef IPFILTER_SYNC 4824145522Sdarrenr clone->nat_sync = ipfsync_new(SMC_NAT, fin, clone); 4825145522Sdarrenr#endif 4826145522Sdarrenr if (nat_logging) 4827145522Sdarrenr nat_log(clone, NL_CLONE); 4828145522Sdarrenr return clone; 4829145522Sdarrenr} 4830145522Sdarrenr 4831145522Sdarrenr 4832145522Sdarrenr/* ------------------------------------------------------------------------ */ 4833145522Sdarrenr/* Function: nat_wildok */ 4834145522Sdarrenr/* Returns: int - 1 == packet's ports match wildcards */ 4835145522Sdarrenr/* 0 == packet's ports don't match wildcards */ 4836145522Sdarrenr/* Parameters: nat(I) - NAT entry */ 4837145522Sdarrenr/* sport(I) - source port */ 4838145522Sdarrenr/* dport(I) - destination port */ 4839145522Sdarrenr/* flags(I) - wildcard flags */ 4840145522Sdarrenr/* dir(I) - packet direction */ 4841145522Sdarrenr/* */ 4842145522Sdarrenr/* Use NAT entry and packet direction to determine which combination of */ 4843145522Sdarrenr/* wildcard flags should be used. */ 4844145522Sdarrenr/* ------------------------------------------------------------------------ */ 4845153876Sguidostatic int nat_wildok(nat, sport, dport, flags, dir) 4846145522Sdarrenrnat_t *nat; 4847145522Sdarrenrint sport; 4848145522Sdarrenrint dport; 4849145522Sdarrenrint flags; 4850145522Sdarrenrint dir; 4851145522Sdarrenr{ 4852145522Sdarrenr /* 4853145522Sdarrenr * When called by dir is set to 4854145522Sdarrenr * nat_inlookup NAT_INBOUND (0) 4855145522Sdarrenr * nat_outlookup NAT_OUTBOUND (1) 4856145522Sdarrenr * 4857145522Sdarrenr * We simply combine the packet's direction in dir with the original 4858145522Sdarrenr * "intended" direction of that NAT entry in nat->nat_dir to decide 4859145522Sdarrenr * which combination of wildcard flags to allow. 4860145522Sdarrenr */ 4861145522Sdarrenr 4862145522Sdarrenr switch ((dir << 1) | nat->nat_dir) 4863145522Sdarrenr { 4864145522Sdarrenr case 3: /* outbound packet / outbound entry */ 4865145522Sdarrenr if (((nat->nat_inport == sport) || 4866145522Sdarrenr (flags & SI_W_SPORT)) && 4867145522Sdarrenr ((nat->nat_oport == dport) || 4868145522Sdarrenr (flags & SI_W_DPORT))) 4869145522Sdarrenr return 1; 4870145522Sdarrenr break; 4871145522Sdarrenr case 2: /* outbound packet / inbound entry */ 4872145522Sdarrenr if (((nat->nat_outport == sport) || 4873145522Sdarrenr (flags & SI_W_DPORT)) && 4874145522Sdarrenr ((nat->nat_oport == dport) || 4875145522Sdarrenr (flags & SI_W_SPORT))) 4876145522Sdarrenr return 1; 4877145522Sdarrenr break; 4878145522Sdarrenr case 1: /* inbound packet / outbound entry */ 4879145522Sdarrenr if (((nat->nat_oport == sport) || 4880145522Sdarrenr (flags & SI_W_DPORT)) && 4881145522Sdarrenr ((nat->nat_outport == dport) || 4882145522Sdarrenr (flags & SI_W_SPORT))) 4883145522Sdarrenr return 1; 4884145522Sdarrenr break; 4885145522Sdarrenr case 0: /* inbound packet / inbound entry */ 4886145522Sdarrenr if (((nat->nat_oport == sport) || 4887145522Sdarrenr (flags & SI_W_SPORT)) && 4888145522Sdarrenr ((nat->nat_outport == dport) || 4889145522Sdarrenr (flags & SI_W_DPORT))) 4890145522Sdarrenr return 1; 4891145522Sdarrenr break; 4892145522Sdarrenr default: 4893145522Sdarrenr break; 4894145522Sdarrenr } 4895145522Sdarrenr 4896145522Sdarrenr return(0); 4897145522Sdarrenr} 4898145522Sdarrenr 4899145522Sdarrenr 4900145522Sdarrenr/* ------------------------------------------------------------------------ */ 4901145522Sdarrenr/* Function: nat_mssclamp */ 4902145522Sdarrenr/* Returns: Nil */ 4903145522Sdarrenr/* Parameters: tcp(I) - pointer to TCP header */ 4904145522Sdarrenr/* maxmss(I) - value to clamp the TCP MSS to */ 4905145522Sdarrenr/* fin(I) - pointer to packet information */ 4906145522Sdarrenr/* csump(I) - pointer to TCP checksum */ 4907145522Sdarrenr/* */ 4908145522Sdarrenr/* Check for MSS option and clamp it if necessary. If found and changed, */ 4909145522Sdarrenr/* then the TCP header checksum will be updated to reflect the change in */ 4910145522Sdarrenr/* the MSS. */ 4911145522Sdarrenr/* ------------------------------------------------------------------------ */ 4912110916Sdarrenrstatic void nat_mssclamp(tcp, maxmss, fin, csump) 4913110916Sdarrenrtcphdr_t *tcp; 4914110916Sdarrenru_32_t maxmss; 4915110916Sdarrenrfr_info_t *fin; 4916110916Sdarrenru_short *csump; 4917110916Sdarrenr{ 4918110916Sdarrenr u_char *cp, *ep, opt; 4919110916Sdarrenr int hlen, advance; 4920110916Sdarrenr u_32_t mss, sumd; 4921110916Sdarrenr 4922145522Sdarrenr hlen = TCP_OFF(tcp) << 2; 4923110916Sdarrenr if (hlen > sizeof(*tcp)) { 4924110916Sdarrenr cp = (u_char *)tcp + sizeof(*tcp); 4925110916Sdarrenr ep = (u_char *)tcp + hlen; 4926110916Sdarrenr 4927110916Sdarrenr while (cp < ep) { 4928110916Sdarrenr opt = cp[0]; 4929110916Sdarrenr if (opt == TCPOPT_EOL) 4930110916Sdarrenr break; 4931110916Sdarrenr else if (opt == TCPOPT_NOP) { 4932110916Sdarrenr cp++; 4933110916Sdarrenr continue; 4934110916Sdarrenr } 4935145522Sdarrenr 4936145522Sdarrenr if (cp + 1 >= ep) 4937110916Sdarrenr break; 4938110916Sdarrenr advance = cp[1]; 4939145522Sdarrenr if ((cp + advance > ep) || (advance <= 0)) 4940110916Sdarrenr break; 4941145522Sdarrenr switch (opt) 4942145522Sdarrenr { 4943110916Sdarrenr case TCPOPT_MAXSEG: 4944110916Sdarrenr if (advance != 4) 4945110916Sdarrenr break; 4946145522Sdarrenr mss = cp[2] * 256 + cp[3]; 4947110916Sdarrenr if (mss > maxmss) { 4948145522Sdarrenr cp[2] = maxmss / 256; 4949145522Sdarrenr cp[3] = maxmss & 0xff; 4950110916Sdarrenr CALC_SUMD(mss, maxmss, sumd); 4951110916Sdarrenr fix_outcksum(fin, csump, sumd); 4952110916Sdarrenr } 4953110916Sdarrenr break; 4954110916Sdarrenr default: 4955110916Sdarrenr /* ignore unknown options */ 4956110916Sdarrenr break; 4957110916Sdarrenr } 4958145522Sdarrenr 4959145522Sdarrenr cp += advance; 4960145522Sdarrenr } 4961145522Sdarrenr } 4962145522Sdarrenr} 4963145522Sdarrenr 4964145522Sdarrenr 4965145522Sdarrenr/* ------------------------------------------------------------------------ */ 4966145522Sdarrenr/* Function: fr_setnatqueue */ 4967145522Sdarrenr/* Returns: Nil */ 4968145522Sdarrenr/* Parameters: nat(I)- pointer to NAT structure */ 4969145522Sdarrenr/* rev(I) - forward(0) or reverse(1) direction */ 4970145522Sdarrenr/* Locks: ipf_nat (read or write) */ 4971145522Sdarrenr/* */ 4972145522Sdarrenr/* Put the NAT entry on its default queue entry, using rev as a helped in */ 4973145522Sdarrenr/* determining which queue it should be placed on. */ 4974145522Sdarrenr/* ------------------------------------------------------------------------ */ 4975145522Sdarrenrvoid fr_setnatqueue(nat, rev) 4976145522Sdarrenrnat_t *nat; 4977145522Sdarrenrint rev; 4978145522Sdarrenr{ 4979145522Sdarrenr ipftq_t *oifq, *nifq; 4980145522Sdarrenr 4981145522Sdarrenr if (nat->nat_ptr != NULL) 4982145522Sdarrenr nifq = nat->nat_ptr->in_tqehead[rev]; 4983145522Sdarrenr else 4984145522Sdarrenr nifq = NULL; 4985145522Sdarrenr 4986145522Sdarrenr if (nifq == NULL) { 4987145522Sdarrenr switch (nat->nat_p) 4988145522Sdarrenr { 4989145522Sdarrenr case IPPROTO_UDP : 4990145522Sdarrenr nifq = &nat_udptq; 4991145522Sdarrenr break; 4992145522Sdarrenr case IPPROTO_ICMP : 4993145522Sdarrenr nifq = &nat_icmptq; 4994145522Sdarrenr break; 4995145522Sdarrenr case IPPROTO_TCP : 4996145522Sdarrenr nifq = nat_tqb + nat->nat_tqe.tqe_state[rev]; 4997145522Sdarrenr break; 4998145522Sdarrenr default : 4999145522Sdarrenr nifq = &nat_iptq; 5000145522Sdarrenr break; 5001145522Sdarrenr } 5002145522Sdarrenr } 5003145522Sdarrenr 5004145522Sdarrenr oifq = nat->nat_tqe.tqe_ifq; 5005145522Sdarrenr /* 5006145522Sdarrenr * If it's currently on a timeout queue, move it from one queue to 5007145522Sdarrenr * another, else put it on the end of the newly determined queue. 5008145522Sdarrenr */ 5009145522Sdarrenr if (oifq != NULL) 5010145522Sdarrenr fr_movequeue(&nat->nat_tqe, oifq, nifq); 5011145522Sdarrenr else 5012145522Sdarrenr fr_queueappend(&nat->nat_tqe, nifq, nat); 5013145522Sdarrenr return; 5014145522Sdarrenr} 5015170268Sdarrenr 5016170268Sdarrenr 5017170268Sdarrenr/* ------------------------------------------------------------------------ */ 5018170268Sdarrenr/* Function: nat_getnext */ 5019170268Sdarrenr/* Returns: int - 0 == ok, else error */ 5020170268Sdarrenr/* Parameters: t(I) - pointer to ipftoken structure */ 5021170268Sdarrenr/* itp(I) - pointer to ipfgeniter_t structure */ 5022170268Sdarrenr/* */ 5023170268Sdarrenr/* Fetch the next nat/ipnat structure pointer from the linked list and */ 5024170268Sdarrenr/* copy it out to the storage space pointed to by itp_data. The next item */ 5025170268Sdarrenr/* in the list to look at is put back in the ipftoken struture. */ 5026170268Sdarrenr/* If we call ipf_freetoken, the accompanying pointer is set to NULL because*/ 5027170268Sdarrenr/* ipf_freetoken will call a deref function for us and we dont want to call */ 5028170268Sdarrenr/* that twice (second time would be in the second switch statement below. */ 5029170268Sdarrenr/* ------------------------------------------------------------------------ */ 5030170268Sdarrenrstatic int nat_getnext(t, itp) 5031170268Sdarrenripftoken_t *t; 5032170268Sdarrenripfgeniter_t *itp; 5033170268Sdarrenr{ 5034170268Sdarrenr hostmap_t *hm, *nexthm = NULL, zerohm; 5035170268Sdarrenr ipnat_t *ipn, *nextipnat = NULL, zeroipn; 5036170268Sdarrenr nat_t *nat, *nextnat = NULL, zeronat; 5037170268Sdarrenr int error = 0, count; 5038170268Sdarrenr char *dst; 5039170268Sdarrenr 5040172776Sdarrenr count = itp->igi_nitems; 5041172776Sdarrenr if (count < 1) 5042172776Sdarrenr return ENOSPC; 5043170268Sdarrenr 5044170268Sdarrenr READ_ENTER(&ipf_nat); 5045170268Sdarrenr 5046170268Sdarrenr switch (itp->igi_type) 5047170268Sdarrenr { 5048170268Sdarrenr case IPFGENITER_HOSTMAP : 5049170268Sdarrenr hm = t->ipt_data; 5050170268Sdarrenr if (hm == NULL) { 5051170268Sdarrenr nexthm = ipf_hm_maplist; 5052170268Sdarrenr } else { 5053170268Sdarrenr nexthm = hm->hm_next; 5054170268Sdarrenr } 5055170268Sdarrenr break; 5056170268Sdarrenr 5057170268Sdarrenr case IPFGENITER_IPNAT : 5058170268Sdarrenr ipn = t->ipt_data; 5059170268Sdarrenr if (ipn == NULL) { 5060170268Sdarrenr nextipnat = nat_list; 5061170268Sdarrenr } else { 5062170268Sdarrenr nextipnat = ipn->in_next; 5063170268Sdarrenr } 5064170268Sdarrenr break; 5065170268Sdarrenr 5066170268Sdarrenr case IPFGENITER_NAT : 5067170268Sdarrenr nat = t->ipt_data; 5068170268Sdarrenr if (nat == NULL) { 5069170268Sdarrenr nextnat = nat_instances; 5070170268Sdarrenr } else { 5071170268Sdarrenr nextnat = nat->nat_next; 5072170268Sdarrenr } 5073170268Sdarrenr break; 5074170268Sdarrenr default : 5075170268Sdarrenr RWLOCK_EXIT(&ipf_nat); 5076170268Sdarrenr return EINVAL; 5077170268Sdarrenr } 5078170268Sdarrenr 5079170268Sdarrenr dst = itp->igi_data; 5080172776Sdarrenr for (;;) { 5081170268Sdarrenr switch (itp->igi_type) 5082170268Sdarrenr { 5083170268Sdarrenr case IPFGENITER_HOSTMAP : 5084170268Sdarrenr if (nexthm != NULL) { 5085170268Sdarrenr if (count == 1) { 5086170268Sdarrenr ATOMIC_INC32(nexthm->hm_ref); 5087172776Sdarrenr t->ipt_data = nexthm; 5088170268Sdarrenr } 5089170268Sdarrenr } else { 5090170268Sdarrenr bzero(&zerohm, sizeof(zerohm)); 5091170268Sdarrenr nexthm = &zerohm; 5092170268Sdarrenr count = 1; 5093172776Sdarrenr t->ipt_data = NULL; 5094170268Sdarrenr } 5095170268Sdarrenr break; 5096170268Sdarrenr 5097170268Sdarrenr case IPFGENITER_IPNAT : 5098170268Sdarrenr if (nextipnat != NULL) { 5099170268Sdarrenr if (count == 1) { 5100170268Sdarrenr MUTEX_ENTER(&nextipnat->in_lock); 5101170268Sdarrenr nextipnat->in_use++; 5102170268Sdarrenr MUTEX_EXIT(&nextipnat->in_lock); 5103172776Sdarrenr t->ipt_data = nextipnat; 5104170268Sdarrenr } 5105170268Sdarrenr } else { 5106170268Sdarrenr bzero(&zeroipn, sizeof(zeroipn)); 5107170268Sdarrenr nextipnat = &zeroipn; 5108170268Sdarrenr count = 1; 5109172776Sdarrenr t->ipt_data = NULL; 5110170268Sdarrenr } 5111170268Sdarrenr break; 5112170268Sdarrenr 5113170268Sdarrenr case IPFGENITER_NAT : 5114170268Sdarrenr if (nextnat != NULL) { 5115170268Sdarrenr if (count == 1) { 5116170268Sdarrenr MUTEX_ENTER(&nextnat->nat_lock); 5117170268Sdarrenr nextnat->nat_ref++; 5118170268Sdarrenr MUTEX_EXIT(&nextnat->nat_lock); 5119172776Sdarrenr t->ipt_data = nextnat; 5120170268Sdarrenr } 5121170268Sdarrenr } else { 5122170268Sdarrenr bzero(&zeronat, sizeof(zeronat)); 5123170268Sdarrenr nextnat = &zeronat; 5124170268Sdarrenr count = 1; 5125172776Sdarrenr t->ipt_data = NULL; 5126170268Sdarrenr } 5127170268Sdarrenr break; 5128170268Sdarrenr default : 5129170268Sdarrenr break; 5130170268Sdarrenr } 5131170268Sdarrenr RWLOCK_EXIT(&ipf_nat); 5132170268Sdarrenr 5133172776Sdarrenr /* 5134172776Sdarrenr * Copying out to user space needs to be done without the lock. 5135172776Sdarrenr */ 5136170268Sdarrenr switch (itp->igi_type) 5137170268Sdarrenr { 5138170268Sdarrenr case IPFGENITER_HOSTMAP : 5139170268Sdarrenr error = COPYOUT(nexthm, dst, sizeof(*nexthm)); 5140170268Sdarrenr if (error != 0) 5141170268Sdarrenr error = EFAULT; 5142170268Sdarrenr else 5143170268Sdarrenr dst += sizeof(*nexthm); 5144170268Sdarrenr break; 5145170268Sdarrenr 5146170268Sdarrenr case IPFGENITER_IPNAT : 5147170268Sdarrenr error = COPYOUT(nextipnat, dst, sizeof(*nextipnat)); 5148170268Sdarrenr if (error != 0) 5149170268Sdarrenr error = EFAULT; 5150170268Sdarrenr else 5151170268Sdarrenr dst += sizeof(*nextipnat); 5152170268Sdarrenr break; 5153170268Sdarrenr 5154170268Sdarrenr case IPFGENITER_NAT : 5155170268Sdarrenr error = COPYOUT(nextnat, dst, sizeof(*nextnat)); 5156170268Sdarrenr if (error != 0) 5157170268Sdarrenr error = EFAULT; 5158170268Sdarrenr else 5159170268Sdarrenr dst += sizeof(*nextnat); 5160170268Sdarrenr break; 5161170268Sdarrenr } 5162170268Sdarrenr 5163170268Sdarrenr if ((count == 1) || (error != 0)) 5164170268Sdarrenr break; 5165170268Sdarrenr 5166172776Sdarrenr count--; 5167172776Sdarrenr 5168170268Sdarrenr READ_ENTER(&ipf_nat); 5169170268Sdarrenr 5170172776Sdarrenr /* 5171172776Sdarrenr * We need to have the lock again here to make sure that 5172172776Sdarrenr * using _next is consistent. 5173172776Sdarrenr */ 5174170268Sdarrenr switch (itp->igi_type) 5175170268Sdarrenr { 5176170268Sdarrenr case IPFGENITER_HOSTMAP : 5177172776Sdarrenr nexthm = nexthm->hm_next; 5178170268Sdarrenr break; 5179170268Sdarrenr case IPFGENITER_IPNAT : 5180172776Sdarrenr nextipnat = nextipnat->in_next; 5181170268Sdarrenr break; 5182170268Sdarrenr case IPFGENITER_NAT : 5183172776Sdarrenr nextnat = nextnat->nat_next; 5184170268Sdarrenr break; 5185170268Sdarrenr } 5186170268Sdarrenr } 5187170268Sdarrenr 5188172776Sdarrenr 5189172776Sdarrenr switch (itp->igi_type) 5190172776Sdarrenr { 5191172776Sdarrenr case IPFGENITER_HOSTMAP : 5192172776Sdarrenr if (hm != NULL) { 5193172776Sdarrenr WRITE_ENTER(&ipf_nat); 5194172776Sdarrenr fr_hostmapdel(&hm); 5195172776Sdarrenr RWLOCK_EXIT(&ipf_nat); 5196172776Sdarrenr } 5197172776Sdarrenr break; 5198172776Sdarrenr case IPFGENITER_IPNAT : 5199172776Sdarrenr if (ipn != NULL) { 5200172776Sdarrenr fr_ipnatderef(&ipn); 5201172776Sdarrenr } 5202172776Sdarrenr break; 5203172776Sdarrenr case IPFGENITER_NAT : 5204172776Sdarrenr if (nat != NULL) { 5205172776Sdarrenr fr_natderef(&nat); 5206172776Sdarrenr } 5207172776Sdarrenr break; 5208172776Sdarrenr default : 5209172776Sdarrenr break; 5210172776Sdarrenr } 5211172776Sdarrenr 5212170268Sdarrenr return error; 5213170268Sdarrenr} 5214170268Sdarrenr 5215170268Sdarrenr 5216170268Sdarrenr/* ------------------------------------------------------------------------ */ 5217170268Sdarrenr/* Function: nat_iterator */ 5218170268Sdarrenr/* Returns: int - 0 == ok, else error */ 5219170268Sdarrenr/* Parameters: token(I) - pointer to ipftoken structure */ 5220170268Sdarrenr/* itp(I) - pointer to ipfgeniter_t structure */ 5221170268Sdarrenr/* */ 5222170268Sdarrenr/* This function acts as a handler for the SIOCGENITER ioctls that use a */ 5223170268Sdarrenr/* generic structure to iterate through a list. There are three different */ 5224170268Sdarrenr/* linked lists of NAT related information to go through: NAT rules, active */ 5225170268Sdarrenr/* NAT mappings and the NAT fragment cache. */ 5226170268Sdarrenr/* ------------------------------------------------------------------------ */ 5227170268Sdarrenrstatic int nat_iterator(token, itp) 5228170268Sdarrenripftoken_t *token; 5229170268Sdarrenripfgeniter_t *itp; 5230170268Sdarrenr{ 5231170268Sdarrenr int error; 5232170268Sdarrenr 5233170268Sdarrenr if (itp->igi_data == NULL) 5234170268Sdarrenr return EFAULT; 5235170268Sdarrenr 5236170268Sdarrenr token->ipt_subtype = itp->igi_type; 5237170268Sdarrenr 5238170268Sdarrenr switch (itp->igi_type) 5239170268Sdarrenr { 5240170268Sdarrenr case IPFGENITER_HOSTMAP : 5241170268Sdarrenr case IPFGENITER_IPNAT : 5242170268Sdarrenr case IPFGENITER_NAT : 5243170268Sdarrenr error = nat_getnext(token, itp); 5244170268Sdarrenr break; 5245170268Sdarrenr 5246170268Sdarrenr case IPFGENITER_NATFRAG : 5247170268Sdarrenr#ifdef USE_MUTEXES 5248170268Sdarrenr error = fr_nextfrag(token, itp, &ipfr_natlist, 5249170268Sdarrenr &ipfr_nattail, &ipf_natfrag); 5250170268Sdarrenr#else 5251170268Sdarrenr error = fr_nextfrag(token, itp, &ipfr_natlist, &ipfr_nattail); 5252170268Sdarrenr#endif 5253170268Sdarrenr break; 5254170268Sdarrenr default : 5255170268Sdarrenr error = EINVAL; 5256170268Sdarrenr break; 5257170268Sdarrenr } 5258170268Sdarrenr 5259170268Sdarrenr return error; 5260170268Sdarrenr} 5261170268Sdarrenr 5262170268Sdarrenr 5263170268Sdarrenr/* ------------------------------------------------------------------------ */ 5264170268Sdarrenr/* Function: nat_extraflush */ 5265170268Sdarrenr/* Returns: int - 0 == success, -1 == failure */ 5266170268Sdarrenr/* Parameters: which(I) - how to flush the active NAT table */ 5267170268Sdarrenr/* Write Locks: ipf_nat */ 5268170268Sdarrenr/* */ 5269170268Sdarrenr/* Flush nat tables. Three actions currently defined: */ 5270170268Sdarrenr/* which == 0 : flush all nat table entries */ 5271170268Sdarrenr/* which == 1 : flush TCP connections which have started to close but are */ 5272170268Sdarrenr/* stuck for some reason. */ 5273170268Sdarrenr/* which == 2 : flush TCP connections which have been idle for a long time, */ 5274170268Sdarrenr/* starting at > 4 days idle and working back in successive half-*/ 5275170268Sdarrenr/* days to at most 12 hours old. If this fails to free enough */ 5276170268Sdarrenr/* slots then work backwards in half hour slots to 30 minutes. */ 5277170268Sdarrenr/* If that too fails, then work backwards in 30 second intervals */ 5278170268Sdarrenr/* for the last 30 minutes to at worst 30 seconds idle. */ 5279170268Sdarrenr/* ------------------------------------------------------------------------ */ 5280170268Sdarrenrstatic int nat_extraflush(which) 5281170268Sdarrenrint which; 5282170268Sdarrenr{ 5283170268Sdarrenr ipftq_t *ifq, *ifqnext; 5284170268Sdarrenr nat_t *nat, **natp; 5285170268Sdarrenr ipftqent_t *tqn; 5286170268Sdarrenr int removed; 5287170268Sdarrenr SPL_INT(s); 5288170268Sdarrenr 5289170268Sdarrenr removed = 0; 5290170268Sdarrenr 5291170268Sdarrenr SPL_NET(s); 5292170268Sdarrenr 5293170268Sdarrenr switch (which) 5294170268Sdarrenr { 5295170268Sdarrenr case 0 : 5296170268Sdarrenr /* 5297170268Sdarrenr * Style 0 flush removes everything... 5298170268Sdarrenr */ 5299170268Sdarrenr for (natp = &nat_instances; ((nat = *natp) != NULL); ) { 5300170268Sdarrenr nat_delete(nat, NL_FLUSH); 5301170268Sdarrenr removed++; 5302170268Sdarrenr } 5303170268Sdarrenr break; 5304170268Sdarrenr 5305170268Sdarrenr case 1 : 5306170268Sdarrenr /* 5307170268Sdarrenr * Since we're only interested in things that are closing, 5308170268Sdarrenr * we can start with the appropriate timeout queue. 5309170268Sdarrenr */ 5310170268Sdarrenr for (ifq = nat_tqb + IPF_TCPS_CLOSE_WAIT; ifq != NULL; 5311170268Sdarrenr ifq = ifq->ifq_next) { 5312170268Sdarrenr 5313170268Sdarrenr for (tqn = ifq->ifq_head; tqn != NULL; ) { 5314170268Sdarrenr nat = tqn->tqe_parent; 5315170268Sdarrenr tqn = tqn->tqe_next; 5316170268Sdarrenr if (nat->nat_p != IPPROTO_TCP) 5317170268Sdarrenr break; 5318170268Sdarrenr nat_delete(nat, NL_EXPIRE); 5319170268Sdarrenr removed++; 5320170268Sdarrenr } 5321170268Sdarrenr } 5322170268Sdarrenr 5323170268Sdarrenr /* 5324170268Sdarrenr * Also need to look through the user defined queues. 5325170268Sdarrenr */ 5326170268Sdarrenr for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) { 5327170268Sdarrenr ifqnext = ifq->ifq_next; 5328170268Sdarrenr for (tqn = ifq->ifq_head; tqn != NULL; ) { 5329170268Sdarrenr nat = tqn->tqe_parent; 5330170268Sdarrenr tqn = tqn->tqe_next; 5331170268Sdarrenr if (nat->nat_p != IPPROTO_TCP) 5332170268Sdarrenr continue; 5333170268Sdarrenr 5334170268Sdarrenr if ((nat->nat_tcpstate[0] > 5335170268Sdarrenr IPF_TCPS_ESTABLISHED) && 5336170268Sdarrenr (nat->nat_tcpstate[1] > 5337170268Sdarrenr IPF_TCPS_ESTABLISHED)) { 5338170268Sdarrenr nat_delete(nat, NL_EXPIRE); 5339170268Sdarrenr removed++; 5340170268Sdarrenr } 5341170268Sdarrenr } 5342170268Sdarrenr } 5343170268Sdarrenr break; 5344170268Sdarrenr 5345170268Sdarrenr /* 5346170268Sdarrenr * Args 5-11 correspond to flushing those particular states 5347170268Sdarrenr * for TCP connections. 5348170268Sdarrenr */ 5349170268Sdarrenr case IPF_TCPS_CLOSE_WAIT : 5350170268Sdarrenr case IPF_TCPS_FIN_WAIT_1 : 5351170268Sdarrenr case IPF_TCPS_CLOSING : 5352170268Sdarrenr case IPF_TCPS_LAST_ACK : 5353170268Sdarrenr case IPF_TCPS_FIN_WAIT_2 : 5354170268Sdarrenr case IPF_TCPS_TIME_WAIT : 5355170268Sdarrenr case IPF_TCPS_CLOSED : 5356170268Sdarrenr tqn = nat_tqb[which].ifq_head; 5357170268Sdarrenr while (tqn != NULL) { 5358170268Sdarrenr nat = tqn->tqe_parent; 5359170268Sdarrenr tqn = tqn->tqe_next; 5360170268Sdarrenr nat_delete(nat, NL_FLUSH); 5361170268Sdarrenr removed++; 5362170268Sdarrenr } 5363170268Sdarrenr break; 5364170268Sdarrenr 5365170268Sdarrenr default : 5366170268Sdarrenr if (which < 30) 5367170268Sdarrenr break; 5368170268Sdarrenr 5369170268Sdarrenr /* 5370170268Sdarrenr * Take a large arbitrary number to mean the number of seconds 5371170268Sdarrenr * for which which consider to be the maximum value we'll allow 5372170268Sdarrenr * the expiration to be. 5373170268Sdarrenr */ 5374170268Sdarrenr which = IPF_TTLVAL(which); 5375170268Sdarrenr for (natp = &nat_instances; ((nat = *natp) != NULL); ) { 5376170268Sdarrenr if (fr_ticks - nat->nat_touched > which) { 5377170268Sdarrenr nat_delete(nat, NL_FLUSH); 5378170268Sdarrenr removed++; 5379170268Sdarrenr } else 5380170268Sdarrenr natp = &nat->nat_next; 5381170268Sdarrenr } 5382170268Sdarrenr break; 5383170268Sdarrenr } 5384170268Sdarrenr 5385170268Sdarrenr if (which != 2) { 5386170268Sdarrenr SPL_X(s); 5387170268Sdarrenr return removed; 5388170268Sdarrenr } 5389170268Sdarrenr 5390170268Sdarrenr /* 5391170268Sdarrenr * Asked to remove inactive entries because the table is full. 5392170268Sdarrenr */ 5393170268Sdarrenr if (fr_ticks - nat_last_force_flush > IPF_TTLVAL(5)) { 5394170268Sdarrenr nat_last_force_flush = fr_ticks; 5395170268Sdarrenr removed = ipf_queueflush(nat_flush_entry, nat_tqb, nat_utqe); 5396170268Sdarrenr } 5397170268Sdarrenr 5398170268Sdarrenr SPL_X(s); 5399170268Sdarrenr return removed; 5400170268Sdarrenr} 5401170268Sdarrenr 5402170268Sdarrenr 5403170268Sdarrenr/* ------------------------------------------------------------------------ */ 5404170268Sdarrenr/* Function: nat_flush_entry */ 5405170268Sdarrenr/* Returns: 0 - always succeeds */ 5406170268Sdarrenr/* Parameters: entry(I) - pointer to NAT entry */ 5407170268Sdarrenr/* Write Locks: ipf_nat */ 5408170268Sdarrenr/* */ 5409170268Sdarrenr/* This function is a stepping stone between ipf_queueflush() and */ 5410170268Sdarrenr/* nat_dlete(). It is used so we can provide a uniform interface via the */ 5411170268Sdarrenr/* ipf_queueflush() function. Since the nat_delete() function returns void */ 5412170268Sdarrenr/* we translate that to mean it always succeeds in deleting something. */ 5413170268Sdarrenr/* ------------------------------------------------------------------------ */ 5414170268Sdarrenrstatic int nat_flush_entry(entry) 5415170268Sdarrenrvoid *entry; 5416170268Sdarrenr{ 5417170268Sdarrenr nat_delete(entry, NL_FLUSH); 5418170268Sdarrenr return 0; 5419170268Sdarrenr} 5420172776Sdarrenr 5421172776Sdarrenr 5422172776Sdarrenr/* ------------------------------------------------------------------------ */ 5423172776Sdarrenr/* Function: nat_gettable */ 5424172776Sdarrenr/* Returns: int - 0 = success, else error */ 5425172776Sdarrenr/* Parameters: data(I) - pointer to ioctl data */ 5426172776Sdarrenr/* */ 5427172776Sdarrenr/* This function handles ioctl requests for tables of nat information. */ 5428172776Sdarrenr/* At present the only table it deals with is the hash bucket statistics. */ 5429172776Sdarrenr/* ------------------------------------------------------------------------ */ 5430172776Sdarrenrstatic int nat_gettable(data) 5431172776Sdarrenrchar *data; 5432172776Sdarrenr{ 5433172776Sdarrenr ipftable_t table; 5434172776Sdarrenr int error; 5435172776Sdarrenr 5436172776Sdarrenr error = fr_inobj(data, &table, IPFOBJ_GTABLE); 5437172776Sdarrenr if (error != 0) 5438172776Sdarrenr return error; 5439172776Sdarrenr 5440172776Sdarrenr switch (table.ita_type) 5441172776Sdarrenr { 5442172776Sdarrenr case IPFTABLE_BUCKETS_NATIN : 5443172776Sdarrenr error = COPYOUT(nat_stats.ns_bucketlen[0], table.ita_table, 5444172776Sdarrenr ipf_nattable_sz * sizeof(u_long)); 5445172776Sdarrenr break; 5446172776Sdarrenr 5447172776Sdarrenr case IPFTABLE_BUCKETS_NATOUT : 5448172776Sdarrenr error = COPYOUT(nat_stats.ns_bucketlen[1], table.ita_table, 5449172776Sdarrenr ipf_nattable_sz * sizeof(u_long)); 5450172776Sdarrenr break; 5451172776Sdarrenr 5452172776Sdarrenr default : 5453172776Sdarrenr return EINVAL; 5454172776Sdarrenr } 5455172776Sdarrenr 5456172776Sdarrenr if (error != 0) { 5457172776Sdarrenr error = EFAULT; 5458172776Sdarrenr } 5459172776Sdarrenr return error; 5460172776Sdarrenr} 5461