1145522Sdarrenr/* $FreeBSD$ */ 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$"; 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 *)); 193173181Sdarrenrstatic int fr_natgetent __P((caddr_t, int)); 194173181Sdarrenrstatic int fr_natgetsz __P((caddr_t, int)); 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 665192895Sjamie# if defined(__FreeBSD_version) && (__FreeBSD_version >= 500034) 666192895Sjamie if (securelevel_ge(curthread->td_ucred, 3) && (mode & FWRITE)) { 667192895Sjamie# else 668170268Sdarrenr if ((securelevel >= 3) && (mode & FWRITE)) { 669192895Sjamie# endif 670170268Sdarrenr return EPERM; 671170268Sdarrenr } 672170268Sdarrenr# endif 67353642Sguido#endif 67453642Sguido 675145522Sdarrenr#if defined(__osf__) && defined(_KERNEL) 676145522Sdarrenr getlock = 0; 677145522Sdarrenr#else 678145522Sdarrenr getlock = (mode & NAT_LOCKHELD) ? 0 : 1; 679145522Sdarrenr#endif 680145522Sdarrenr 68153642Sguido nat = NULL; /* XXX gcc -Wuninitialized */ 682145522Sdarrenr if (cmd == (ioctlcmd_t)SIOCADNAT) { 683145522Sdarrenr KMALLOC(nt, ipnat_t *); 684145522Sdarrenr } else { 685145522Sdarrenr nt = NULL; 686145522Sdarrenr } 687145522Sdarrenr 688145522Sdarrenr if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) { 68995418Sdarrenr if (mode & NAT_SYSSPACE) { 69095418Sdarrenr bcopy(data, (char *)&natd, sizeof(natd)); 69195418Sdarrenr error = 0; 69295418Sdarrenr } else { 693145522Sdarrenr error = fr_inobj(data, &natd, IPFOBJ_IPNAT); 69495418Sdarrenr } 69564580Sdarrenr } 69653642Sguido 697145522Sdarrenr if (error != 0) 69860852Sdarrenr goto done; 69960852Sdarrenr 70053642Sguido /* 70153642Sguido * For add/delete, look to see if the NAT entry is already present 70253642Sguido */ 703145522Sdarrenr if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) { 70453642Sguido nat = &natd; 705145522Sdarrenr if (nat->in_v == 0) /* For backward compat. */ 706145522Sdarrenr nat->in_v = 4; 70753642Sguido nat->in_flags &= IPN_USERFLAGS; 70853642Sguido if ((nat->in_redir & NAT_MAPBLK) == 0) { 70960852Sdarrenr if ((nat->in_flags & IPN_SPLIT) == 0) 71060852Sdarrenr nat->in_inip &= nat->in_inmsk; 71160852Sdarrenr if ((nat->in_flags & IPN_IPRANGE) == 0) 71253642Sguido nat->in_outip &= nat->in_outmsk; 71353642Sguido } 714145522Sdarrenr MUTEX_ENTER(&ipf_natio); 715145522Sdarrenr for (np = &nat_list; ((n = *np) != NULL); np = &n->in_next) 716170268Sdarrenr if (bcmp((char *)&nat->in_flags, (char *)&n->in_flags, 717170268Sdarrenr IPN_CMPSIZ) == 0) { 718170268Sdarrenr if (nat->in_redir == NAT_REDIRECT && 719170268Sdarrenr nat->in_pnext != n->in_pnext) 720170268Sdarrenr continue; 72153642Sguido break; 722170268Sdarrenr } 72353642Sguido } 72453642Sguido 72553642Sguido switch (cmd) 72653642Sguido { 72755929Sguido#ifdef IPFILTER_LOG 72855929Sguido case SIOCIPFFB : 72960852Sdarrenr { 73060852Sdarrenr int tmp; 73160852Sdarrenr 73255929Sguido if (!(mode & FWRITE)) 73355929Sguido error = EPERM; 73460852Sdarrenr else { 73560852Sdarrenr tmp = ipflog_clear(IPL_LOGNAT); 736170268Sdarrenr error = BCOPYOUT((char *)&tmp, (char *)data, 737170268Sdarrenr sizeof(tmp)); 738170268Sdarrenr if (error != 0) 739170268Sdarrenr error = EFAULT; 74060852Sdarrenr } 74155929Sguido break; 74260852Sdarrenr } 743170268Sdarrenr 744145522Sdarrenr case SIOCSETLG : 745145522Sdarrenr if (!(mode & FWRITE)) 746145522Sdarrenr error = EPERM; 747145522Sdarrenr else { 748170268Sdarrenr error = BCOPYIN((char *)data, (char *)&nat_logging, 749170268Sdarrenr sizeof(nat_logging)); 750170268Sdarrenr if (error != 0) 751170268Sdarrenr error = EFAULT; 752145522Sdarrenr } 753145522Sdarrenr break; 754170268Sdarrenr 755145522Sdarrenr case SIOCGETLG : 756170268Sdarrenr error = BCOPYOUT((char *)&nat_logging, (char *)data, 757170268Sdarrenr sizeof(nat_logging)); 758170268Sdarrenr if (error != 0) 759170268Sdarrenr error = EFAULT; 760145522Sdarrenr break; 761170268Sdarrenr 762145522Sdarrenr case FIONREAD : 763145522Sdarrenr arg = iplused[IPL_LOGNAT]; 764170268Sdarrenr error = BCOPYOUT(&arg, data, sizeof(arg)); 765170268Sdarrenr if (error != 0) 766170268Sdarrenr error = EFAULT; 767145522Sdarrenr break; 76855929Sguido#endif 76953642Sguido case SIOCADNAT : 77053642Sguido if (!(mode & FWRITE)) { 77153642Sguido error = EPERM; 772145522Sdarrenr } else if (n != NULL) { 77353642Sguido error = EEXIST; 774145522Sdarrenr } else if (nt == NULL) { 775145522Sdarrenr error = ENOMEM; 77653642Sguido } 777145522Sdarrenr if (error != 0) { 778145522Sdarrenr MUTEX_EXIT(&ipf_natio); 77953642Sguido break; 78053642Sguido } 781145522Sdarrenr bcopy((char *)nat, (char *)nt, sizeof(*n)); 782145522Sdarrenr error = nat_siocaddnat(nt, np, getlock); 783145522Sdarrenr MUTEX_EXIT(&ipf_natio); 784145522Sdarrenr if (error == 0) 785145522Sdarrenr nt = NULL; 78653642Sguido break; 787170268Sdarrenr 78853642Sguido case SIOCRMNAT : 78953642Sguido if (!(mode & FWRITE)) { 79053642Sguido error = EPERM; 79153642Sguido n = NULL; 792145522Sdarrenr } else if (n == NULL) { 793145522Sdarrenr error = ESRCH; 79453642Sguido } 795145522Sdarrenr 796145522Sdarrenr if (error != 0) { 797145522Sdarrenr MUTEX_EXIT(&ipf_natio); 79853642Sguido break; 79953642Sguido } 800145522Sdarrenr nat_siocdelnat(n, np, getlock); 801145522Sdarrenr 802145522Sdarrenr MUTEX_EXIT(&ipf_natio); 80353642Sguido n = NULL; 80453642Sguido break; 805170268Sdarrenr 80653642Sguido case SIOCGNATS : 80753642Sguido nat_stats.ns_table[0] = nat_table[0]; 80853642Sguido nat_stats.ns_table[1] = nat_table[1]; 80953642Sguido nat_stats.ns_list = nat_list; 810170268Sdarrenr nat_stats.ns_maptable = ipf_hm_maptable; 811170268Sdarrenr nat_stats.ns_maplist = ipf_hm_maplist; 81253642Sguido nat_stats.ns_nattab_sz = ipf_nattable_sz; 813145522Sdarrenr nat_stats.ns_nattab_max = ipf_nattable_max; 81453642Sguido nat_stats.ns_rultab_sz = ipf_natrules_sz; 81553642Sguido nat_stats.ns_rdrtab_sz = ipf_rdrrules_sz; 81680482Sdarrenr nat_stats.ns_hostmap_sz = ipf_hostmap_sz; 81753642Sguido nat_stats.ns_instances = nat_instances; 81853642Sguido nat_stats.ns_apslist = ap_sess_list; 819170268Sdarrenr nat_stats.ns_ticks = fr_ticks; 820145522Sdarrenr error = fr_outobj(data, &nat_stats, IPFOBJ_NATSTAT); 82153642Sguido break; 822170268Sdarrenr 82353642Sguido case SIOCGNATL : 82453642Sguido { 82553642Sguido natlookup_t nl; 82653642Sguido 827145522Sdarrenr error = fr_inobj(data, &nl, IPFOBJ_NATLOOKUP); 828145522Sdarrenr if (error == 0) { 829173181Sdarrenr void *ptr; 830173181Sdarrenr 831173181Sdarrenr if (getlock) { 832173181Sdarrenr READ_ENTER(&ipf_nat); 833173181Sdarrenr } 834173181Sdarrenr ptr = nat_lookupredir(&nl); 835173181Sdarrenr if (getlock) { 836173181Sdarrenr RWLOCK_EXIT(&ipf_nat); 837173181Sdarrenr } 838173181Sdarrenr if (ptr != NULL) { 839145522Sdarrenr error = fr_outobj(data, &nl, IPFOBJ_NATLOOKUP); 840145522Sdarrenr } else { 841145522Sdarrenr error = ESRCH; 842145522Sdarrenr } 843145522Sdarrenr } 84453642Sguido break; 84553642Sguido } 846170268Sdarrenr 84760852Sdarrenr case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */ 84853642Sguido if (!(mode & FWRITE)) { 84953642Sguido error = EPERM; 85053642Sguido break; 85153642Sguido } 852145522Sdarrenr if (getlock) { 853145522Sdarrenr WRITE_ENTER(&ipf_nat); 854145522Sdarrenr } 855170268Sdarrenr 856170268Sdarrenr error = BCOPYIN(data, &arg, sizeof(arg)); 857170268Sdarrenr if (error != 0) 858170268Sdarrenr error = EFAULT; 859170268Sdarrenr else { 860170268Sdarrenr if (arg == 0) 861170268Sdarrenr ret = nat_flushtable(); 862170268Sdarrenr else if (arg == 1) 863170268Sdarrenr ret = nat_clearlist(); 864170268Sdarrenr else 865170268Sdarrenr ret = nat_extraflush(arg); 866170268Sdarrenr } 867170268Sdarrenr 868145522Sdarrenr if (getlock) { 869145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 87060852Sdarrenr } 871145522Sdarrenr if (error == 0) { 872170268Sdarrenr error = BCOPYOUT(&ret, data, sizeof(ret)); 873145522Sdarrenr } 87453642Sguido break; 875170268Sdarrenr 876145522Sdarrenr case SIOCPROXY : 877170268Sdarrenr error = appr_ioctl(data, cmd, mode, ctx); 878145522Sdarrenr break; 879170268Sdarrenr 88060852Sdarrenr case SIOCSTLCK : 881153876Sguido if (!(mode & FWRITE)) { 882153876Sguido error = EPERM; 883153876Sguido } else { 884172776Sdarrenr error = fr_lock(data, &fr_nat_lock); 885153876Sguido } 88653642Sguido break; 887170268Sdarrenr 88860852Sdarrenr case SIOCSTPUT : 889153876Sguido if ((mode & FWRITE) != 0) { 890145522Sdarrenr error = fr_natputent(data, getlock); 891145522Sdarrenr } else { 89260852Sdarrenr error = EACCES; 893145522Sdarrenr } 89460852Sdarrenr break; 895170268Sdarrenr 89660852Sdarrenr case SIOCSTGSZ : 897145522Sdarrenr if (fr_nat_lock) { 898173181Sdarrenr error = fr_natgetsz(data, getlock); 899145522Sdarrenr } else 90060852Sdarrenr error = EACCES; 90160852Sdarrenr break; 902170268Sdarrenr 90360852Sdarrenr case SIOCSTGET : 904145522Sdarrenr if (fr_nat_lock) { 905173181Sdarrenr error = fr_natgetent(data, getlock); 906145522Sdarrenr } else 90760852Sdarrenr error = EACCES; 90860852Sdarrenr break; 909170268Sdarrenr 910170268Sdarrenr case SIOCGENITER : 911170268Sdarrenr { 912170268Sdarrenr ipfgeniter_t iter; 913170268Sdarrenr ipftoken_t *token; 914170268Sdarrenr 915170268Sdarrenr SPL_SCHED(s); 916170268Sdarrenr error = fr_inobj(data, &iter, IPFOBJ_GENITER); 917170268Sdarrenr if (error == 0) { 918170268Sdarrenr token = ipf_findtoken(iter.igi_type, uid, ctx); 919170268Sdarrenr if (token != NULL) { 920170268Sdarrenr error = nat_iterator(token, &iter); 921170268Sdarrenr } 922170268Sdarrenr RWLOCK_EXIT(&ipf_tokens); 923170268Sdarrenr } 924170268Sdarrenr SPL_X(s); 925170268Sdarrenr break; 926170268Sdarrenr } 927170268Sdarrenr 928170268Sdarrenr case SIOCIPFDELTOK : 929170268Sdarrenr error = BCOPYIN((caddr_t)data, (caddr_t)&arg, sizeof(arg)); 930170268Sdarrenr if (error == 0) { 931170268Sdarrenr SPL_SCHED(s); 932170268Sdarrenr error = ipf_deltoken(arg, uid, ctx); 933170268Sdarrenr SPL_X(s); 934170268Sdarrenr } else { 935170268Sdarrenr error = EFAULT; 936170268Sdarrenr } 937170268Sdarrenr break; 938170268Sdarrenr 939170268Sdarrenr case SIOCGTQTAB : 940170268Sdarrenr error = fr_outobj(data, nat_tqb, IPFOBJ_STATETQTAB); 941170268Sdarrenr break; 942170268Sdarrenr 943172776Sdarrenr case SIOCGTABL : 944172776Sdarrenr error = nat_gettable(data); 945172776Sdarrenr break; 946172776Sdarrenr 94753642Sguido default : 94853642Sguido error = EINVAL; 94953642Sguido break; 95053642Sguido } 95160852Sdarrenrdone: 952170268Sdarrenr if (nt != NULL) 95353642Sguido KFREE(nt); 95453642Sguido return error; 95553642Sguido} 95653642Sguido 95753642Sguido 958145522Sdarrenr/* ------------------------------------------------------------------------ */ 959145522Sdarrenr/* Function: nat_siocaddnat */ 960145522Sdarrenr/* Returns: int - 0 == success, != 0 == failure */ 961145522Sdarrenr/* Parameters: n(I) - pointer to new NAT rule */ 962145522Sdarrenr/* np(I) - pointer to where to insert new NAT rule */ 963145522Sdarrenr/* getlock(I) - flag indicating if lock on ipf_nat is held */ 964145522Sdarrenr/* Mutex Locks: ipf_natio */ 965145522Sdarrenr/* */ 966145522Sdarrenr/* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 967145522Sdarrenr/* from information passed to the kernel, then add it to the appropriate */ 968145522Sdarrenr/* NAT rule table(s). */ 969145522Sdarrenr/* ------------------------------------------------------------------------ */ 970145522Sdarrenrstatic int nat_siocaddnat(n, np, getlock) 971145522Sdarrenripnat_t *n, **np; 972145522Sdarrenrint getlock; 973145522Sdarrenr{ 974145522Sdarrenr int error = 0, i, j; 975145522Sdarrenr 976161356Sguido if (nat_resolverule(n) != 0) 977161356Sguido return ENOENT; 978145522Sdarrenr 979145522Sdarrenr if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) 980145522Sdarrenr return EINVAL; 981145522Sdarrenr 982145522Sdarrenr n->in_use = 0; 983145522Sdarrenr if (n->in_redir & NAT_MAPBLK) 984145522Sdarrenr n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk); 985145522Sdarrenr else if (n->in_flags & IPN_AUTOPORTMAP) 986145522Sdarrenr n->in_space = USABLE_PORTS * ~ntohl(n->in_inmsk); 987145522Sdarrenr else if (n->in_flags & IPN_IPRANGE) 988145522Sdarrenr n->in_space = ntohl(n->in_outmsk) - ntohl(n->in_outip); 989145522Sdarrenr else if (n->in_flags & IPN_SPLIT) 990145522Sdarrenr n->in_space = 2; 991145522Sdarrenr else if (n->in_outmsk != 0) 992145522Sdarrenr n->in_space = ~ntohl(n->in_outmsk); 993145522Sdarrenr else 994145522Sdarrenr n->in_space = 1; 995145522Sdarrenr 996145522Sdarrenr /* 997145522Sdarrenr * Calculate the number of valid IP addresses in the output 998145522Sdarrenr * mapping range. In all cases, the range is inclusive of 999145522Sdarrenr * the start and ending IP addresses. 1000145522Sdarrenr * If to a CIDR address, lose 2: broadcast + network address 1001145522Sdarrenr * (so subtract 1) 1002145522Sdarrenr * If to a range, add one. 1003145522Sdarrenr * If to a single IP address, set to 1. 1004145522Sdarrenr */ 1005145522Sdarrenr if (n->in_space) { 1006145522Sdarrenr if ((n->in_flags & IPN_IPRANGE) != 0) 1007145522Sdarrenr n->in_space += 1; 1008145522Sdarrenr else 1009145522Sdarrenr n->in_space -= 1; 1010145522Sdarrenr } else 1011145522Sdarrenr n->in_space = 1; 1012145522Sdarrenr 1013145522Sdarrenr if ((n->in_outmsk != 0xffffffff) && (n->in_outmsk != 0) && 1014145522Sdarrenr ((n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0)) 1015145522Sdarrenr n->in_nip = ntohl(n->in_outip) + 1; 1016145522Sdarrenr else if ((n->in_flags & IPN_SPLIT) && 1017145522Sdarrenr (n->in_redir & NAT_REDIRECT)) 1018145522Sdarrenr n->in_nip = ntohl(n->in_inip); 1019145522Sdarrenr else 1020145522Sdarrenr n->in_nip = ntohl(n->in_outip); 1021145522Sdarrenr if (n->in_redir & NAT_MAP) { 1022145522Sdarrenr n->in_pnext = ntohs(n->in_pmin); 1023145522Sdarrenr /* 1024145522Sdarrenr * Multiply by the number of ports made available. 1025145522Sdarrenr */ 1026145522Sdarrenr if (ntohs(n->in_pmax) >= ntohs(n->in_pmin)) { 1027145522Sdarrenr n->in_space *= (ntohs(n->in_pmax) - 1028145522Sdarrenr ntohs(n->in_pmin) + 1); 1029145522Sdarrenr /* 1030145522Sdarrenr * Because two different sources can map to 1031145522Sdarrenr * different destinations but use the same 1032145522Sdarrenr * local IP#/port #. 1033145522Sdarrenr * If the result is smaller than in_space, then 1034145522Sdarrenr * we may have wrapped around 32bits. 1035145522Sdarrenr */ 1036145522Sdarrenr i = n->in_inmsk; 1037145522Sdarrenr if ((i != 0) && (i != 0xffffffff)) { 1038145522Sdarrenr j = n->in_space * (~ntohl(i) + 1); 1039145522Sdarrenr if (j >= n->in_space) 1040145522Sdarrenr n->in_space = j; 1041145522Sdarrenr else 1042145522Sdarrenr n->in_space = 0xffffffff; 1043145522Sdarrenr } 1044145522Sdarrenr } 1045145522Sdarrenr /* 1046145522Sdarrenr * If no protocol is specified, multiple by 256 to allow for 1047145522Sdarrenr * at least one IP:IP mapping per protocol. 1048145522Sdarrenr */ 1049145522Sdarrenr if ((n->in_flags & IPN_TCPUDPICMP) == 0) { 1050145522Sdarrenr j = n->in_space * 256; 1051145522Sdarrenr if (j >= n->in_space) 1052145522Sdarrenr n->in_space = j; 1053145522Sdarrenr else 1054145522Sdarrenr n->in_space = 0xffffffff; 1055145522Sdarrenr } 1056145522Sdarrenr } 1057145522Sdarrenr 1058145522Sdarrenr /* Otherwise, these fields are preset */ 1059145522Sdarrenr 1060145522Sdarrenr if (getlock) { 1061145522Sdarrenr WRITE_ENTER(&ipf_nat); 1062145522Sdarrenr } 1063145522Sdarrenr n->in_next = NULL; 1064145522Sdarrenr *np = n; 1065145522Sdarrenr 1066145522Sdarrenr if (n->in_age[0] != 0) 1067145522Sdarrenr n->in_tqehead[0] = fr_addtimeoutqueue(&nat_utqe, n->in_age[0]); 1068145522Sdarrenr 1069145522Sdarrenr if (n->in_age[1] != 0) 1070145522Sdarrenr n->in_tqehead[1] = fr_addtimeoutqueue(&nat_utqe, n->in_age[1]); 1071145522Sdarrenr 1072145522Sdarrenr if (n->in_redir & NAT_REDIRECT) { 1073145522Sdarrenr n->in_flags &= ~IPN_NOTDST; 1074145522Sdarrenr nat_addrdr(n); 1075145522Sdarrenr } 1076145522Sdarrenr if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { 1077145522Sdarrenr n->in_flags &= ~IPN_NOTSRC; 1078145522Sdarrenr nat_addnat(n); 1079145522Sdarrenr } 1080170268Sdarrenr MUTEX_INIT(&n->in_lock, "ipnat rule lock"); 1081170268Sdarrenr 1082145522Sdarrenr n = NULL; 1083145522Sdarrenr nat_stats.ns_rules++; 1084172776Sdarrenr#if SOLARIS && !defined(_INET_IP_STACK_H) 1085145522Sdarrenr pfil_delayed_copy = 0; 1086145522Sdarrenr#endif 1087145522Sdarrenr if (getlock) { 1088145522Sdarrenr RWLOCK_EXIT(&ipf_nat); /* WRITE */ 1089145522Sdarrenr } 1090145522Sdarrenr 1091145522Sdarrenr return error; 1092145522Sdarrenr} 1093145522Sdarrenr 1094145522Sdarrenr 1095145522Sdarrenr/* ------------------------------------------------------------------------ */ 1096145522Sdarrenr/* Function: nat_resolvrule */ 1097145522Sdarrenr/* Returns: Nil */ 1098145522Sdarrenr/* Parameters: n(I) - pointer to NAT rule */ 1099145522Sdarrenr/* */ 1100145522Sdarrenr/* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 1101145522Sdarrenr/* from information passed to the kernel, then add it to the appropriate */ 1102145522Sdarrenr/* NAT rule table(s). */ 1103145522Sdarrenr/* ------------------------------------------------------------------------ */ 1104161356Sguidostatic int nat_resolverule(n) 1105145522Sdarrenripnat_t *n; 1106145522Sdarrenr{ 1107145522Sdarrenr n->in_ifnames[0][LIFNAMSIZ - 1] = '\0'; 1108145522Sdarrenr n->in_ifps[0] = fr_resolvenic(n->in_ifnames[0], 4); 1109145522Sdarrenr 1110145522Sdarrenr n->in_ifnames[1][LIFNAMSIZ - 1] = '\0'; 1111145522Sdarrenr if (n->in_ifnames[1][0] == '\0') { 1112145522Sdarrenr (void) strncpy(n->in_ifnames[1], n->in_ifnames[0], LIFNAMSIZ); 1113145522Sdarrenr n->in_ifps[1] = n->in_ifps[0]; 1114145522Sdarrenr } else { 1115161356Sguido n->in_ifps[1] = fr_resolvenic(n->in_ifnames[1], 4); 1116145522Sdarrenr } 1117145522Sdarrenr 1118145522Sdarrenr if (n->in_plabel[0] != '\0') { 1119145522Sdarrenr n->in_apr = appr_lookup(n->in_p, n->in_plabel); 1120161356Sguido if (n->in_apr == NULL) 1121161356Sguido return -1; 1122145522Sdarrenr } 1123161356Sguido return 0; 1124145522Sdarrenr} 1125145522Sdarrenr 1126145522Sdarrenr 1127145522Sdarrenr/* ------------------------------------------------------------------------ */ 1128145522Sdarrenr/* Function: nat_siocdelnat */ 1129145522Sdarrenr/* Returns: int - 0 == success, != 0 == failure */ 1130145522Sdarrenr/* Parameters: n(I) - pointer to new NAT rule */ 1131145522Sdarrenr/* np(I) - pointer to where to insert new NAT rule */ 1132145522Sdarrenr/* getlock(I) - flag indicating if lock on ipf_nat is held */ 1133145522Sdarrenr/* Mutex Locks: ipf_natio */ 1134145522Sdarrenr/* */ 1135145522Sdarrenr/* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 1136145522Sdarrenr/* from information passed to the kernel, then add it to the appropriate */ 1137145522Sdarrenr/* NAT rule table(s). */ 1138145522Sdarrenr/* ------------------------------------------------------------------------ */ 1139145522Sdarrenrstatic void nat_siocdelnat(n, np, getlock) 1140145522Sdarrenripnat_t *n, **np; 1141145522Sdarrenrint getlock; 1142145522Sdarrenr{ 1143145522Sdarrenr if (getlock) { 1144145522Sdarrenr WRITE_ENTER(&ipf_nat); 1145145522Sdarrenr } 1146145522Sdarrenr if (n->in_redir & NAT_REDIRECT) 1147145522Sdarrenr nat_delrdr(n); 1148145522Sdarrenr if (n->in_redir & (NAT_MAPBLK|NAT_MAP)) 1149145522Sdarrenr nat_delnat(n); 1150145522Sdarrenr if (nat_list == NULL) { 1151145522Sdarrenr nat_masks = 0; 1152145522Sdarrenr rdr_masks = 0; 1153145522Sdarrenr } 1154145522Sdarrenr 1155145522Sdarrenr if (n->in_tqehead[0] != NULL) { 1156145522Sdarrenr if (fr_deletetimeoutqueue(n->in_tqehead[0]) == 0) { 1157145522Sdarrenr fr_freetimeoutqueue(n->in_tqehead[1]); 1158145522Sdarrenr } 1159145522Sdarrenr } 1160145522Sdarrenr 1161145522Sdarrenr if (n->in_tqehead[1] != NULL) { 1162145522Sdarrenr if (fr_deletetimeoutqueue(n->in_tqehead[1]) == 0) { 1163145522Sdarrenr fr_freetimeoutqueue(n->in_tqehead[1]); 1164145522Sdarrenr } 1165145522Sdarrenr } 1166145522Sdarrenr 1167145522Sdarrenr *np = n->in_next; 1168145522Sdarrenr 1169145522Sdarrenr if (n->in_use == 0) { 1170145522Sdarrenr if (n->in_apr) 1171145522Sdarrenr appr_free(n->in_apr); 1172172776Sdarrenr MUTEX_DESTROY(&n->in_lock); 1173145522Sdarrenr KFREE(n); 1174145522Sdarrenr nat_stats.ns_rules--; 1175172776Sdarrenr#if SOLARIS && !defined(_INET_IP_STACK_H) 1176145522Sdarrenr if (nat_stats.ns_rules == 0) 1177145522Sdarrenr pfil_delayed_copy = 1; 1178145522Sdarrenr#endif 1179145522Sdarrenr } else { 1180145522Sdarrenr n->in_flags |= IPN_DELETE; 1181145522Sdarrenr n->in_next = NULL; 1182145522Sdarrenr } 1183145522Sdarrenr if (getlock) { 1184145522Sdarrenr RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ 1185145522Sdarrenr } 1186145522Sdarrenr} 1187145522Sdarrenr 1188145522Sdarrenr 1189145522Sdarrenr/* ------------------------------------------------------------------------ */ 1190145522Sdarrenr/* Function: fr_natgetsz */ 1191145522Sdarrenr/* Returns: int - 0 == success, != 0 is the error value. */ 1192145522Sdarrenr/* Parameters: data(I) - pointer to natget structure with kernel pointer */ 1193145522Sdarrenr/* get the size of. */ 1194145522Sdarrenr/* */ 1195145522Sdarrenr/* Handle SIOCSTGSZ. */ 1196145522Sdarrenr/* Return the size of the nat list entry to be copied back to user space. */ 1197145522Sdarrenr/* The size of the entry is stored in the ng_sz field and the enture natget */ 1198145522Sdarrenr/* structure is copied back to the user. */ 1199145522Sdarrenr/* ------------------------------------------------------------------------ */ 1200173181Sdarrenrstatic int fr_natgetsz(data, getlock) 120160852Sdarrenrcaddr_t data; 1202173181Sdarrenrint getlock; 120360852Sdarrenr{ 120460852Sdarrenr ap_session_t *aps; 120560852Sdarrenr nat_t *nat, *n; 120660852Sdarrenr natget_t ng; 120760852Sdarrenr 1208170268Sdarrenr if (BCOPYIN(data, &ng, sizeof(ng)) != 0) 1209170268Sdarrenr return EFAULT; 121060852Sdarrenr 1211173181Sdarrenr if (getlock) { 1212173181Sdarrenr READ_ENTER(&ipf_nat); 1213173181Sdarrenr } 1214173181Sdarrenr 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) { 1223173181Sdarrenr if (getlock) { 1224173181Sdarrenr RWLOCK_EXIT(&ipf_nat); 1225173181Sdarrenr } 1226170268Sdarrenr if (BCOPYOUT(&ng, data, sizeof(ng)) != 0) 1227170268Sdarrenr return EFAULT; 1228145522Sdarrenr return 0; 122960852Sdarrenr } 123060852Sdarrenr } else { 123160852Sdarrenr /* 123260852Sdarrenr * Make sure the pointer we're copying from exists in the 123360852Sdarrenr * current list of entries. Security precaution to prevent 123460852Sdarrenr * copying of random kernel data. 123560852Sdarrenr */ 123660852Sdarrenr for (n = nat_instances; n; n = n->nat_next) 123760852Sdarrenr if (n == nat) 123860852Sdarrenr break; 1239173181Sdarrenr if (n == NULL) { 1240173181Sdarrenr if (getlock) { 1241173181Sdarrenr RWLOCK_EXIT(&ipf_nat); 1242173181Sdarrenr } 124360852Sdarrenr return ESRCH; 1244173181Sdarrenr } 124560852Sdarrenr } 124660852Sdarrenr 1247145522Sdarrenr /* 1248145522Sdarrenr * Incluse any space required for proxy data structures. 1249145522Sdarrenr */ 125060852Sdarrenr ng.ng_sz = sizeof(nat_save_t); 125160852Sdarrenr aps = nat->nat_aps; 1252145522Sdarrenr if (aps != NULL) { 1253145522Sdarrenr ng.ng_sz += sizeof(ap_session_t) - 4; 1254145522Sdarrenr if (aps->aps_data != 0) 1255145522Sdarrenr ng.ng_sz += aps->aps_psiz; 125660852Sdarrenr } 1257173181Sdarrenr if (getlock) { 1258173181Sdarrenr RWLOCK_EXIT(&ipf_nat); 1259173181Sdarrenr } 126060852Sdarrenr 1261170268Sdarrenr if (BCOPYOUT(&ng, data, sizeof(ng)) != 0) 1262170268Sdarrenr return EFAULT; 1263145522Sdarrenr return 0; 126460852Sdarrenr} 126560852Sdarrenr 126660852Sdarrenr 1267145522Sdarrenr/* ------------------------------------------------------------------------ */ 1268145522Sdarrenr/* Function: fr_natgetent */ 1269145522Sdarrenr/* Returns: int - 0 == success, != 0 is the error value. */ 1270145522Sdarrenr/* Parameters: data(I) - pointer to natget structure with kernel pointer */ 1271145522Sdarrenr/* to NAT structure to copy out. */ 1272145522Sdarrenr/* */ 1273145522Sdarrenr/* Handle SIOCSTGET. */ 1274145522Sdarrenr/* Copies out NAT entry to user space. Any additional data held for a */ 1275145522Sdarrenr/* proxy is also copied, as to is the NAT rule which was responsible for it */ 1276145522Sdarrenr/* ------------------------------------------------------------------------ */ 1277173181Sdarrenrstatic int fr_natgetent(data, getlock) 127860852Sdarrenrcaddr_t data; 1279173181Sdarrenrint getlock; 128060852Sdarrenr{ 1281145522Sdarrenr int error, outsize; 128260852Sdarrenr ap_session_t *aps; 1283145522Sdarrenr nat_save_t *ipn, ipns; 1284145522Sdarrenr nat_t *n, *nat; 128560852Sdarrenr 1286145522Sdarrenr error = fr_inobj(data, &ipns, IPFOBJ_NATSAVE); 1287145522Sdarrenr if (error != 0) 1288145522Sdarrenr return error; 128960852Sdarrenr 1290145522Sdarrenr if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) 1291145522Sdarrenr return EINVAL; 1292145522Sdarrenr 1293145522Sdarrenr KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize); 1294145522Sdarrenr if (ipn == NULL) 1295145522Sdarrenr return ENOMEM; 1296145522Sdarrenr 1297173181Sdarrenr if (getlock) { 1298173181Sdarrenr READ_ENTER(&ipf_nat); 1299173181Sdarrenr } 1300173181Sdarrenr 1301145522Sdarrenr ipn->ipn_dsize = ipns.ipn_dsize; 1302145522Sdarrenr nat = ipns.ipn_next; 1303145522Sdarrenr if (nat == NULL) { 130460852Sdarrenr nat = nat_instances; 130560852Sdarrenr if (nat == NULL) { 130660852Sdarrenr if (nat_instances == NULL) 1307145522Sdarrenr error = ENOENT; 1308145522Sdarrenr goto finished; 130960852Sdarrenr } 131060852Sdarrenr } else { 131160852Sdarrenr /* 131260852Sdarrenr * Make sure the pointer we're copying from exists in the 131360852Sdarrenr * current list of entries. Security precaution to prevent 131460852Sdarrenr * copying of random kernel data. 131560852Sdarrenr */ 131660852Sdarrenr for (n = nat_instances; n; n = n->nat_next) 131760852Sdarrenr if (n == nat) 131860852Sdarrenr break; 1319145522Sdarrenr if (n == NULL) { 1320145522Sdarrenr error = ESRCH; 1321145522Sdarrenr goto finished; 1322145522Sdarrenr } 132360852Sdarrenr } 1324145522Sdarrenr ipn->ipn_next = nat->nat_next; 132560852Sdarrenr 1326145522Sdarrenr /* 1327145522Sdarrenr * Copy the NAT structure. 1328145522Sdarrenr */ 1329145522Sdarrenr bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat)); 133060852Sdarrenr 1331145522Sdarrenr /* 1332145522Sdarrenr * If we have a pointer to the NAT rule it belongs to, save that too. 1333145522Sdarrenr */ 1334145522Sdarrenr if (nat->nat_ptr != NULL) 1335145522Sdarrenr bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat, 1336145522Sdarrenr sizeof(ipn->ipn_ipnat)); 133760852Sdarrenr 1338145522Sdarrenr /* 1339145522Sdarrenr * If we also know the NAT entry has an associated filter rule, 1340145522Sdarrenr * save that too. 1341145522Sdarrenr */ 1342145522Sdarrenr if (nat->nat_fr != NULL) 1343145522Sdarrenr bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr, 1344145522Sdarrenr sizeof(ipn->ipn_fr)); 134560852Sdarrenr 1346145522Sdarrenr /* 1347145522Sdarrenr * Last but not least, if there is an application proxy session set 1348145522Sdarrenr * up for this NAT entry, then copy that out too, including any 1349145522Sdarrenr * private data saved along side it by the proxy. 1350145522Sdarrenr */ 1351145522Sdarrenr aps = nat->nat_aps; 1352145522Sdarrenr outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data); 1353145522Sdarrenr if (aps != NULL) { 1354145522Sdarrenr char *s; 135560852Sdarrenr 1356145522Sdarrenr if (outsize < sizeof(*aps)) { 1357145522Sdarrenr error = ENOBUFS; 1358145522Sdarrenr goto finished; 135960852Sdarrenr } 1360145522Sdarrenr 1361145522Sdarrenr s = ipn->ipn_data; 1362145522Sdarrenr bcopy((char *)aps, s, sizeof(*aps)); 1363145522Sdarrenr s += sizeof(*aps); 1364145522Sdarrenr outsize -= sizeof(*aps); 1365145522Sdarrenr if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz)) 1366145522Sdarrenr bcopy(aps->aps_data, s, aps->aps_psiz); 1367145522Sdarrenr else 1368145522Sdarrenr error = ENOBUFS; 136960852Sdarrenr } 1370145522Sdarrenr if (error == 0) { 1371173181Sdarrenr if (getlock) { 1372173181Sdarrenr RWLOCK_EXIT(&ipf_nat); 1373173181Sdarrenr getlock = 0; 1374173181Sdarrenr } 1375145522Sdarrenr error = fr_outobjsz(data, ipn, IPFOBJ_NATSAVE, ipns.ipn_dsize); 1376145522Sdarrenr } 1377145522Sdarrenr 1378145522Sdarrenrfinished: 1379173181Sdarrenr if (getlock) { 1380173181Sdarrenr RWLOCK_EXIT(&ipf_nat); 1381173181Sdarrenr } 1382145522Sdarrenr if (ipn != NULL) { 1383145522Sdarrenr KFREES(ipn, ipns.ipn_dsize); 1384145522Sdarrenr } 138564580Sdarrenr return error; 138660852Sdarrenr} 138760852Sdarrenr 138860852Sdarrenr 1389145522Sdarrenr/* ------------------------------------------------------------------------ */ 1390145522Sdarrenr/* Function: fr_natputent */ 1391145522Sdarrenr/* Returns: int - 0 == success, != 0 is the error value. */ 1392145522Sdarrenr/* Parameters: data(I) - pointer to natget structure with NAT */ 1393145522Sdarrenr/* structure information to load into the kernel */ 1394145522Sdarrenr/* getlock(I) - flag indicating whether or not a write lock */ 1395145522Sdarrenr/* on ipf_nat is already held. */ 1396145522Sdarrenr/* */ 1397145522Sdarrenr/* Handle SIOCSTPUT. */ 1398145522Sdarrenr/* Loads a NAT table entry from user space, including a NAT rule, proxy and */ 1399145522Sdarrenr/* firewall rule data structures, if pointers to them indicate so. */ 1400145522Sdarrenr/* ------------------------------------------------------------------------ */ 1401145522Sdarrenrstatic int fr_natputent(data, getlock) 140260852Sdarrenrcaddr_t data; 1403145522Sdarrenrint getlock; 140460852Sdarrenr{ 1405145522Sdarrenr nat_save_t ipn, *ipnn; 140660852Sdarrenr ap_session_t *aps; 1407145522Sdarrenr nat_t *n, *nat; 140860852Sdarrenr frentry_t *fr; 1409145522Sdarrenr fr_info_t fin; 141060852Sdarrenr ipnat_t *in; 141160852Sdarrenr int error; 141260852Sdarrenr 1413145522Sdarrenr error = fr_inobj(data, &ipn, IPFOBJ_NATSAVE); 1414145522Sdarrenr if (error != 0) 1415145522Sdarrenr return error; 1416145522Sdarrenr 1417145522Sdarrenr /* 1418145522Sdarrenr * Initialise early because of code at junkput label. 1419145522Sdarrenr */ 1420145522Sdarrenr in = NULL; 1421145522Sdarrenr aps = NULL; 142264580Sdarrenr nat = NULL; 1423145522Sdarrenr ipnn = NULL; 1424170268Sdarrenr fr = NULL; 1425145522Sdarrenr 1426145522Sdarrenr /* 1427145522Sdarrenr * New entry, copy in the rest of the NAT entry if it's size is more 1428145522Sdarrenr * than just the nat_t structure. 1429145522Sdarrenr */ 1430145522Sdarrenr if (ipn.ipn_dsize > sizeof(ipn)) { 1431145522Sdarrenr if (ipn.ipn_dsize > 81920) { 1432145522Sdarrenr error = ENOMEM; 1433145522Sdarrenr goto junkput; 1434145522Sdarrenr } 1435145522Sdarrenr 1436145522Sdarrenr KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize); 143760852Sdarrenr if (ipnn == NULL) 143860852Sdarrenr return ENOMEM; 1439145522Sdarrenr 1440145522Sdarrenr error = fr_inobjsz(data, ipnn, IPFOBJ_NATSAVE, ipn.ipn_dsize); 1441145522Sdarrenr if (error != 0) { 144264580Sdarrenr error = EFAULT; 144364580Sdarrenr goto junkput; 144464580Sdarrenr } 144560852Sdarrenr } else 1446145522Sdarrenr ipnn = &ipn; 144760852Sdarrenr 144860852Sdarrenr KMALLOC(nat, nat_t *); 144964580Sdarrenr if (nat == NULL) { 1450145522Sdarrenr error = ENOMEM; 145164580Sdarrenr goto junkput; 145264580Sdarrenr } 145360852Sdarrenr 1454145522Sdarrenr bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat)); 145560852Sdarrenr /* 145660852Sdarrenr * Initialize all these so that nat_delete() doesn't cause a crash. 145760852Sdarrenr */ 1458145522Sdarrenr bzero((char *)nat, offsetof(struct nat, nat_tqe)); 1459145522Sdarrenr nat->nat_tqe.tqe_pnext = NULL; 1460145522Sdarrenr nat->nat_tqe.tqe_next = NULL; 1461145522Sdarrenr nat->nat_tqe.tqe_ifq = NULL; 1462145522Sdarrenr nat->nat_tqe.tqe_parent = nat; 146360852Sdarrenr 146460852Sdarrenr /* 146560852Sdarrenr * Restore the rule associated with this nat session 146660852Sdarrenr */ 1467145522Sdarrenr in = ipnn->ipn_nat.nat_ptr; 1468145522Sdarrenr if (in != NULL) { 146960852Sdarrenr KMALLOC(in, ipnat_t *); 1470145522Sdarrenr nat->nat_ptr = in; 147160852Sdarrenr if (in == NULL) { 147260852Sdarrenr error = ENOMEM; 147360852Sdarrenr goto junkput; 147460852Sdarrenr } 1475145522Sdarrenr bzero((char *)in, offsetof(struct ipnat, in_next6)); 1476145522Sdarrenr bcopy((char *)&ipnn->ipn_ipnat, (char *)in, sizeof(*in)); 147760852Sdarrenr in->in_use = 1; 147860852Sdarrenr in->in_flags |= IPN_DELETE; 1479145522Sdarrenr 1480145522Sdarrenr ATOMIC_INC(nat_stats.ns_rules); 1481145522Sdarrenr 1482161356Sguido if (nat_resolverule(in) != 0) { 1483161356Sguido error = ESRCH; 1484161356Sguido goto junkput; 1485161356Sguido } 1486145522Sdarrenr } 1487145522Sdarrenr 1488145522Sdarrenr /* 1489145522Sdarrenr * Check that the NAT entry doesn't already exist in the kernel. 1490161356Sguido * 1491161356Sguido * For NAT_OUTBOUND, we're lookup for a duplicate MAP entry. To do 1492161356Sguido * this, we check to see if the inbound combination of addresses and 1493161356Sguido * ports is already known. Similar logic is applied for NAT_INBOUND. 1494161356Sguido * 1495145522Sdarrenr */ 1496145522Sdarrenr bzero((char *)&fin, sizeof(fin)); 1497145522Sdarrenr fin.fin_p = nat->nat_p; 1498145522Sdarrenr if (nat->nat_dir == NAT_OUTBOUND) { 1499170268Sdarrenr fin.fin_ifp = nat->nat_ifps[0]; 1500145522Sdarrenr fin.fin_data[0] = ntohs(nat->nat_oport); 1501145522Sdarrenr fin.fin_data[1] = ntohs(nat->nat_outport); 1502153876Sguido if (getlock) { 1503153876Sguido READ_ENTER(&ipf_nat); 1504153876Sguido } 1505161356Sguido n = nat_inlookup(&fin, nat->nat_flags, fin.fin_p, 1506161356Sguido nat->nat_oip, nat->nat_inip); 1507153876Sguido if (getlock) { 1508153876Sguido RWLOCK_EXIT(&ipf_nat); 1509153876Sguido } 1510153876Sguido if (n != NULL) { 1511145522Sdarrenr error = EEXIST; 1512145522Sdarrenr goto junkput; 151360852Sdarrenr } 1514145522Sdarrenr } else if (nat->nat_dir == NAT_INBOUND) { 1515170268Sdarrenr fin.fin_ifp = nat->nat_ifps[0]; 1516145522Sdarrenr fin.fin_data[0] = ntohs(nat->nat_outport); 1517145522Sdarrenr fin.fin_data[1] = ntohs(nat->nat_oport); 1518153876Sguido if (getlock) { 1519153876Sguido READ_ENTER(&ipf_nat); 1520153876Sguido } 1521161356Sguido n = nat_outlookup(&fin, nat->nat_flags, fin.fin_p, 1522161356Sguido nat->nat_outip, nat->nat_oip); 1523153876Sguido if (getlock) { 1524153876Sguido RWLOCK_EXIT(&ipf_nat); 1525153876Sguido } 1526153876Sguido if (n != NULL) { 1527145522Sdarrenr error = EEXIST; 1528145522Sdarrenr goto junkput; 1529145522Sdarrenr } 1530145522Sdarrenr } else { 1531145522Sdarrenr error = EINVAL; 1532145522Sdarrenr goto junkput; 153360852Sdarrenr } 153460852Sdarrenr 153560852Sdarrenr /* 153660852Sdarrenr * Restore ap_session_t structure. Include the private data allocated 153760852Sdarrenr * if it was there. 153860852Sdarrenr */ 1539145522Sdarrenr aps = nat->nat_aps; 1540145522Sdarrenr if (aps != NULL) { 154160852Sdarrenr KMALLOC(aps, ap_session_t *); 1542145522Sdarrenr nat->nat_aps = aps; 154360852Sdarrenr if (aps == NULL) { 154460852Sdarrenr error = ENOMEM; 154560852Sdarrenr goto junkput; 154660852Sdarrenr } 154760852Sdarrenr bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps)); 1548145522Sdarrenr if (in != NULL) 154960852Sdarrenr aps->aps_apr = in->in_apr; 1550145522Sdarrenr else 1551145522Sdarrenr aps->aps_apr = NULL; 1552145522Sdarrenr if (aps->aps_psiz != 0) { 1553145522Sdarrenr if (aps->aps_psiz > 81920) { 1554145522Sdarrenr error = ENOMEM; 1555145522Sdarrenr goto junkput; 1556145522Sdarrenr } 155760852Sdarrenr KMALLOCS(aps->aps_data, void *, aps->aps_psiz); 155860852Sdarrenr if (aps->aps_data == NULL) { 155960852Sdarrenr error = ENOMEM; 156060852Sdarrenr goto junkput; 156160852Sdarrenr } 156260852Sdarrenr bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data, 156360852Sdarrenr aps->aps_psiz); 156460852Sdarrenr } else { 156560852Sdarrenr aps->aps_psiz = 0; 156660852Sdarrenr aps->aps_data = NULL; 156760852Sdarrenr } 156860852Sdarrenr } 156960852Sdarrenr 157060852Sdarrenr /* 157160852Sdarrenr * If there was a filtering rule associated with this entry then 157260852Sdarrenr * build up a new one. 157360852Sdarrenr */ 1574145522Sdarrenr fr = nat->nat_fr; 157560852Sdarrenr if (fr != NULL) { 1576145522Sdarrenr if ((nat->nat_flags & SI_NEWFR) != 0) { 157760852Sdarrenr KMALLOC(fr, frentry_t *); 157860852Sdarrenr nat->nat_fr = fr; 157960852Sdarrenr if (fr == NULL) { 158060852Sdarrenr error = ENOMEM; 158160852Sdarrenr goto junkput; 158260852Sdarrenr } 1583145522Sdarrenr ipnn->ipn_nat.nat_fr = fr; 1584145522Sdarrenr fr->fr_ref = 1; 1585145522Sdarrenr (void) fr_outobj(data, ipnn, IPFOBJ_NATSAVE); 1586145522Sdarrenr bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr)); 1587161356Sguido 1588161356Sguido fr->fr_ref = 1; 1589161356Sguido fr->fr_dsize = 0; 1590161356Sguido fr->fr_data = NULL; 1591161356Sguido fr->fr_type = FR_T_NONE; 1592161356Sguido 1593145522Sdarrenr MUTEX_NUKE(&fr->fr_lock); 1594145522Sdarrenr MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock"); 159560852Sdarrenr } else { 1596153876Sguido if (getlock) { 1597153876Sguido READ_ENTER(&ipf_nat); 1598153876Sguido } 159960852Sdarrenr for (n = nat_instances; n; n = n->nat_next) 160060852Sdarrenr if (n->nat_fr == fr) 160160852Sdarrenr break; 1602145522Sdarrenr 1603145522Sdarrenr if (n != NULL) { 1604145522Sdarrenr MUTEX_ENTER(&fr->fr_lock); 1605145522Sdarrenr fr->fr_ref++; 1606145522Sdarrenr MUTEX_EXIT(&fr->fr_lock); 1607145522Sdarrenr } 1608153876Sguido if (getlock) { 1609153876Sguido RWLOCK_EXIT(&ipf_nat); 1610153876Sguido } 1611145522Sdarrenr 161260852Sdarrenr if (!n) { 161360852Sdarrenr error = ESRCH; 161460852Sdarrenr goto junkput; 161560852Sdarrenr } 161660852Sdarrenr } 161760852Sdarrenr } 161860852Sdarrenr 1619145522Sdarrenr if (ipnn != &ipn) { 1620145522Sdarrenr KFREES(ipnn, ipn.ipn_dsize); 1621145522Sdarrenr ipnn = NULL; 1622145522Sdarrenr } 1623145522Sdarrenr 1624145522Sdarrenr if (getlock) { 1625145522Sdarrenr WRITE_ENTER(&ipf_nat); 1626145522Sdarrenr } 1627145522Sdarrenr error = nat_insert(nat, nat->nat_rev); 1628145522Sdarrenr if ((error == 0) && (aps != NULL)) { 1629145522Sdarrenr aps->aps_next = ap_sess_list; 1630145522Sdarrenr ap_sess_list = aps; 1631145522Sdarrenr } 1632145522Sdarrenr if (getlock) { 1633145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 1634145522Sdarrenr } 1635145522Sdarrenr 1636145522Sdarrenr if (error == 0) 1637145522Sdarrenr return 0; 1638145522Sdarrenr 1639145522Sdarrenr error = ENOMEM; 1640145522Sdarrenr 164160852Sdarrenrjunkput: 1642145522Sdarrenr if (fr != NULL) 1643170268Sdarrenr (void) fr_derefrule(&fr); 1644145522Sdarrenr 1645145522Sdarrenr if ((ipnn != NULL) && (ipnn != &ipn)) { 1646145522Sdarrenr KFREES(ipnn, ipn.ipn_dsize); 1647145522Sdarrenr } 1648145522Sdarrenr if (nat != NULL) { 1649145522Sdarrenr if (aps != NULL) { 1650145522Sdarrenr if (aps->aps_data != NULL) { 1651145522Sdarrenr KFREES(aps->aps_data, aps->aps_psiz); 1652145522Sdarrenr } 1653145522Sdarrenr KFREE(aps); 1654145522Sdarrenr } 1655145522Sdarrenr if (in != NULL) { 1656145522Sdarrenr if (in->in_apr) 1657145522Sdarrenr appr_free(in->in_apr); 1658145522Sdarrenr KFREE(in); 1659145522Sdarrenr } 1660145522Sdarrenr KFREE(nat); 1661145522Sdarrenr } 166260852Sdarrenr return error; 166360852Sdarrenr} 166460852Sdarrenr 166560852Sdarrenr 1666145522Sdarrenr/* ------------------------------------------------------------------------ */ 1667145522Sdarrenr/* Function: nat_delete */ 1668145522Sdarrenr/* Returns: Nil */ 1669145522Sdarrenr/* Parameters: natd(I) - pointer to NAT structure to delete */ 1670145522Sdarrenr/* logtype(I) - type of LOG record to create before deleting */ 1671145522Sdarrenr/* Write Lock: ipf_nat */ 1672145522Sdarrenr/* */ 1673145522Sdarrenr/* Delete a nat entry from the various lists and table. If NAT logging is */ 1674145522Sdarrenr/* enabled then generate a NAT log record for this event. */ 1675145522Sdarrenr/* ------------------------------------------------------------------------ */ 1676172776Sdarrenrvoid nat_delete(nat, logtype) 1677145522Sdarrenrstruct nat *nat; 1678145522Sdarrenrint logtype; 167953642Sguido{ 168053642Sguido struct ipnat *ipn; 1681172776Sdarrenr int removed = 0; 168253642Sguido 1683145522Sdarrenr if (logtype != 0 && nat_logging != 0) 1684145522Sdarrenr nat_log(nat, logtype); 1685180778Sdarrenr#if defined(NEED_LOCAL_RAND) && defined(_KERNEL) 1686180778Sdarrenr ipf_rand_push(nat, sizeof(*nat)); 1687180778Sdarrenr#endif 168853642Sguido 1689145522Sdarrenr /* 1690145522Sdarrenr * Take it as a general indication that all the pointers are set if 1691145522Sdarrenr * nat_pnext is set. 1692145522Sdarrenr */ 1693145522Sdarrenr if (nat->nat_pnext != NULL) { 1694172776Sdarrenr removed = 1; 1695172776Sdarrenr 1696145522Sdarrenr nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--; 1697145522Sdarrenr nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--; 1698145522Sdarrenr 1699145522Sdarrenr *nat->nat_pnext = nat->nat_next; 1700145522Sdarrenr if (nat->nat_next != NULL) { 1701145522Sdarrenr nat->nat_next->nat_pnext = nat->nat_pnext; 1702145522Sdarrenr nat->nat_next = NULL; 1703145522Sdarrenr } 1704145522Sdarrenr nat->nat_pnext = NULL; 1705145522Sdarrenr 1706145522Sdarrenr *nat->nat_phnext[0] = nat->nat_hnext[0]; 1707145522Sdarrenr if (nat->nat_hnext[0] != NULL) { 1708145522Sdarrenr nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 1709145522Sdarrenr nat->nat_hnext[0] = NULL; 1710145522Sdarrenr } 1711145522Sdarrenr nat->nat_phnext[0] = NULL; 1712145522Sdarrenr 1713145522Sdarrenr *nat->nat_phnext[1] = nat->nat_hnext[1]; 1714145522Sdarrenr if (nat->nat_hnext[1] != NULL) { 1715145522Sdarrenr nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 1716145522Sdarrenr nat->nat_hnext[1] = NULL; 1717145522Sdarrenr } 1718145522Sdarrenr nat->nat_phnext[1] = NULL; 1719145522Sdarrenr 1720145522Sdarrenr if ((nat->nat_flags & SI_WILDP) != 0) 1721145522Sdarrenr nat_stats.ns_wilds--; 172253642Sguido } 172360852Sdarrenr 1724145522Sdarrenr if (nat->nat_me != NULL) { 1725145522Sdarrenr *nat->nat_me = NULL; 1726145522Sdarrenr nat->nat_me = NULL; 1727145522Sdarrenr } 172860852Sdarrenr 1729170268Sdarrenr if (nat->nat_tqe.tqe_ifq != NULL) 1730170268Sdarrenr fr_deletequeueentry(&nat->nat_tqe); 1731145522Sdarrenr 1732170268Sdarrenr if (logtype == NL_EXPIRE) 1733170268Sdarrenr nat_stats.ns_expire++; 1734170268Sdarrenr 1735172776Sdarrenr MUTEX_ENTER(&nat->nat_lock); 1736172776Sdarrenr /* 1737172776Sdarrenr * NL_DESTROY should only be passed in when we've got nat_ref >= 2. 1738172776Sdarrenr * This happens when a nat'd packet is blocked and we want to throw 1739172776Sdarrenr * away the NAT session. 1740172776Sdarrenr */ 1741172776Sdarrenr if (logtype == NL_DESTROY) { 1742172776Sdarrenr if (nat->nat_ref > 2) { 1743172776Sdarrenr nat->nat_ref -= 2; 1744172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 1745172776Sdarrenr if (removed) 1746172776Sdarrenr nat_stats.ns_orphans++; 1747172776Sdarrenr return; 1748172776Sdarrenr } 1749172776Sdarrenr } else if (nat->nat_ref > 1) { 1750172776Sdarrenr nat->nat_ref--; 1751172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 1752172776Sdarrenr if (removed) 1753172776Sdarrenr nat_stats.ns_orphans++; 1754145522Sdarrenr return; 1755145522Sdarrenr } 1756172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 1757170268Sdarrenr 1758161356Sguido /* 1759172776Sdarrenr * At this point, nat_ref is 1, doing "--" would make it 0.. 1760161356Sguido */ 1761172776Sdarrenr nat->nat_ref = 0; 1762172776Sdarrenr if (!removed) 1763172776Sdarrenr nat_stats.ns_orphans--; 1764145522Sdarrenr 1765145522Sdarrenr#ifdef IPFILTER_SYNC 1766145522Sdarrenr if (nat->nat_sync) 1767145522Sdarrenr ipfsync_del(nat->nat_sync); 1768145522Sdarrenr#endif 1769145522Sdarrenr 1770145522Sdarrenr if (nat->nat_fr != NULL) 1771170268Sdarrenr (void) fr_derefrule(&nat->nat_fr); 1772145522Sdarrenr 1773145522Sdarrenr if (nat->nat_hm != NULL) 1774170268Sdarrenr fr_hostmapdel(&nat->nat_hm); 1775145522Sdarrenr 177653642Sguido /* 177753642Sguido * If there is an active reference from the nat entry to its parent 177853642Sguido * rule, decrement the rule's reference count and free it too if no 177953642Sguido * longer being used. 178053642Sguido */ 1781145522Sdarrenr ipn = nat->nat_ptr; 178253642Sguido if (ipn != NULL) { 1783170268Sdarrenr fr_ipnatderef(&ipn); 178453642Sguido } 178553642Sguido 1786145522Sdarrenr MUTEX_DESTROY(&nat->nat_lock); 1787145522Sdarrenr 1788145522Sdarrenr aps_free(nat->nat_aps); 1789145522Sdarrenr nat_stats.ns_inuse--; 1790145522Sdarrenr 179153642Sguido /* 179253642Sguido * If there's a fragment table entry too for this nat entry, then 1793145522Sdarrenr * dereference that as well. This is after nat_lock is released 1794145522Sdarrenr * because of Tru64. 179553642Sguido */ 1796145522Sdarrenr fr_forgetnat((void *)nat); 1797145522Sdarrenr 1798145522Sdarrenr KFREE(nat); 179953642Sguido} 180053642Sguido 180153642Sguido 1802145522Sdarrenr/* ------------------------------------------------------------------------ */ 1803145522Sdarrenr/* Function: nat_flushtable */ 1804145522Sdarrenr/* Returns: int - number of NAT rules deleted */ 1805145522Sdarrenr/* Parameters: Nil */ 1806145522Sdarrenr/* */ 1807145522Sdarrenr/* Deletes all currently active NAT sessions. In deleting each NAT entry a */ 1808145522Sdarrenr/* log record should be emitted in nat_delete() if NAT logging is enabled. */ 1809145522Sdarrenr/* ------------------------------------------------------------------------ */ 181053642Sguido/* 181153642Sguido * nat_flushtable - clear the NAT table of all mapping entries. 181253642Sguido */ 181353642Sguidostatic int nat_flushtable() 181453642Sguido{ 1815145522Sdarrenr nat_t *nat; 1816145522Sdarrenr int j = 0; 181767614Sdarrenr 181853642Sguido /* 181953642Sguido * ALL NAT mappings deleted, so lets just make the deletions 182053642Sguido * quicker. 182153642Sguido */ 182253642Sguido if (nat_table[0] != NULL) 182353642Sguido bzero((char *)nat_table[0], 182453642Sguido sizeof(nat_table[0]) * ipf_nattable_sz); 182553642Sguido if (nat_table[1] != NULL) 182653642Sguido bzero((char *)nat_table[1], 182753642Sguido sizeof(nat_table[1]) * ipf_nattable_sz); 182853642Sguido 1829145522Sdarrenr while ((nat = nat_instances) != NULL) { 1830145522Sdarrenr nat_delete(nat, NL_FLUSH); 183153642Sguido j++; 183253642Sguido } 1833145522Sdarrenr 183453642Sguido nat_stats.ns_inuse = 0; 183553642Sguido return j; 183653642Sguido} 183753642Sguido 183853642Sguido 1839145522Sdarrenr/* ------------------------------------------------------------------------ */ 1840145522Sdarrenr/* Function: nat_clearlist */ 1841145522Sdarrenr/* Returns: int - number of NAT/RDR rules deleted */ 1842145522Sdarrenr/* Parameters: Nil */ 1843145522Sdarrenr/* */ 1844145522Sdarrenr/* Delete all rules in the current list of rules. There is nothing elegant */ 1845145522Sdarrenr/* about this cleanup: simply free all entries on the list of rules and */ 1846145522Sdarrenr/* clear out the tables used for hashed NAT rule lookups. */ 1847145522Sdarrenr/* ------------------------------------------------------------------------ */ 1848145522Sdarrenrstatic int nat_clearlist() 184953642Sguido{ 1850145522Sdarrenr ipnat_t *n, **np = &nat_list; 185153642Sguido int i = 0; 185253642Sguido 185353642Sguido if (nat_rules != NULL) 185453642Sguido bzero((char *)nat_rules, sizeof(*nat_rules) * ipf_natrules_sz); 185553642Sguido if (rdr_rules != NULL) 185653642Sguido bzero((char *)rdr_rules, sizeof(*rdr_rules) * ipf_rdrrules_sz); 185753642Sguido 1858145522Sdarrenr while ((n = *np) != NULL) { 185953642Sguido *np = n->in_next; 1860145522Sdarrenr if (n->in_use == 0) { 1861145522Sdarrenr if (n->in_apr != NULL) 186253642Sguido appr_free(n->in_apr); 1863172776Sdarrenr MUTEX_DESTROY(&n->in_lock); 186453642Sguido KFREE(n); 186553642Sguido nat_stats.ns_rules--; 186653642Sguido } else { 186753642Sguido n->in_flags |= IPN_DELETE; 186853642Sguido n->in_next = NULL; 186953642Sguido } 187053642Sguido i++; 187153642Sguido } 1872172776Sdarrenr#if SOLARIS && !defined(_INET_IP_STACK_H) 1873145522Sdarrenr pfil_delayed_copy = 1; 1874145522Sdarrenr#endif 187553642Sguido nat_masks = 0; 187653642Sguido rdr_masks = 0; 187753642Sguido return i; 187853642Sguido} 187953642Sguido 188053642Sguido 1881145522Sdarrenr/* ------------------------------------------------------------------------ */ 1882145522Sdarrenr/* Function: nat_newmap */ 1883145522Sdarrenr/* Returns: int - -1 == error, 0 == success */ 1884145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 1885145522Sdarrenr/* nat(I) - pointer to NAT entry */ 1886145522Sdarrenr/* ni(I) - pointer to structure with misc. information needed */ 1887145522Sdarrenr/* to create new NAT entry. */ 1888145522Sdarrenr/* */ 1889145522Sdarrenr/* Given an empty NAT structure, populate it with new information about a */ 1890145522Sdarrenr/* new NAT session, as defined by the matching NAT rule. */ 1891145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 1892145522Sdarrenr/* to the new IP address for the translation. */ 1893145522Sdarrenr/* ------------------------------------------------------------------------ */ 1894145522Sdarrenrstatic INLINE int nat_newmap(fin, nat, ni) 189592685Sdarrenrfr_info_t *fin; 1896145522Sdarrenrnat_t *nat; 1897145522Sdarrenrnatinfo_t *ni; 1898145522Sdarrenr{ 1899145522Sdarrenr u_short st_port, dport, sport, port, sp, dp; 1900145522Sdarrenr struct in_addr in, inb; 1901145522Sdarrenr hostmap_t *hm; 1902145522Sdarrenr u_32_t flags; 1903145522Sdarrenr u_32_t st_ip; 1904145522Sdarrenr ipnat_t *np; 1905145522Sdarrenr nat_t *natl; 1906145522Sdarrenr int l; 1907145522Sdarrenr 1908145522Sdarrenr /* 1909145522Sdarrenr * If it's an outbound packet which doesn't match any existing 1910145522Sdarrenr * record, then create a new port 1911145522Sdarrenr */ 1912145522Sdarrenr l = 0; 1913145522Sdarrenr hm = NULL; 1914145522Sdarrenr np = ni->nai_np; 1915145522Sdarrenr st_ip = np->in_nip; 1916145522Sdarrenr st_port = np->in_pnext; 1917145522Sdarrenr flags = ni->nai_flags; 1918145522Sdarrenr sport = ni->nai_sport; 1919145522Sdarrenr dport = ni->nai_dport; 1920145522Sdarrenr 1921145522Sdarrenr /* 1922145522Sdarrenr * Do a loop until we either run out of entries to try or we find 1923145522Sdarrenr * a NAT mapping that isn't currently being used. This is done 1924145522Sdarrenr * because the change to the source is not (usually) being fixed. 1925145522Sdarrenr */ 1926145522Sdarrenr do { 1927145522Sdarrenr port = 0; 1928145522Sdarrenr in.s_addr = htonl(np->in_nip); 1929145522Sdarrenr if (l == 0) { 1930145522Sdarrenr /* 1931145522Sdarrenr * Check to see if there is an existing NAT 1932145522Sdarrenr * setup for this IP address pair. 1933145522Sdarrenr */ 1934145522Sdarrenr hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, 1935145522Sdarrenr in, 0); 1936145522Sdarrenr if (hm != NULL) 1937145522Sdarrenr in.s_addr = hm->hm_mapip.s_addr; 1938145522Sdarrenr } else if ((l == 1) && (hm != NULL)) { 1939170268Sdarrenr fr_hostmapdel(&hm); 1940145522Sdarrenr } 1941145522Sdarrenr in.s_addr = ntohl(in.s_addr); 1942145522Sdarrenr 1943145522Sdarrenr nat->nat_hm = hm; 1944145522Sdarrenr 1945145522Sdarrenr if ((np->in_outmsk == 0xffffffff) && (np->in_pnext == 0)) { 1946145522Sdarrenr if (l > 0) 1947145522Sdarrenr return -1; 1948145522Sdarrenr } 1949145522Sdarrenr 1950145522Sdarrenr if (np->in_redir == NAT_BIMAP && 1951145522Sdarrenr np->in_inmsk == np->in_outmsk) { 1952145522Sdarrenr /* 1953145522Sdarrenr * map the address block in a 1:1 fashion 1954145522Sdarrenr */ 1955145522Sdarrenr in.s_addr = np->in_outip; 1956145522Sdarrenr in.s_addr |= fin->fin_saddr & ~np->in_inmsk; 1957145522Sdarrenr in.s_addr = ntohl(in.s_addr); 1958145522Sdarrenr 1959145522Sdarrenr } else if (np->in_redir & NAT_MAPBLK) { 1960145522Sdarrenr if ((l >= np->in_ppip) || ((l > 0) && 1961145522Sdarrenr !(flags & IPN_TCPUDP))) 1962145522Sdarrenr return -1; 1963145522Sdarrenr /* 1964145522Sdarrenr * map-block - Calculate destination address. 1965145522Sdarrenr */ 1966145522Sdarrenr in.s_addr = ntohl(fin->fin_saddr); 1967145522Sdarrenr in.s_addr &= ntohl(~np->in_inmsk); 1968145522Sdarrenr inb.s_addr = in.s_addr; 1969145522Sdarrenr in.s_addr /= np->in_ippip; 1970145522Sdarrenr in.s_addr &= ntohl(~np->in_outmsk); 1971145522Sdarrenr in.s_addr += ntohl(np->in_outip); 1972145522Sdarrenr /* 1973145522Sdarrenr * Calculate destination port. 1974145522Sdarrenr */ 1975145522Sdarrenr if ((flags & IPN_TCPUDP) && 1976145522Sdarrenr (np->in_ppip != 0)) { 1977145522Sdarrenr port = ntohs(sport) + l; 1978145522Sdarrenr port %= np->in_ppip; 1979145522Sdarrenr port += np->in_ppip * 1980145522Sdarrenr (inb.s_addr % np->in_ippip); 1981145522Sdarrenr port += MAPBLK_MINPORT; 1982145522Sdarrenr port = htons(port); 1983145522Sdarrenr } 1984145522Sdarrenr 1985145522Sdarrenr } else if ((np->in_outip == 0) && 1986145522Sdarrenr (np->in_outmsk == 0xffffffff)) { 1987145522Sdarrenr /* 1988145522Sdarrenr * 0/32 - use the interface's IP address. 1989145522Sdarrenr */ 1990145522Sdarrenr if ((l > 0) || 1991145522Sdarrenr fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp, 1992145522Sdarrenr &in, NULL) == -1) 1993145522Sdarrenr return -1; 1994145522Sdarrenr in.s_addr = ntohl(in.s_addr); 1995145522Sdarrenr 1996145522Sdarrenr } else if ((np->in_outip == 0) && (np->in_outmsk == 0)) { 1997145522Sdarrenr /* 1998145522Sdarrenr * 0/0 - use the original source address/port. 1999145522Sdarrenr */ 2000145522Sdarrenr if (l > 0) 2001145522Sdarrenr return -1; 2002145522Sdarrenr in.s_addr = ntohl(fin->fin_saddr); 2003145522Sdarrenr 2004145522Sdarrenr } else if ((np->in_outmsk != 0xffffffff) && 2005145522Sdarrenr (np->in_pnext == 0) && ((l > 0) || (hm == NULL))) 2006145522Sdarrenr np->in_nip++; 2007145522Sdarrenr 2008145522Sdarrenr natl = NULL; 2009145522Sdarrenr 2010145522Sdarrenr if ((flags & IPN_TCPUDP) && 2011145522Sdarrenr ((np->in_redir & NAT_MAPBLK) == 0) && 2012145522Sdarrenr (np->in_flags & IPN_AUTOPORTMAP)) { 2013145522Sdarrenr /* 2014145522Sdarrenr * "ports auto" (without map-block) 2015145522Sdarrenr */ 2016145522Sdarrenr if ((l > 0) && (l % np->in_ppip == 0)) { 2017145522Sdarrenr if (l > np->in_space) { 2018145522Sdarrenr return -1; 2019145522Sdarrenr } else if ((l > np->in_ppip) && 2020145522Sdarrenr np->in_outmsk != 0xffffffff) 2021145522Sdarrenr np->in_nip++; 2022145522Sdarrenr } 2023145522Sdarrenr if (np->in_ppip != 0) { 2024145522Sdarrenr port = ntohs(sport); 2025145522Sdarrenr port += (l % np->in_ppip); 2026145522Sdarrenr port %= np->in_ppip; 2027145522Sdarrenr port += np->in_ppip * 2028145522Sdarrenr (ntohl(fin->fin_saddr) % 2029145522Sdarrenr np->in_ippip); 2030145522Sdarrenr port += MAPBLK_MINPORT; 2031145522Sdarrenr port = htons(port); 2032145522Sdarrenr } 2033145522Sdarrenr 2034145522Sdarrenr } else if (((np->in_redir & NAT_MAPBLK) == 0) && 2035145522Sdarrenr (flags & IPN_TCPUDPICMP) && (np->in_pnext != 0)) { 2036145522Sdarrenr /* 2037145522Sdarrenr * Standard port translation. Select next port. 2038145522Sdarrenr */ 2039180778Sdarrenr if (np->in_flags & IPN_SEQUENTIAL) { 2040180832Sdarrenr port = np->in_pnext; 2041180778Sdarrenr } else { 2042180778Sdarrenr port = ipf_random() % (ntohs(np->in_pmax) - 2043180778Sdarrenr ntohs(np->in_pmin)); 2044180832Sdarrenr port += ntohs(np->in_pmin); 2045180778Sdarrenr } 2046180832Sdarrenr port = htons(port); 2047180778Sdarrenr np->in_pnext++; 2048145522Sdarrenr 2049145522Sdarrenr if (np->in_pnext > ntohs(np->in_pmax)) { 2050145522Sdarrenr np->in_pnext = ntohs(np->in_pmin); 2051145522Sdarrenr if (np->in_outmsk != 0xffffffff) 2052145522Sdarrenr np->in_nip++; 2053145522Sdarrenr } 2054145522Sdarrenr } 2055145522Sdarrenr 2056145522Sdarrenr if (np->in_flags & IPN_IPRANGE) { 2057145522Sdarrenr if (np->in_nip > ntohl(np->in_outmsk)) 2058145522Sdarrenr np->in_nip = ntohl(np->in_outip); 2059145522Sdarrenr } else { 2060145522Sdarrenr if ((np->in_outmsk != 0xffffffff) && 2061145522Sdarrenr ((np->in_nip + 1) & ntohl(np->in_outmsk)) > 2062145522Sdarrenr ntohl(np->in_outip)) 2063145522Sdarrenr np->in_nip = ntohl(np->in_outip) + 1; 2064145522Sdarrenr } 2065145522Sdarrenr 2066145522Sdarrenr if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) 2067145522Sdarrenr port = sport; 2068145522Sdarrenr 2069145522Sdarrenr /* 2070145522Sdarrenr * Here we do a lookup of the connection as seen from 2071145522Sdarrenr * the outside. If an IP# pair already exists, try 2072145522Sdarrenr * again. So if you have A->B becomes C->B, you can 2073145522Sdarrenr * also have D->E become C->E but not D->B causing 2074145522Sdarrenr * another C->B. Also take protocol and ports into 2075145522Sdarrenr * account when determining whether a pre-existing 2076145522Sdarrenr * NAT setup will cause an external conflict where 2077145522Sdarrenr * this is appropriate. 2078145522Sdarrenr */ 2079145522Sdarrenr inb.s_addr = htonl(in.s_addr); 2080145522Sdarrenr sp = fin->fin_data[0]; 2081145522Sdarrenr dp = fin->fin_data[1]; 2082145522Sdarrenr fin->fin_data[0] = fin->fin_data[1]; 2083145522Sdarrenr fin->fin_data[1] = htons(port); 2084145522Sdarrenr natl = nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 2085145522Sdarrenr (u_int)fin->fin_p, fin->fin_dst, inb); 2086145522Sdarrenr fin->fin_data[0] = sp; 2087145522Sdarrenr fin->fin_data[1] = dp; 2088145522Sdarrenr 2089145522Sdarrenr /* 2090145522Sdarrenr * Has the search wrapped around and come back to the 2091145522Sdarrenr * start ? 2092145522Sdarrenr */ 2093145522Sdarrenr if ((natl != NULL) && 2094145522Sdarrenr (np->in_pnext != 0) && (st_port == np->in_pnext) && 2095145522Sdarrenr (np->in_nip != 0) && (st_ip == np->in_nip)) 2096145522Sdarrenr return -1; 2097145522Sdarrenr l++; 2098145522Sdarrenr } while (natl != NULL); 2099145522Sdarrenr 2100145522Sdarrenr if (np->in_space > 0) 2101145522Sdarrenr np->in_space--; 2102145522Sdarrenr 2103145522Sdarrenr /* Setup the NAT table */ 2104145522Sdarrenr nat->nat_inip = fin->fin_src; 2105145522Sdarrenr nat->nat_outip.s_addr = htonl(in.s_addr); 2106145522Sdarrenr nat->nat_oip = fin->fin_dst; 2107145522Sdarrenr if (nat->nat_hm == NULL) 2108145522Sdarrenr nat->nat_hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, 2109145522Sdarrenr nat->nat_outip, 0); 2110145522Sdarrenr 2111145522Sdarrenr /* 2112145522Sdarrenr * The ICMP checksum does not have a pseudo header containing 2113145522Sdarrenr * the IP addresses 2114145522Sdarrenr */ 2115145522Sdarrenr ni->nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr)); 2116145522Sdarrenr ni->nai_sum2 = LONG_SUM(in.s_addr); 2117145522Sdarrenr if ((flags & IPN_TCPUDP)) { 2118145522Sdarrenr ni->nai_sum1 += ntohs(sport); 2119145522Sdarrenr ni->nai_sum2 += ntohs(port); 2120145522Sdarrenr } 2121145522Sdarrenr 2122145522Sdarrenr if (flags & IPN_TCPUDP) { 2123145522Sdarrenr nat->nat_inport = sport; 2124145522Sdarrenr nat->nat_outport = port; /* sport */ 2125145522Sdarrenr nat->nat_oport = dport; 2126145522Sdarrenr ((tcphdr_t *)fin->fin_dp)->th_sport = port; 2127145522Sdarrenr } else if (flags & IPN_ICMPQUERY) { 2128145522Sdarrenr ((icmphdr_t *)fin->fin_dp)->icmp_id = port; 2129145522Sdarrenr nat->nat_inport = port; 2130145522Sdarrenr nat->nat_outport = port; 2131145522Sdarrenr } else if (fin->fin_p == IPPROTO_GRE) { 2132145522Sdarrenr#if 0 2133145522Sdarrenr nat->nat_gre.gs_flags = ((grehdr_t *)fin->fin_dp)->gr_flags; 2134145522Sdarrenr if (GRE_REV(nat->nat_gre.gs_flags) == 1) { 2135145522Sdarrenr nat->nat_oport = 0;/*fin->fin_data[1];*/ 2136145522Sdarrenr nat->nat_inport = 0;/*fin->fin_data[0];*/ 2137145522Sdarrenr nat->nat_outport = 0;/*fin->fin_data[0];*/ 2138145522Sdarrenr nat->nat_call[0] = fin->fin_data[0]; 2139145522Sdarrenr nat->nat_call[1] = fin->fin_data[0]; 2140145522Sdarrenr } 2141145522Sdarrenr#endif 2142145522Sdarrenr } 2143145522Sdarrenr ni->nai_ip.s_addr = in.s_addr; 2144145522Sdarrenr ni->nai_port = port; 2145145522Sdarrenr ni->nai_nport = dport; 2146145522Sdarrenr return 0; 2147145522Sdarrenr} 2148145522Sdarrenr 2149145522Sdarrenr 2150145522Sdarrenr/* ------------------------------------------------------------------------ */ 2151145522Sdarrenr/* Function: nat_newrdr */ 2152145522Sdarrenr/* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ 2153145522Sdarrenr/* allow rule to be moved if IPN_ROUNDR is set. */ 2154145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2155145522Sdarrenr/* nat(I) - pointer to NAT entry */ 2156145522Sdarrenr/* ni(I) - pointer to structure with misc. information needed */ 2157145522Sdarrenr/* to create new NAT entry. */ 2158145522Sdarrenr/* */ 2159145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 2160145522Sdarrenr/* to the new IP address for the translation. */ 2161145522Sdarrenr/* ------------------------------------------------------------------------ */ 2162145522Sdarrenrstatic INLINE int nat_newrdr(fin, nat, ni) 2163145522Sdarrenrfr_info_t *fin; 2164145522Sdarrenrnat_t *nat; 2165145522Sdarrenrnatinfo_t *ni; 2166145522Sdarrenr{ 2167145522Sdarrenr u_short nport, dport, sport; 2168170268Sdarrenr struct in_addr in, inb; 2169170268Sdarrenr u_short sp, dp; 2170145522Sdarrenr hostmap_t *hm; 2171145522Sdarrenr u_32_t flags; 2172145522Sdarrenr ipnat_t *np; 2173170268Sdarrenr nat_t *natl; 2174145522Sdarrenr int move; 2175145522Sdarrenr 2176145522Sdarrenr move = 1; 2177145522Sdarrenr hm = NULL; 2178145522Sdarrenr in.s_addr = 0; 2179145522Sdarrenr np = ni->nai_np; 2180145522Sdarrenr flags = ni->nai_flags; 2181145522Sdarrenr sport = ni->nai_sport; 2182145522Sdarrenr dport = ni->nai_dport; 2183145522Sdarrenr 2184145522Sdarrenr /* 2185145522Sdarrenr * If the matching rule has IPN_STICKY set, then we want to have the 2186145522Sdarrenr * same rule kick in as before. Why would this happen? If you have 2187145522Sdarrenr * a collection of rdr rules with "round-robin sticky", the current 2188145522Sdarrenr * packet might match a different one to the previous connection but 2189145522Sdarrenr * we want the same destination to be used. 2190145522Sdarrenr */ 2191153876Sguido if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) && 2192153876Sguido ((np->in_flags & IPN_STICKY) != 0)) { 2193145522Sdarrenr hm = nat_hostmap(NULL, fin->fin_src, fin->fin_dst, in, 2194145522Sdarrenr (u_32_t)dport); 2195145522Sdarrenr if (hm != NULL) { 2196145522Sdarrenr in.s_addr = ntohl(hm->hm_mapip.s_addr); 2197145522Sdarrenr np = hm->hm_ipnat; 2198145522Sdarrenr ni->nai_np = np; 2199145522Sdarrenr move = 0; 2200145522Sdarrenr } 2201145522Sdarrenr } 2202145522Sdarrenr 2203145522Sdarrenr /* 2204145522Sdarrenr * Otherwise, it's an inbound packet. Most likely, we don't 2205145522Sdarrenr * want to rewrite source ports and source addresses. Instead, 2206145522Sdarrenr * we want to rewrite to a fixed internal address and fixed 2207145522Sdarrenr * internal port. 2208145522Sdarrenr */ 2209145522Sdarrenr if (np->in_flags & IPN_SPLIT) { 2210145522Sdarrenr in.s_addr = np->in_nip; 2211145522Sdarrenr 2212145522Sdarrenr if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { 2213153876Sguido hm = nat_hostmap(NULL, fin->fin_src, fin->fin_dst, 2214145522Sdarrenr in, (u_32_t)dport); 2215145522Sdarrenr if (hm != NULL) { 2216145522Sdarrenr in.s_addr = hm->hm_mapip.s_addr; 2217145522Sdarrenr move = 0; 2218145522Sdarrenr } 2219145522Sdarrenr } 2220145522Sdarrenr 2221145522Sdarrenr if (hm == NULL || hm->hm_ref == 1) { 2222145522Sdarrenr if (np->in_inip == htonl(in.s_addr)) { 2223145522Sdarrenr np->in_nip = ntohl(np->in_inmsk); 2224145522Sdarrenr move = 0; 2225145522Sdarrenr } else { 2226145522Sdarrenr np->in_nip = ntohl(np->in_inip); 2227145522Sdarrenr } 2228145522Sdarrenr } 2229145522Sdarrenr 2230145522Sdarrenr } else if ((np->in_inip == 0) && (np->in_inmsk == 0xffffffff)) { 2231145522Sdarrenr /* 2232145522Sdarrenr * 0/32 - use the interface's IP address. 2233145522Sdarrenr */ 2234145522Sdarrenr if (fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp, &in, NULL) == -1) 2235145522Sdarrenr return -1; 2236145522Sdarrenr in.s_addr = ntohl(in.s_addr); 2237145522Sdarrenr 2238145522Sdarrenr } else if ((np->in_inip == 0) && (np->in_inmsk== 0)) { 2239145522Sdarrenr /* 2240145522Sdarrenr * 0/0 - use the original destination address/port. 2241145522Sdarrenr */ 2242145522Sdarrenr in.s_addr = ntohl(fin->fin_daddr); 2243145522Sdarrenr 2244145522Sdarrenr } else if (np->in_redir == NAT_BIMAP && 2245145522Sdarrenr np->in_inmsk == np->in_outmsk) { 2246145522Sdarrenr /* 2247145522Sdarrenr * map the address block in a 1:1 fashion 2248145522Sdarrenr */ 2249145522Sdarrenr in.s_addr = np->in_inip; 2250145522Sdarrenr in.s_addr |= fin->fin_daddr & ~np->in_inmsk; 2251145522Sdarrenr in.s_addr = ntohl(in.s_addr); 2252145522Sdarrenr } else { 2253145522Sdarrenr in.s_addr = ntohl(np->in_inip); 2254145522Sdarrenr } 2255145522Sdarrenr 2256145522Sdarrenr if ((np->in_pnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) 2257145522Sdarrenr nport = dport; 2258145522Sdarrenr else { 2259145522Sdarrenr /* 2260145522Sdarrenr * Whilst not optimized for the case where 2261145522Sdarrenr * pmin == pmax, the gain is not significant. 2262145522Sdarrenr */ 2263145522Sdarrenr if (((np->in_flags & IPN_FIXEDDPORT) == 0) && 2264145522Sdarrenr (np->in_pmin != np->in_pmax)) { 2265145522Sdarrenr nport = ntohs(dport) - ntohs(np->in_pmin) + 2266145522Sdarrenr ntohs(np->in_pnext); 2267145522Sdarrenr nport = htons(nport); 2268145522Sdarrenr } else 2269145522Sdarrenr nport = np->in_pnext; 2270145522Sdarrenr } 2271145522Sdarrenr 2272145522Sdarrenr /* 2273145522Sdarrenr * When the redirect-to address is set to 0.0.0.0, just 2274145522Sdarrenr * assume a blank `forwarding' of the packet. We don't 2275145522Sdarrenr * setup any translation for this either. 2276145522Sdarrenr */ 2277145522Sdarrenr if (in.s_addr == 0) { 2278145522Sdarrenr if (nport == dport) 2279145522Sdarrenr return -1; 2280145522Sdarrenr in.s_addr = ntohl(fin->fin_daddr); 2281145522Sdarrenr } 2282145522Sdarrenr 2283170268Sdarrenr /* 2284170268Sdarrenr * Check to see if this redirect mapping already exists and if 2285170268Sdarrenr * it does, return "failure" (allowing it to be created will just 2286170268Sdarrenr * cause one or both of these "connections" to stop working.) 2287170268Sdarrenr */ 2288170268Sdarrenr inb.s_addr = htonl(in.s_addr); 2289170268Sdarrenr sp = fin->fin_data[0]; 2290170268Sdarrenr dp = fin->fin_data[1]; 2291170268Sdarrenr fin->fin_data[1] = fin->fin_data[0]; 2292170268Sdarrenr fin->fin_data[0] = ntohs(nport); 2293170268Sdarrenr natl = nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 2294170268Sdarrenr (u_int)fin->fin_p, inb, fin->fin_src); 2295170268Sdarrenr fin->fin_data[0] = sp; 2296170268Sdarrenr fin->fin_data[1] = dp; 2297170268Sdarrenr if (natl != NULL) 2298170268Sdarrenr return -1; 2299170268Sdarrenr 2300145522Sdarrenr nat->nat_inip.s_addr = htonl(in.s_addr); 2301145522Sdarrenr nat->nat_outip = fin->fin_dst; 2302145522Sdarrenr nat->nat_oip = fin->fin_src; 2303153876Sguido if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0)) 2304153876Sguido nat->nat_hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, in, 2305153876Sguido (u_32_t)dport); 2306145522Sdarrenr 2307145522Sdarrenr ni->nai_sum1 = LONG_SUM(ntohl(fin->fin_daddr)) + ntohs(dport); 2308145522Sdarrenr ni->nai_sum2 = LONG_SUM(in.s_addr) + ntohs(nport); 2309145522Sdarrenr 2310145522Sdarrenr ni->nai_ip.s_addr = in.s_addr; 2311145522Sdarrenr ni->nai_nport = nport; 2312145522Sdarrenr ni->nai_port = sport; 2313145522Sdarrenr 2314145522Sdarrenr if (flags & IPN_TCPUDP) { 2315145522Sdarrenr nat->nat_inport = nport; 2316145522Sdarrenr nat->nat_outport = dport; 2317145522Sdarrenr nat->nat_oport = sport; 2318145522Sdarrenr ((tcphdr_t *)fin->fin_dp)->th_dport = nport; 2319145522Sdarrenr } else if (flags & IPN_ICMPQUERY) { 2320145522Sdarrenr ((icmphdr_t *)fin->fin_dp)->icmp_id = nport; 2321145522Sdarrenr nat->nat_inport = nport; 2322145522Sdarrenr nat->nat_outport = nport; 2323145522Sdarrenr } else if (fin->fin_p == IPPROTO_GRE) { 2324145522Sdarrenr#if 0 2325145522Sdarrenr nat->nat_gre.gs_flags = ((grehdr_t *)fin->fin_dp)->gr_flags; 2326145522Sdarrenr if (GRE_REV(nat->nat_gre.gs_flags) == 1) { 2327145522Sdarrenr nat->nat_call[0] = fin->fin_data[0]; 2328145522Sdarrenr nat->nat_call[1] = fin->fin_data[1]; 2329145522Sdarrenr nat->nat_oport = 0; /*fin->fin_data[0];*/ 2330145522Sdarrenr nat->nat_inport = 0; /*fin->fin_data[1];*/ 2331145522Sdarrenr nat->nat_outport = 0; /*fin->fin_data[1];*/ 2332145522Sdarrenr } 2333145522Sdarrenr#endif 2334145522Sdarrenr } 2335145522Sdarrenr 2336145522Sdarrenr return move; 2337145522Sdarrenr} 2338145522Sdarrenr 2339145522Sdarrenr/* ------------------------------------------------------------------------ */ 2340145522Sdarrenr/* Function: nat_new */ 2341145522Sdarrenr/* Returns: nat_t* - NULL == failure to create new NAT structure, */ 2342145522Sdarrenr/* else pointer to new NAT structure */ 2343145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2344145522Sdarrenr/* np(I) - pointer to NAT rule */ 2345145522Sdarrenr/* natsave(I) - pointer to where to store NAT struct pointer */ 2346145522Sdarrenr/* flags(I) - flags describing the current packet */ 2347145522Sdarrenr/* direction(I) - direction of packet (in/out) */ 2348145522Sdarrenr/* Write Lock: ipf_nat */ 2349145522Sdarrenr/* */ 2350145522Sdarrenr/* Attempts to create a new NAT entry. Does not actually change the packet */ 2351145522Sdarrenr/* in any way. */ 2352145522Sdarrenr/* */ 2353145522Sdarrenr/* This fucntion is in three main parts: (1) deal with creating a new NAT */ 2354145522Sdarrenr/* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */ 2355145522Sdarrenr/* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */ 2356145522Sdarrenr/* and (3) building that structure and putting it into the NAT table(s). */ 2357161356Sguido/* */ 2358161356Sguido/* NOTE: natsave should NOT be used top point back to an ipstate_t struct */ 2359161356Sguido/* as it can result in memory being corrupted. */ 2360145522Sdarrenr/* ------------------------------------------------------------------------ */ 2361145522Sdarrenrnat_t *nat_new(fin, np, natsave, flags, direction) 2362145522Sdarrenrfr_info_t *fin; 236353642Sguidoipnat_t *np; 236492685Sdarrenrnat_t **natsave; 236553642Sguidou_int flags; 236653642Sguidoint direction; 236753642Sguido{ 236853642Sguido u_short port = 0, sport = 0, dport = 0, nport = 0; 236953642Sguido tcphdr_t *tcp = NULL; 237060852Sdarrenr hostmap_t *hm = NULL; 2371145522Sdarrenr struct in_addr in; 237260852Sdarrenr nat_t *nat, *natl; 2373145522Sdarrenr u_int nflags; 2374145522Sdarrenr natinfo_t ni; 2375145522Sdarrenr u_32_t sumd; 2376145522Sdarrenr int move; 2377145522Sdarrenr#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC) 2378145522Sdarrenr qpktinfo_t *qpi = fin->fin_qpi; 237955929Sguido#endif 238053642Sguido 2381130886Sdarrenr if (nat_stats.ns_inuse >= ipf_nattable_max) { 2382130886Sdarrenr nat_stats.ns_memfail++; 2383170268Sdarrenr fr_nat_doflush = 1; 2384130886Sdarrenr return NULL; 2385130886Sdarrenr } 2386130886Sdarrenr 2387145522Sdarrenr move = 1; 2388145522Sdarrenr nflags = np->in_flags & flags; 2389145522Sdarrenr nflags &= NAT_FROMRULE; 239053642Sguido 2391145522Sdarrenr ni.nai_np = np; 2392145522Sdarrenr ni.nai_nflags = nflags; 2393145522Sdarrenr ni.nai_flags = flags; 2394170268Sdarrenr ni.nai_dport = 0; 2395170268Sdarrenr ni.nai_sport = 0; 2396145522Sdarrenr 239753642Sguido /* Give me a new nat */ 239853642Sguido KMALLOC(nat, nat_t *); 239960852Sdarrenr if (nat == NULL) { 240060852Sdarrenr nat_stats.ns_memfail++; 2401130886Sdarrenr /* 2402130886Sdarrenr * Try to automatically tune the max # of entries in the 2403130886Sdarrenr * table allowed to be less than what will cause kmem_alloc() 2404130886Sdarrenr * to fail and try to eliminate panics due to out of memory 2405130886Sdarrenr * conditions arising. 2406130886Sdarrenr */ 2407130886Sdarrenr if (ipf_nattable_max > ipf_nattable_sz) { 2408130886Sdarrenr ipf_nattable_max = nat_stats.ns_inuse - 100; 2409130886Sdarrenr printf("ipf_nattable_max reduced to %d\n", 2410130886Sdarrenr ipf_nattable_max); 2411130886Sdarrenr } 241253642Sguido return NULL; 241360852Sdarrenr } 241453642Sguido 2415145522Sdarrenr if (flags & IPN_TCPUDP) { 2416145522Sdarrenr tcp = fin->fin_dp; 2417145522Sdarrenr ni.nai_sport = htons(fin->fin_sport); 2418145522Sdarrenr ni.nai_dport = htons(fin->fin_dport); 2419145522Sdarrenr } else if (flags & IPN_ICMPQUERY) { 2420145522Sdarrenr /* 2421145522Sdarrenr * In the ICMP query NAT code, we translate the ICMP id fields 2422145522Sdarrenr * to make them unique. This is indepedent of the ICMP type 2423145522Sdarrenr * (e.g. in the unlikely event that a host sends an echo and 2424145522Sdarrenr * an tstamp request with the same id, both packets will have 2425145522Sdarrenr * their ip address/id field changed in the same way). 2426145522Sdarrenr */ 2427145522Sdarrenr /* The icmp_id field is used by the sender to identify the 2428145522Sdarrenr * process making the icmp request. (the receiver justs 2429145522Sdarrenr * copies it back in its response). So, it closely matches 2430145522Sdarrenr * the concept of source port. We overlay sport, so we can 2431145522Sdarrenr * maximally reuse the existing code. 2432145522Sdarrenr */ 2433145522Sdarrenr ni.nai_sport = ((icmphdr_t *)fin->fin_dp)->icmp_id; 2434145522Sdarrenr ni.nai_dport = ni.nai_sport; 2435145522Sdarrenr } 2436145522Sdarrenr 243753642Sguido bzero((char *)nat, sizeof(*nat)); 243853642Sguido nat->nat_flags = flags; 2439170268Sdarrenr nat->nat_redir = np->in_redir; 2440145522Sdarrenr 2441145522Sdarrenr if ((flags & NAT_SLAVE) == 0) { 2442145522Sdarrenr MUTEX_ENTER(&ipf_nat_new); 2443145522Sdarrenr } 2444145522Sdarrenr 244553642Sguido /* 244653642Sguido * Search the current table for a match. 244753642Sguido */ 244853642Sguido if (direction == NAT_OUTBOUND) { 244953642Sguido /* 2450145522Sdarrenr * We can now arrange to call this for the same connection 2451145522Sdarrenr * because ipf_nat_new doesn't protect the code path into 2452145522Sdarrenr * this function. 245353642Sguido */ 2454145522Sdarrenr natl = nat_outlookup(fin, nflags, (u_int)fin->fin_p, 2455145522Sdarrenr fin->fin_src, fin->fin_dst); 2456145522Sdarrenr if (natl != NULL) { 2457161356Sguido KFREE(nat); 2458145522Sdarrenr nat = natl; 2459145522Sdarrenr goto done; 2460145522Sdarrenr } 246153642Sguido 2462145522Sdarrenr move = nat_newmap(fin, nat, &ni); 2463145522Sdarrenr if (move == -1) 2464145522Sdarrenr goto badnat; 246553642Sguido 2466145522Sdarrenr np = ni.nai_np; 2467145522Sdarrenr in = ni.nai_ip; 246853642Sguido } else { 246953642Sguido /* 2470145522Sdarrenr * NAT_INBOUND is used only for redirects rules 247153642Sguido */ 2472145522Sdarrenr natl = nat_inlookup(fin, nflags, (u_int)fin->fin_p, 2473145522Sdarrenr fin->fin_src, fin->fin_dst); 2474145522Sdarrenr if (natl != NULL) { 2475161356Sguido KFREE(nat); 2476145522Sdarrenr nat = natl; 2477145522Sdarrenr goto done; 247860852Sdarrenr } 247953642Sguido 2480145522Sdarrenr move = nat_newrdr(fin, nat, &ni); 2481145522Sdarrenr if (move == -1) 2482145522Sdarrenr goto badnat; 248353642Sguido 2484145522Sdarrenr np = ni.nai_np; 2485145522Sdarrenr in = ni.nai_ip; 2486145522Sdarrenr } 2487145522Sdarrenr port = ni.nai_port; 2488145522Sdarrenr nport = ni.nai_nport; 248953642Sguido 2490145522Sdarrenr if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { 2491145522Sdarrenr if (np->in_redir == NAT_REDIRECT) { 2492145522Sdarrenr nat_delrdr(np); 2493145522Sdarrenr nat_addrdr(np); 2494145522Sdarrenr } else if (np->in_redir == NAT_MAP) { 2495145522Sdarrenr nat_delnat(np); 2496145522Sdarrenr nat_addnat(np); 249753642Sguido } 249853642Sguido } 249953642Sguido 2500145522Sdarrenr if (flags & IPN_TCPUDP) { 2501145522Sdarrenr sport = ni.nai_sport; 2502145522Sdarrenr dport = ni.nai_dport; 2503145522Sdarrenr } else if (flags & IPN_ICMPQUERY) { 2504145522Sdarrenr sport = ni.nai_sport; 2505145522Sdarrenr dport = 0; 2506145522Sdarrenr } 2507145522Sdarrenr 2508145522Sdarrenr CALC_SUMD(ni.nai_sum1, ni.nai_sum2, sumd); 250955929Sguido nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 2510145522Sdarrenr#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC) 2511130886Sdarrenr if ((flags & IPN_TCP) && dohwcksum && 2512145522Sdarrenr (((ill_t *)qpi->qpi_ill)->ill_ick.ick_magic == ICK_M_CTL_MAGIC)) { 251355929Sguido if (direction == NAT_OUTBOUND) 2514145522Sdarrenr ni.nai_sum1 = LONG_SUM(in.s_addr); 251555929Sguido else 2516145522Sdarrenr ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr)); 2517145522Sdarrenr ni.nai_sum1 += LONG_SUM(ntohl(fin->fin_daddr)); 2518145522Sdarrenr ni.nai_sum1 += 30; 2519145522Sdarrenr ni.nai_sum1 = (ni.nai_sum1 & 0xffff) + (ni.nai_sum1 >> 16); 2520145522Sdarrenr nat->nat_sumd[1] = NAT_HW_CKSUM|(ni.nai_sum1 & 0xffff); 252155929Sguido } else 252255929Sguido#endif 252355929Sguido nat->nat_sumd[1] = nat->nat_sumd[0]; 252453642Sguido 2525145522Sdarrenr if ((flags & IPN_TCPUDPICMP) && ((sport != port) || (dport != nport))) { 252653642Sguido if (direction == NAT_OUTBOUND) 2527145522Sdarrenr ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr)); 252853642Sguido else 2529145522Sdarrenr ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_daddr)); 253053642Sguido 2531145522Sdarrenr ni.nai_sum2 = LONG_SUM(in.s_addr); 253253642Sguido 2533145522Sdarrenr CALC_SUMD(ni.nai_sum1, ni.nai_sum2, sumd); 253453642Sguido nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); 2535145522Sdarrenr } else { 253655929Sguido nat->nat_ipsumd = nat->nat_sumd[0]; 2537145522Sdarrenr if (!(flags & IPN_TCPUDPICMP)) { 2538145522Sdarrenr nat->nat_sumd[0] = 0; 2539145522Sdarrenr nat->nat_sumd[1] = 0; 2540145522Sdarrenr } 2541145522Sdarrenr } 254253642Sguido 2543145522Sdarrenr if (nat_finalise(fin, nat, &ni, tcp, natsave, direction) == -1) { 2544172776Sdarrenr fr_nat_doflush = 1; 2545145522Sdarrenr goto badnat; 2546145522Sdarrenr } 2547145522Sdarrenr if (flags & SI_WILDP) 2548145522Sdarrenr nat_stats.ns_wilds++; 2549172776Sdarrenr fin->fin_flx |= FI_NEWNAT; 2550145522Sdarrenr goto done; 2551145522Sdarrenrbadnat: 2552145522Sdarrenr nat_stats.ns_badnat++; 2553145522Sdarrenr if ((hm = nat->nat_hm) != NULL) 2554170268Sdarrenr fr_hostmapdel(&hm); 2555145522Sdarrenr KFREE(nat); 2556145522Sdarrenr nat = NULL; 2557145522Sdarrenrdone: 2558145522Sdarrenr if ((flags & NAT_SLAVE) == 0) { 2559145522Sdarrenr MUTEX_EXIT(&ipf_nat_new); 2560145522Sdarrenr } 2561145522Sdarrenr return nat; 2562145522Sdarrenr} 256360852Sdarrenr 256460852Sdarrenr 2565145522Sdarrenr/* ------------------------------------------------------------------------ */ 2566145522Sdarrenr/* Function: nat_finalise */ 2567145522Sdarrenr/* Returns: int - 0 == sucess, -1 == failure */ 2568145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2569145522Sdarrenr/* nat(I) - pointer to NAT entry */ 2570145522Sdarrenr/* ni(I) - pointer to structure with misc. information needed */ 2571145522Sdarrenr/* to create new NAT entry. */ 2572145522Sdarrenr/* Write Lock: ipf_nat */ 2573145522Sdarrenr/* */ 2574145522Sdarrenr/* This is the tail end of constructing a new NAT entry and is the same */ 2575145522Sdarrenr/* for both IPv4 and IPv6. */ 2576145522Sdarrenr/* ------------------------------------------------------------------------ */ 2577145522Sdarrenr/*ARGSUSED*/ 2578153876Sguidostatic int nat_finalise(fin, nat, ni, tcp, natsave, direction) 2579145522Sdarrenrfr_info_t *fin; 2580145522Sdarrenrnat_t *nat; 2581145522Sdarrenrnatinfo_t *ni; 2582145522Sdarrenrtcphdr_t *tcp; 2583145522Sdarrenrnat_t **natsave; 2584145522Sdarrenrint direction; 2585145522Sdarrenr{ 2586145522Sdarrenr frentry_t *fr; 2587145522Sdarrenr ipnat_t *np; 2588145522Sdarrenr 2589145522Sdarrenr np = ni->nai_np; 2590145522Sdarrenr 2591161356Sguido if (np->in_ifps[0] != NULL) { 2592172776Sdarrenr COPYIFNAME(4, np->in_ifps[0], nat->nat_ifnames[0]); 2593161356Sguido } 2594161356Sguido if (np->in_ifps[1] != NULL) { 2595172776Sdarrenr COPYIFNAME(4, np->in_ifps[1], nat->nat_ifnames[1]); 2596161356Sguido } 2597145522Sdarrenr#ifdef IPFILTER_SYNC 2598145522Sdarrenr if ((nat->nat_flags & SI_CLONE) == 0) 2599145522Sdarrenr nat->nat_sync = ipfsync_new(SMC_NAT, fin, nat); 2600145522Sdarrenr#endif 2601145522Sdarrenr 260292685Sdarrenr nat->nat_me = natsave; 260353642Sguido nat->nat_dir = direction; 2604161356Sguido nat->nat_ifps[0] = np->in_ifps[0]; 2605161356Sguido nat->nat_ifps[1] = np->in_ifps[1]; 260653642Sguido nat->nat_ptr = np; 260792685Sdarrenr nat->nat_p = fin->fin_p; 2608110916Sdarrenr nat->nat_mssclamp = np->in_mssclamp; 2609172776Sdarrenr if (nat->nat_p == IPPROTO_TCP) 2610172776Sdarrenr nat->nat_seqnext[0] = ntohl(tcp->th_seq); 261192685Sdarrenr 2612145522Sdarrenr if ((np->in_apr != NULL) && ((ni->nai_flags & NAT_SLAVE) == 0)) 2613145522Sdarrenr if (appr_new(fin, nat) == -1) 2614145522Sdarrenr return -1; 261592685Sdarrenr 2616145522Sdarrenr if (nat_insert(nat, fin->fin_rev) == 0) { 2617145522Sdarrenr if (nat_logging) 2618145522Sdarrenr nat_log(nat, (u_int)np->in_redir); 2619145522Sdarrenr np->in_use++; 2620153876Sguido fr = fin->fin_fr; 2621153876Sguido nat->nat_fr = fr; 2622145522Sdarrenr if (fr != NULL) { 2623145522Sdarrenr MUTEX_ENTER(&fr->fr_lock); 2624145522Sdarrenr fr->fr_ref++; 2625145522Sdarrenr MUTEX_EXIT(&fr->fr_lock); 2626145522Sdarrenr } 2627145522Sdarrenr return 0; 2628145522Sdarrenr } 262992685Sdarrenr 2630145522Sdarrenr /* 2631145522Sdarrenr * nat_insert failed, so cleanup time... 2632145522Sdarrenr */ 2633145522Sdarrenr return -1; 263453642Sguido} 263553642Sguido 263653642Sguido 2637145522Sdarrenr/* ------------------------------------------------------------------------ */ 2638145522Sdarrenr/* Function: nat_insert */ 2639145522Sdarrenr/* Returns: int - 0 == sucess, -1 == failure */ 2640145522Sdarrenr/* Parameters: nat(I) - pointer to NAT structure */ 2641145522Sdarrenr/* rev(I) - flag indicating forward/reverse direction of packet */ 2642145522Sdarrenr/* Write Lock: ipf_nat */ 2643145522Sdarrenr/* */ 2644145522Sdarrenr/* Insert a NAT entry into the hash tables for searching and add it to the */ 2645145522Sdarrenr/* list of active NAT entries. Adjust global counters when complete. */ 2646145522Sdarrenr/* ------------------------------------------------------------------------ */ 2647145522Sdarrenrint nat_insert(nat, rev) 264860852Sdarrenrnat_t *nat; 2649145522Sdarrenrint rev; 265060852Sdarrenr{ 265180482Sdarrenr u_int hv1, hv2; 265260852Sdarrenr nat_t **natp; 265360852Sdarrenr 2654145522Sdarrenr /* 2655145522Sdarrenr * Try and return an error as early as possible, so calculate the hash 2656145522Sdarrenr * entry numbers first and then proceed. 2657145522Sdarrenr */ 2658145522Sdarrenr if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { 265980482Sdarrenr hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 266080482Sdarrenr 0xffffffff); 266180482Sdarrenr hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1 + nat->nat_oport, 266280482Sdarrenr ipf_nattable_sz); 266380482Sdarrenr hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, 266480482Sdarrenr 0xffffffff); 266580482Sdarrenr hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2 + nat->nat_oport, 2666145522Sdarrenr ipf_nattable_sz); 266780482Sdarrenr } else { 2668145522Sdarrenr hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, 0, 0xffffffff); 2669145522Sdarrenr hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1, ipf_nattable_sz); 2670145522Sdarrenr hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, 0, 0xffffffff); 2671145522Sdarrenr hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2, ipf_nattable_sz); 267280482Sdarrenr } 267380482Sdarrenr 2674145522Sdarrenr if (nat_stats.ns_bucketlen[0][hv1] >= fr_nat_maxbucket || 2675145522Sdarrenr nat_stats.ns_bucketlen[1][hv2] >= fr_nat_maxbucket) { 2676145522Sdarrenr return -1; 2677145522Sdarrenr } 2678145522Sdarrenr 2679145522Sdarrenr nat->nat_hv[0] = hv1; 2680145522Sdarrenr nat->nat_hv[1] = hv2; 2681145522Sdarrenr 2682145522Sdarrenr MUTEX_INIT(&nat->nat_lock, "nat entry lock"); 2683145522Sdarrenr 2684145522Sdarrenr nat->nat_rev = rev; 2685145522Sdarrenr nat->nat_ref = 1; 2686145522Sdarrenr nat->nat_bytes[0] = 0; 2687145522Sdarrenr nat->nat_pkts[0] = 0; 2688145522Sdarrenr nat->nat_bytes[1] = 0; 2689145522Sdarrenr nat->nat_pkts[1] = 0; 2690145522Sdarrenr 2691145522Sdarrenr nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; 2692145522Sdarrenr nat->nat_ifps[0] = fr_resolvenic(nat->nat_ifnames[0], 4); 2693145522Sdarrenr 2694161356Sguido if (nat->nat_ifnames[1][0] != '\0') { 2695145522Sdarrenr nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 2696145522Sdarrenr nat->nat_ifps[1] = fr_resolvenic(nat->nat_ifnames[1], 4); 2697145522Sdarrenr } else { 2698145522Sdarrenr (void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0], 2699145522Sdarrenr LIFNAMSIZ); 2700145522Sdarrenr nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 2701145522Sdarrenr nat->nat_ifps[1] = nat->nat_ifps[0]; 2702145522Sdarrenr } 2703145522Sdarrenr 2704145522Sdarrenr nat->nat_next = nat_instances; 2705145522Sdarrenr nat->nat_pnext = &nat_instances; 2706145522Sdarrenr if (nat_instances) 2707145522Sdarrenr nat_instances->nat_pnext = &nat->nat_next; 2708145522Sdarrenr nat_instances = nat; 2709145522Sdarrenr 271080482Sdarrenr natp = &nat_table[0][hv1]; 271167614Sdarrenr if (*natp) 271267614Sdarrenr (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 271367614Sdarrenr nat->nat_phnext[0] = natp; 271460852Sdarrenr nat->nat_hnext[0] = *natp; 271560852Sdarrenr *natp = nat; 2716145522Sdarrenr nat_stats.ns_bucketlen[0][hv1]++; 271767614Sdarrenr 271880482Sdarrenr natp = &nat_table[1][hv2]; 271967614Sdarrenr if (*natp) 272067614Sdarrenr (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 272167614Sdarrenr nat->nat_phnext[1] = natp; 272260852Sdarrenr nat->nat_hnext[1] = *natp; 272360852Sdarrenr *natp = nat; 2724145522Sdarrenr nat_stats.ns_bucketlen[1][hv2]++; 272560852Sdarrenr 2726145522Sdarrenr fr_setnatqueue(nat, rev); 2727145522Sdarrenr 272860852Sdarrenr nat_stats.ns_added++; 272960852Sdarrenr nat_stats.ns_inuse++; 2730145522Sdarrenr return 0; 273160852Sdarrenr} 273260852Sdarrenr 273360852Sdarrenr 2734145522Sdarrenr/* ------------------------------------------------------------------------ */ 2735145522Sdarrenr/* Function: nat_icmperrorlookup */ 2736145522Sdarrenr/* Returns: nat_t* - point to matching NAT structure */ 2737145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2738145522Sdarrenr/* dir(I) - direction of packet (in/out) */ 2739145522Sdarrenr/* */ 2740145522Sdarrenr/* Check if the ICMP error message is related to an existing TCP, UDP or */ 2741145522Sdarrenr/* ICMP query nat entry. It is assumed that the packet is already of the */ 2742145522Sdarrenr/* the required length. */ 2743145522Sdarrenr/* ------------------------------------------------------------------------ */ 2744145522Sdarrenrnat_t *nat_icmperrorlookup(fin, dir) 274553642Sguidofr_info_t *fin; 274660852Sdarrenrint dir; 274753642Sguido{ 2748145522Sdarrenr int flags = 0, type, minlen; 2749145522Sdarrenr icmphdr_t *icmp, *orgicmp; 275053642Sguido tcphdr_t *tcp = NULL; 2751145522Sdarrenr u_short data[2]; 2752145522Sdarrenr nat_t *nat; 275353642Sguido ip_t *oip; 2754145522Sdarrenr u_int p; 275553642Sguido 2756145522Sdarrenr icmp = fin->fin_dp; 2757145522Sdarrenr type = icmp->icmp_type; 275853642Sguido /* 275953642Sguido * Does it at least have the return (basic) IP header ? 276053642Sguido * Only a basic IP header (no options) should be with an ICMP error 2761145522Sdarrenr * header. Also, if it's not an error type, then return. 276253642Sguido */ 2763153876Sguido if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) 276453642Sguido return NULL; 2765145522Sdarrenr 276653642Sguido /* 2767145522Sdarrenr * Check packet size 276853642Sguido */ 276953642Sguido oip = (ip_t *)((char *)fin->fin_dp + 8); 2770145522Sdarrenr minlen = IP_HL(oip) << 2; 2771145522Sdarrenr if ((minlen < sizeof(ip_t)) || 2772145522Sdarrenr (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) 277353642Sguido return NULL; 277464580Sdarrenr /* 277564580Sdarrenr * Is the buffer big enough for all of it ? It's the size of the IP 277664580Sdarrenr * header claimed in the encapsulated part which is of concern. It 277764580Sdarrenr * may be too big to be in this buffer but not so big that it's 277864580Sdarrenr * outside the ICMP packet, leading to TCP deref's causing problems. 277964580Sdarrenr * This is possible because we don't know how big oip_hl is when we 278064580Sdarrenr * do the pullup early in fr_check() and thus can't gaurantee it is 278164580Sdarrenr * all here now. 278264580Sdarrenr */ 278364580Sdarrenr#ifdef _KERNEL 278464580Sdarrenr { 278564580Sdarrenr mb_t *m; 278664580Sdarrenr 2787145522Sdarrenr m = fin->fin_m; 2788145522Sdarrenr# if defined(MENTAT) 278964580Sdarrenr if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr) 279064580Sdarrenr return NULL; 279164580Sdarrenr# else 279264580Sdarrenr if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > 2793145522Sdarrenr (char *)fin->fin_ip + M_LEN(m)) 279464580Sdarrenr return NULL; 279564580Sdarrenr# endif 279664580Sdarrenr } 279764580Sdarrenr#endif 279864580Sdarrenr 2799145522Sdarrenr if (fin->fin_daddr != oip->ip_src.s_addr) 2800145522Sdarrenr return NULL; 2801145522Sdarrenr 2802145522Sdarrenr p = oip->ip_p; 2803145522Sdarrenr if (p == IPPROTO_TCP) 280453642Sguido flags = IPN_TCP; 2805145522Sdarrenr else if (p == IPPROTO_UDP) 280653642Sguido flags = IPN_UDP; 2807145522Sdarrenr else if (p == IPPROTO_ICMP) { 2808145522Sdarrenr orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 2809145522Sdarrenr 2810145522Sdarrenr /* see if this is related to an ICMP query */ 2811145522Sdarrenr if (nat_icmpquerytype4(orgicmp->icmp_type)) { 2812145522Sdarrenr data[0] = fin->fin_data[0]; 2813145522Sdarrenr data[1] = fin->fin_data[1]; 2814145522Sdarrenr fin->fin_data[0] = 0; 2815145522Sdarrenr fin->fin_data[1] = orgicmp->icmp_id; 2816145522Sdarrenr 2817145522Sdarrenr flags = IPN_ICMPERR|IPN_ICMPQUERY; 2818145522Sdarrenr /* 2819145522Sdarrenr * NOTE : dir refers to the direction of the original 2820145522Sdarrenr * ip packet. By definition the icmp error 2821145522Sdarrenr * message flows in the opposite direction. 2822145522Sdarrenr */ 2823145522Sdarrenr if (dir == NAT_INBOUND) 2824145522Sdarrenr nat = nat_inlookup(fin, flags, p, oip->ip_dst, 2825145522Sdarrenr oip->ip_src); 2826145522Sdarrenr else 2827145522Sdarrenr nat = nat_outlookup(fin, flags, p, oip->ip_dst, 2828145522Sdarrenr oip->ip_src); 2829145522Sdarrenr fin->fin_data[0] = data[0]; 2830145522Sdarrenr fin->fin_data[1] = data[1]; 2831145522Sdarrenr return nat; 2832145522Sdarrenr } 2833145522Sdarrenr } 2834145522Sdarrenr 283553642Sguido if (flags & IPN_TCPUDP) { 283664580Sdarrenr minlen += 8; /* + 64bits of data to get ports */ 2837145522Sdarrenr if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) 283864580Sdarrenr return NULL; 283992685Sdarrenr 284092685Sdarrenr data[0] = fin->fin_data[0]; 284192685Sdarrenr data[1] = fin->fin_data[1]; 2842145522Sdarrenr tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 284392685Sdarrenr fin->fin_data[0] = ntohs(tcp->th_dport); 284492685Sdarrenr fin->fin_data[1] = ntohs(tcp->th_sport); 284592685Sdarrenr 284692685Sdarrenr if (dir == NAT_INBOUND) { 2847145522Sdarrenr nat = nat_inlookup(fin, flags, p, oip->ip_dst, 2848145522Sdarrenr oip->ip_src); 284992685Sdarrenr } else { 2850145522Sdarrenr nat = nat_outlookup(fin, flags, p, oip->ip_dst, 2851145522Sdarrenr oip->ip_src); 285292685Sdarrenr } 285392685Sdarrenr fin->fin_data[0] = data[0]; 285492685Sdarrenr fin->fin_data[1] = data[1]; 285592685Sdarrenr return nat; 285653642Sguido } 285760852Sdarrenr if (dir == NAT_INBOUND) 2858145522Sdarrenr return nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src); 285960852Sdarrenr else 2860145522Sdarrenr return nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src); 286153642Sguido} 286253642Sguido 286353642Sguido 2864145522Sdarrenr/* ------------------------------------------------------------------------ */ 2865145522Sdarrenr/* Function: nat_icmperror */ 2866145522Sdarrenr/* Returns: nat_t* - point to matching NAT structure */ 2867145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2868145522Sdarrenr/* nflags(I) - NAT flags for this packet */ 2869145522Sdarrenr/* dir(I) - direction of packet (in/out) */ 2870145522Sdarrenr/* */ 2871145522Sdarrenr/* Fix up an ICMP packet which is an error message for an existing NAT */ 2872145522Sdarrenr/* session. This will correct both packet header data and checksums. */ 2873145522Sdarrenr/* */ 2874145522Sdarrenr/* This should *ONLY* be used for incoming ICMP error packets to make sure */ 2875145522Sdarrenr/* a NAT'd ICMP packet gets correctly recognised. */ 2876145522Sdarrenr/* ------------------------------------------------------------------------ */ 2877145522Sdarrenrnat_t *nat_icmperror(fin, nflags, dir) 287853642Sguidofr_info_t *fin; 287953642Sguidou_int *nflags; 288060852Sdarrenrint dir; 288153642Sguido{ 2882145522Sdarrenr u_32_t sum1, sum2, sumd, sumd2; 2883170268Sdarrenr struct in_addr a1, a2; 2884170268Sdarrenr int flags, dlen, odst; 2885145522Sdarrenr icmphdr_t *icmp; 2886145522Sdarrenr u_short *csump; 288795418Sdarrenr tcphdr_t *tcp; 288853642Sguido nat_t *nat; 288953642Sguido ip_t *oip; 2890145522Sdarrenr void *dp; 289153642Sguido 2892145522Sdarrenr if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) 289363523Sdarrenr return NULL; 289467614Sdarrenr /* 2895145522Sdarrenr * nat_icmperrorlookup() will return NULL for `defective' packets. 289667614Sdarrenr */ 2897145522Sdarrenr if ((fin->fin_v != 4) || !(nat = nat_icmperrorlookup(fin, dir))) 289853642Sguido return NULL; 289992685Sdarrenr 2900145522Sdarrenr tcp = NULL; 2901145522Sdarrenr csump = NULL; 290292685Sdarrenr flags = 0; 2903130886Sdarrenr sumd2 = 0; 290453642Sguido *nflags = IPN_ICMPERR; 2905145522Sdarrenr icmp = fin->fin_dp; 290653642Sguido oip = (ip_t *)&icmp->icmp_ip; 2907145522Sdarrenr dp = (((char *)oip) + (IP_HL(oip) << 2)); 2908145522Sdarrenr if (oip->ip_p == IPPROTO_TCP) { 2909145522Sdarrenr tcp = (tcphdr_t *)dp; 2910145522Sdarrenr csump = (u_short *)&tcp->th_sum; 291153642Sguido flags = IPN_TCP; 2912145522Sdarrenr } else if (oip->ip_p == IPPROTO_UDP) { 2913145522Sdarrenr udphdr_t *udp; 2914145522Sdarrenr 2915145522Sdarrenr udp = (udphdr_t *)dp; 2916145522Sdarrenr tcp = (tcphdr_t *)dp; 2917145522Sdarrenr csump = (u_short *)&udp->uh_sum; 291853642Sguido flags = IPN_UDP; 2919145522Sdarrenr } else if (oip->ip_p == IPPROTO_ICMP) 2920145522Sdarrenr flags = IPN_ICMPQUERY; 2921145522Sdarrenr dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip); 292295418Sdarrenr 292395418Sdarrenr /* 292453642Sguido * Need to adjust ICMP header to include the real IP#'s and 292553642Sguido * port #'s. Only apply a checksum change relative to the 2926145522Sdarrenr * IP address change as it will be modified again in fr_checknatout 292753642Sguido * for both address and port. Two checksum changes are 292853642Sguido * necessary for the two header address changes. Be careful 292953642Sguido * to only modify the checksum once for the port # and twice 293053642Sguido * for the IP#. 293153642Sguido */ 293260852Sdarrenr 293367614Sdarrenr /* 293467614Sdarrenr * Step 1 293567614Sdarrenr * Fix the IP addresses in the offending IP packet. You also need 2936170268Sdarrenr * to adjust the IP header checksum of that offending IP packet. 293767614Sdarrenr * 2938145522Sdarrenr * Normally, you would expect that the ICMP checksum of the 2939130886Sdarrenr * ICMP error message needs to be adjusted as well for the 2940130886Sdarrenr * IP address change in oip. 2941145522Sdarrenr * However, this is a NOP, because the ICMP checksum is 2942130886Sdarrenr * calculated over the complete ICMP packet, which includes the 2943145522Sdarrenr * changed oip IP addresses and oip->ip_sum. However, these 2944130886Sdarrenr * two changes cancel each other out (if the delta for 2945145522Sdarrenr * the IP address is x, then the delta for ip_sum is minus x), 2946130886Sdarrenr * so no change in the icmp_cksum is necessary. 2947130886Sdarrenr * 2948170268Sdarrenr * Inbound ICMP 2949170268Sdarrenr * ------------ 2950170268Sdarrenr * MAP rule, SRC=a,DST=b -> SRC=c,DST=b 2951170268Sdarrenr * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b) 2952170268Sdarrenr * - OIP_SRC(c)=nat_outip, OIP_DST(b)=nat_oip 2953170268Sdarrenr * 2954170268Sdarrenr * RDR rule, SRC=a,DST=b -> SRC=a,DST=c 2955170268Sdarrenr * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a) 2956170268Sdarrenr * - OIP_SRC(b)=nat_outip, OIP_DST(a)=nat_oip 2957170268Sdarrenr * 2958170268Sdarrenr * Outbound ICMP 2959170268Sdarrenr * ------------- 2960170268Sdarrenr * MAP rule, SRC=a,DST=b -> SRC=c,DST=b 2961170268Sdarrenr * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) 2962170268Sdarrenr * - OIP_SRC(a)=nat_oip, OIP_DST(c)=nat_inip 2963170268Sdarrenr * 2964170268Sdarrenr * RDR rule, SRC=a,DST=b -> SRC=a,DST=c 2965170268Sdarrenr * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c) 2966170268Sdarrenr * - OIP_SRC(a)=nat_oip, OIP_DST(c)=nat_inip 2967170268Sdarrenr * 2968130886Sdarrenr */ 2969170268Sdarrenr odst = (oip->ip_dst.s_addr == nat->nat_oip.s_addr) ? 1 : 0; 2970170268Sdarrenr if (odst == 1) { 2971170268Sdarrenr a1.s_addr = ntohl(nat->nat_inip.s_addr); 2972170268Sdarrenr a2.s_addr = ntohl(oip->ip_src.s_addr); 2973170268Sdarrenr oip->ip_src.s_addr = htonl(a1.s_addr); 2974170268Sdarrenr } else { 2975170268Sdarrenr a1.s_addr = ntohl(nat->nat_outip.s_addr); 2976170268Sdarrenr a2.s_addr = ntohl(oip->ip_dst.s_addr); 2977170268Sdarrenr oip->ip_dst.s_addr = htonl(a1.s_addr); 2978170268Sdarrenr } 2979130886Sdarrenr 2980170268Sdarrenr sumd = a2.s_addr - a1.s_addr; 2981170268Sdarrenr if (sumd != 0) { 2982170268Sdarrenr if (a1.s_addr > a2.s_addr) 2983170268Sdarrenr sumd--; 2984170268Sdarrenr sumd = ~sumd; 298553642Sguido 2986170268Sdarrenr fix_datacksum(&oip->ip_sum, sumd); 2987130886Sdarrenr } 298867614Sdarrenr 2989170268Sdarrenr sumd2 = sumd; 2990170268Sdarrenr sum1 = 0; 2991170268Sdarrenr sum2 = 0; 2992170268Sdarrenr 2993130886Sdarrenr /* 2994170268Sdarrenr * Fix UDP pseudo header checksum to compensate for the 2995170268Sdarrenr * IP address change. 2996130886Sdarrenr */ 2997130886Sdarrenr if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) { 299864580Sdarrenr /* 299967614Sdarrenr * Step 2 : 300067614Sdarrenr * For offending TCP/UDP IP packets, translate the ports as 300167614Sdarrenr * well, based on the NAT specification. Of course such 3002170268Sdarrenr * a change may be reflected in the ICMP checksum as well. 300367614Sdarrenr * 300467614Sdarrenr * Since the port fields are part of the TCP/UDP checksum 300567614Sdarrenr * of the offending IP packet, you need to adjust that checksum 3006161356Sguido * as well... except that the change in the port numbers should 3007170268Sdarrenr * be offset by the checksum change. However, the TCP/UDP 3008170268Sdarrenr * checksum will also need to change if there has been an 3009170268Sdarrenr * IP address change. 301067614Sdarrenr */ 3011170268Sdarrenr if (odst == 1) { 3012170268Sdarrenr sum1 = ntohs(nat->nat_inport); 3013170268Sdarrenr sum2 = ntohs(tcp->th_sport); 3014145522Sdarrenr 3015170268Sdarrenr tcp->th_sport = htons(sum1); 3016170268Sdarrenr } else { 3017145522Sdarrenr sum1 = ntohs(nat->nat_outport); 3018145522Sdarrenr sum2 = ntohs(tcp->th_dport); 3019170268Sdarrenr 3020170268Sdarrenr tcp->th_dport = htons(sum1); 3021145522Sdarrenr } 302267614Sdarrenr 3023170268Sdarrenr sumd += sum1 - sum2; 3024170268Sdarrenr if (sumd != 0 || sumd2 != 0) { 3025145522Sdarrenr /* 3026170268Sdarrenr * At this point, sumd is the delta to apply to the 3027170268Sdarrenr * TCP/UDP header, given the changes in both the IP 3028170268Sdarrenr * address and the ports and sumd2 is the delta to 3029170268Sdarrenr * apply to the ICMP header, given the IP address 3030170268Sdarrenr * change delta that may need to be applied to the 3031170268Sdarrenr * TCP/UDP checksum instead. 3032145522Sdarrenr * 3033170268Sdarrenr * If we will both the IP and TCP/UDP checksums 3034170268Sdarrenr * then the ICMP checksum changes by the address 3035170268Sdarrenr * delta applied to the TCP/UDP checksum. If we 3036170268Sdarrenr * do not change the TCP/UDP checksum them we 3037170268Sdarrenr * apply the delta in ports to the ICMP checksum. 3038145522Sdarrenr */ 3039161356Sguido if (oip->ip_p == IPPROTO_UDP) { 3040161356Sguido if ((dlen >= 8) && (*csump != 0)) { 3041161356Sguido fix_datacksum(csump, sumd); 3042161356Sguido } else { 3043170268Sdarrenr sumd2 = sum1 - sum2; 3044170268Sdarrenr if (sum2 > sum1) 3045170268Sdarrenr sumd2--; 3046161356Sguido } 3047170268Sdarrenr } else if (oip->ip_p == IPPROTO_TCP) { 3048145522Sdarrenr if (dlen >= 18) { 3049145522Sdarrenr fix_datacksum(csump, sumd); 3050145522Sdarrenr } else { 3051170268Sdarrenr sumd2 = sum2 - sum1; 3052170268Sdarrenr if (sum1 > sum2) 3053170268Sdarrenr sumd2--; 305467614Sdarrenr } 305553642Sguido } 3056130886Sdarrenr 3057170268Sdarrenr if (sumd2 != 0) { 3058172776Sdarrenr ipnat_t *np; 3059172776Sdarrenr 3060172776Sdarrenr np = nat->nat_ptr; 3061170268Sdarrenr sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 3062170268Sdarrenr sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 3063170268Sdarrenr sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 3064172776Sdarrenr 3065172776Sdarrenr if ((odst == 0) && (dir == NAT_OUTBOUND) && 3066172776Sdarrenr (fin->fin_rev == 0) && (np != NULL) && 3067172776Sdarrenr (np->in_redir & NAT_REDIRECT)) { 3068172776Sdarrenr fix_outcksum(fin, &icmp->icmp_cksum, 3069172776Sdarrenr sumd2); 3070172776Sdarrenr } else { 3071172776Sdarrenr fix_incksum(fin, &icmp->icmp_cksum, 3072172776Sdarrenr sumd2); 3073172776Sdarrenr } 3074130886Sdarrenr } 307553642Sguido } 3076145522Sdarrenr } else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) { 3077145522Sdarrenr icmphdr_t *orgicmp; 3078145522Sdarrenr 3079145522Sdarrenr /* 3080145522Sdarrenr * XXX - what if this is bogus hl and we go off the end ? 3081145522Sdarrenr * In this case, nat_icmperrorlookup() will have returned NULL. 3082145522Sdarrenr */ 3083145522Sdarrenr orgicmp = (icmphdr_t *)dp; 3084145522Sdarrenr 3085170268Sdarrenr if (odst == 1) { 3086145522Sdarrenr if (orgicmp->icmp_id != nat->nat_inport) { 3087145522Sdarrenr 3088145522Sdarrenr /* 3089145522Sdarrenr * Fix ICMP checksum (of the offening ICMP 3090145522Sdarrenr * query packet) to compensate the change 3091145522Sdarrenr * in the ICMP id of the offending ICMP 3092145522Sdarrenr * packet. 3093145522Sdarrenr * 3094145522Sdarrenr * Since you modify orgicmp->icmp_id with 3095145522Sdarrenr * a delta (say x) and you compensate that 3096145522Sdarrenr * in origicmp->icmp_cksum with a delta 3097145522Sdarrenr * minus x, you don't have to adjust the 3098145522Sdarrenr * overall icmp->icmp_cksum 3099145522Sdarrenr */ 3100145522Sdarrenr sum1 = ntohs(orgicmp->icmp_id); 3101145522Sdarrenr sum2 = ntohs(nat->nat_inport); 3102145522Sdarrenr CALC_SUMD(sum1, sum2, sumd); 3103145522Sdarrenr orgicmp->icmp_id = nat->nat_inport; 3104145522Sdarrenr fix_datacksum(&orgicmp->icmp_cksum, sumd); 3105145522Sdarrenr } 3106145522Sdarrenr } /* nat_dir == NAT_INBOUND is impossible for icmp queries */ 310753642Sguido } 310853642Sguido return nat; 310953642Sguido} 311053642Sguido 311153642Sguido 311253642Sguido/* 3113145522Sdarrenr * NB: these lookups don't lock access to the list, it assumed that it has 3114145522Sdarrenr * already been done! 311553642Sguido */ 3116145522Sdarrenr 3117145522Sdarrenr/* ------------------------------------------------------------------------ */ 3118145522Sdarrenr/* Function: nat_inlookup */ 3119145522Sdarrenr/* Returns: nat_t* - NULL == no match, */ 3120145522Sdarrenr/* else pointer to matching NAT entry */ 3121145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3122145522Sdarrenr/* flags(I) - NAT flags for this packet */ 3123145522Sdarrenr/* p(I) - protocol for this packet */ 3124145522Sdarrenr/* src(I) - source IP address */ 3125145522Sdarrenr/* mapdst(I) - destination IP address */ 3126145522Sdarrenr/* */ 3127145522Sdarrenr/* Lookup a nat entry based on the mapped destination ip address/port and */ 3128145522Sdarrenr/* real source address/port. We use this lookup when receiving a packet, */ 3129145522Sdarrenr/* we're looking for a table entry, based on the destination address. */ 3130145522Sdarrenr/* */ 3131145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 3132145522Sdarrenr/* */ 3133145522Sdarrenr/* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */ 3134145522Sdarrenr/* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 3135145522Sdarrenr/* */ 3136145522Sdarrenr/* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 3137145522Sdarrenr/* the packet is of said protocol */ 3138145522Sdarrenr/* ------------------------------------------------------------------------ */ 3139145522Sdarrenrnat_t *nat_inlookup(fin, flags, p, src, mapdst) 314092685Sdarrenrfr_info_t *fin; 3141145522Sdarrenru_int flags, p; 314253642Sguidostruct in_addr src , mapdst; 314353642Sguido{ 3144145522Sdarrenr u_short sport, dport; 3145145522Sdarrenr grehdr_t *gre; 314692685Sdarrenr ipnat_t *ipn; 3147145522Sdarrenr u_int sflags; 3148145522Sdarrenr nat_t *nat; 3149145522Sdarrenr int nflags; 3150145522Sdarrenr u_32_t dst; 315192685Sdarrenr void *ifp; 315253642Sguido u_int hv; 315353642Sguido 3154161356Sguido ifp = fin->fin_ifp; 3155145522Sdarrenr sport = 0; 3156145522Sdarrenr dport = 0; 3157145522Sdarrenr gre = NULL; 315867614Sdarrenr dst = mapdst.s_addr; 3159145522Sdarrenr sflags = flags & NAT_TCPUDPICMP; 3160145522Sdarrenr 3161145522Sdarrenr switch (p) 3162145522Sdarrenr { 3163145522Sdarrenr case IPPROTO_TCP : 3164145522Sdarrenr case IPPROTO_UDP : 316592685Sdarrenr sport = htons(fin->fin_data[0]); 316692685Sdarrenr dport = htons(fin->fin_data[1]); 3167145522Sdarrenr break; 3168145522Sdarrenr case IPPROTO_ICMP : 3169145522Sdarrenr if (flags & IPN_ICMPERR) 3170145522Sdarrenr sport = fin->fin_data[1]; 3171145522Sdarrenr else 3172145522Sdarrenr dport = fin->fin_data[1]; 3173145522Sdarrenr break; 3174145522Sdarrenr default : 3175145522Sdarrenr break; 317692685Sdarrenr } 317753642Sguido 3178145522Sdarrenr 3179145522Sdarrenr if ((flags & SI_WILDP) != 0) 3180145522Sdarrenr goto find_in_wild_ports; 3181145522Sdarrenr 318280482Sdarrenr hv = NAT_HASH_FN(dst, dport, 0xffffffff); 318380482Sdarrenr hv = NAT_HASH_FN(src.s_addr, hv + sport, ipf_nattable_sz); 318453642Sguido nat = nat_table[1][hv]; 318553642Sguido for (; nat; nat = nat->nat_hnext[1]) { 3186161356Sguido if (nat->nat_ifps[0] != NULL) { 3187161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 3188161356Sguido continue; 3189161356Sguido } else if (ifp != NULL) 3190161356Sguido nat->nat_ifps[0] = ifp; 3191161356Sguido 319253642Sguido nflags = nat->nat_flags; 3193145522Sdarrenr 3194145522Sdarrenr if (nat->nat_oip.s_addr == src.s_addr && 319567614Sdarrenr nat->nat_outip.s_addr == dst && 3196145522Sdarrenr (((p == 0) && 3197145522Sdarrenr (sflags == (nat->nat_flags & IPN_TCPUDPICMP))) 3198145522Sdarrenr || (p == nat->nat_p))) { 319992685Sdarrenr switch (p) 320092685Sdarrenr { 3201145522Sdarrenr#if 0 3202145522Sdarrenr case IPPROTO_GRE : 3203145522Sdarrenr if (nat->nat_call[1] != fin->fin_data[0]) 3204145522Sdarrenr continue; 3205145522Sdarrenr break; 3206145522Sdarrenr#endif 3207145522Sdarrenr case IPPROTO_ICMP : 3208145522Sdarrenr if ((flags & IPN_ICMPERR) != 0) { 3209145522Sdarrenr if (nat->nat_outport != sport) 3210145522Sdarrenr continue; 3211145522Sdarrenr } else { 3212145522Sdarrenr if (nat->nat_outport != dport) 3213145522Sdarrenr continue; 3214145522Sdarrenr } 3215145522Sdarrenr break; 321692685Sdarrenr case IPPROTO_TCP : 321792685Sdarrenr case IPPROTO_UDP : 321892685Sdarrenr if (nat->nat_oport != sport) 321992685Sdarrenr continue; 322092685Sdarrenr if (nat->nat_outport != dport) 322192685Sdarrenr continue; 322292685Sdarrenr break; 322392685Sdarrenr default : 322492685Sdarrenr break; 322592685Sdarrenr } 322692685Sdarrenr 322792685Sdarrenr ipn = nat->nat_ptr; 322892685Sdarrenr if ((ipn != NULL) && (nat->nat_aps != NULL)) 322992685Sdarrenr if (appr_match(fin, nat) != 0) 323092685Sdarrenr continue; 323153642Sguido return nat; 323292685Sdarrenr } 323353642Sguido } 3234145522Sdarrenr 3235145522Sdarrenr /* 3236145522Sdarrenr * So if we didn't find it but there are wildcard members in the hash 3237145522Sdarrenr * table, go back and look for them. We do this search and update here 3238145522Sdarrenr * because it is modifying the NAT table and we want to do this only 3239145522Sdarrenr * for the first packet that matches. The exception, of course, is 3240145522Sdarrenr * for "dummy" (FI_IGNORE) lookups. 3241145522Sdarrenr */ 3242145522Sdarrenrfind_in_wild_ports: 3243145522Sdarrenr if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) 324467614Sdarrenr return NULL; 3245145522Sdarrenr if (nat_stats.ns_wilds == 0) 3246145522Sdarrenr return NULL; 3247145522Sdarrenr 3248145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 3249145522Sdarrenr 325080482Sdarrenr hv = NAT_HASH_FN(dst, 0, 0xffffffff); 3251145522Sdarrenr hv = NAT_HASH_FN(src.s_addr, hv, ipf_nattable_sz); 3252145522Sdarrenr 3253145522Sdarrenr WRITE_ENTER(&ipf_nat); 3254145522Sdarrenr 325567614Sdarrenr nat = nat_table[1][hv]; 325667614Sdarrenr for (; nat; nat = nat->nat_hnext[1]) { 3257161356Sguido if (nat->nat_ifps[0] != NULL) { 3258161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 3259161356Sguido continue; 3260161356Sguido } else if (ifp != NULL) 3261161356Sguido nat->nat_ifps[0] = ifp; 3262145522Sdarrenr 3263145522Sdarrenr if (nat->nat_p != fin->fin_p) 326467614Sdarrenr continue; 326567614Sdarrenr if (nat->nat_oip.s_addr != src.s_addr || 326667614Sdarrenr nat->nat_outip.s_addr != dst) 326767614Sdarrenr continue; 3268145522Sdarrenr 3269145522Sdarrenr nflags = nat->nat_flags; 3270145522Sdarrenr if (!(nflags & (NAT_TCPUDP|SI_WILDP))) 3271145522Sdarrenr continue; 3272145522Sdarrenr 3273145522Sdarrenr if (nat_wildok(nat, (int)sport, (int)dport, nflags, 3274145522Sdarrenr NAT_INBOUND) == 1) { 3275145522Sdarrenr if ((fin->fin_flx & FI_IGNORE) != 0) 3276145522Sdarrenr break; 3277145522Sdarrenr if ((nflags & SI_CLONE) != 0) { 3278145522Sdarrenr nat = fr_natclone(fin, nat); 3279145522Sdarrenr if (nat == NULL) 3280145522Sdarrenr break; 3281145522Sdarrenr } else { 3282145522Sdarrenr MUTEX_ENTER(&ipf_nat_new); 3283145522Sdarrenr nat_stats.ns_wilds--; 3284145522Sdarrenr MUTEX_EXIT(&ipf_nat_new); 3285145522Sdarrenr } 3286145522Sdarrenr nat->nat_oport = sport; 3287145522Sdarrenr nat->nat_outport = dport; 3288145522Sdarrenr nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 3289145522Sdarrenr nat_tabmove(nat); 329067614Sdarrenr break; 329167614Sdarrenr } 329267614Sdarrenr } 3293145522Sdarrenr 3294145522Sdarrenr MUTEX_DOWNGRADE(&ipf_nat); 3295145522Sdarrenr 329667614Sdarrenr return nat; 329753642Sguido} 329853642Sguido 329953642Sguido 3300145522Sdarrenr/* ------------------------------------------------------------------------ */ 3301145522Sdarrenr/* Function: nat_tabmove */ 3302145522Sdarrenr/* Returns: Nil */ 3303145522Sdarrenr/* Parameters: nat(I) - pointer to NAT structure */ 3304145522Sdarrenr/* Write Lock: ipf_nat */ 3305145522Sdarrenr/* */ 3306145522Sdarrenr/* This function is only called for TCP/UDP NAT table entries where the */ 3307145522Sdarrenr/* original was placed in the table without hashing on the ports and we now */ 3308145522Sdarrenr/* want to include hashing on port numbers. */ 3309145522Sdarrenr/* ------------------------------------------------------------------------ */ 3310145522Sdarrenrstatic void nat_tabmove(nat) 331167614Sdarrenrnat_t *nat; 331267614Sdarrenr{ 331367614Sdarrenr nat_t **natp; 3314145522Sdarrenr u_int hv; 331567614Sdarrenr 3316145522Sdarrenr if (nat->nat_flags & SI_CLONE) 3317145522Sdarrenr return; 331872006Sdarrenr 331967614Sdarrenr /* 332067614Sdarrenr * Remove the NAT entry from the old location 332167614Sdarrenr */ 332267614Sdarrenr if (nat->nat_hnext[0]) 332367614Sdarrenr nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 332467614Sdarrenr *nat->nat_phnext[0] = nat->nat_hnext[0]; 3325145522Sdarrenr nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--; 332667614Sdarrenr 332767614Sdarrenr if (nat->nat_hnext[1]) 332867853Sdarrenr nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 332967614Sdarrenr *nat->nat_phnext[1] = nat->nat_hnext[1]; 3330145522Sdarrenr nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--; 333167614Sdarrenr 333267853Sdarrenr /* 333367853Sdarrenr * Add into the NAT table in the new position 333467853Sdarrenr */ 3335145522Sdarrenr hv = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 0xffffffff); 3336145522Sdarrenr hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport, 3337145522Sdarrenr ipf_nattable_sz); 3338145522Sdarrenr nat->nat_hv[0] = hv; 333967614Sdarrenr natp = &nat_table[0][hv]; 334067614Sdarrenr if (*natp) 334167614Sdarrenr (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 334267614Sdarrenr nat->nat_phnext[0] = natp; 334367614Sdarrenr nat->nat_hnext[0] = *natp; 334467614Sdarrenr *natp = nat; 3345145522Sdarrenr nat_stats.ns_bucketlen[0][hv]++; 334667614Sdarrenr 3347145522Sdarrenr hv = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, 0xffffffff); 3348145522Sdarrenr hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport, 3349145522Sdarrenr ipf_nattable_sz); 3350145522Sdarrenr nat->nat_hv[1] = hv; 335167614Sdarrenr natp = &nat_table[1][hv]; 335267614Sdarrenr if (*natp) 335367614Sdarrenr (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 335467614Sdarrenr nat->nat_phnext[1] = natp; 335567614Sdarrenr nat->nat_hnext[1] = *natp; 335667614Sdarrenr *natp = nat; 3357145522Sdarrenr nat_stats.ns_bucketlen[1][hv]++; 335867614Sdarrenr} 335967614Sdarrenr 336067614Sdarrenr 3361145522Sdarrenr/* ------------------------------------------------------------------------ */ 3362145522Sdarrenr/* Function: nat_outlookup */ 3363145522Sdarrenr/* Returns: nat_t* - NULL == no match, */ 3364145522Sdarrenr/* else pointer to matching NAT entry */ 3365145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3366145522Sdarrenr/* flags(I) - NAT flags for this packet */ 3367145522Sdarrenr/* p(I) - protocol for this packet */ 3368145522Sdarrenr/* src(I) - source IP address */ 3369145522Sdarrenr/* dst(I) - destination IP address */ 3370145522Sdarrenr/* rw(I) - 1 == write lock on ipf_nat held, 0 == read lock. */ 3371145522Sdarrenr/* */ 3372145522Sdarrenr/* Lookup a nat entry based on the source 'real' ip address/port and */ 3373145522Sdarrenr/* destination address/port. We use this lookup when sending a packet out, */ 3374145522Sdarrenr/* we're looking for a table entry, based on the source address. */ 3375145522Sdarrenr/* */ 3376145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 3377145522Sdarrenr/* */ 3378145522Sdarrenr/* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */ 3379145522Sdarrenr/* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 3380145522Sdarrenr/* */ 3381145522Sdarrenr/* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 3382145522Sdarrenr/* the packet is of said protocol */ 3383145522Sdarrenr/* ------------------------------------------------------------------------ */ 3384145522Sdarrenrnat_t *nat_outlookup(fin, flags, p, src, dst) 338592685Sdarrenrfr_info_t *fin; 3386145522Sdarrenru_int flags, p; 338753642Sguidostruct in_addr src , dst; 338853642Sguido{ 3389145522Sdarrenr u_short sport, dport; 3390145522Sdarrenr u_int sflags; 339192685Sdarrenr ipnat_t *ipn; 339267614Sdarrenr u_32_t srcip; 3393145522Sdarrenr nat_t *nat; 3394145522Sdarrenr int nflags; 339592685Sdarrenr void *ifp; 339653642Sguido u_int hv; 339753642Sguido 339892685Sdarrenr ifp = fin->fin_ifp; 339967614Sdarrenr srcip = src.s_addr; 3400145522Sdarrenr sflags = flags & IPN_TCPUDPICMP; 3401145522Sdarrenr sport = 0; 3402145522Sdarrenr dport = 0; 3403145522Sdarrenr 3404145522Sdarrenr switch (p) 3405145522Sdarrenr { 3406145522Sdarrenr case IPPROTO_TCP : 3407145522Sdarrenr case IPPROTO_UDP : 3408145522Sdarrenr sport = htons(fin->fin_data[0]); 3409145522Sdarrenr dport = htons(fin->fin_data[1]); 3410145522Sdarrenr break; 3411145522Sdarrenr case IPPROTO_ICMP : 3412145522Sdarrenr if (flags & IPN_ICMPERR) 3413145522Sdarrenr sport = fin->fin_data[1]; 3414145522Sdarrenr else 3415145522Sdarrenr dport = fin->fin_data[1]; 3416145522Sdarrenr break; 3417145522Sdarrenr default : 3418145522Sdarrenr break; 341992685Sdarrenr } 342053642Sguido 3421145522Sdarrenr if ((flags & SI_WILDP) != 0) 3422145522Sdarrenr goto find_out_wild_ports; 3423145522Sdarrenr 342480482Sdarrenr hv = NAT_HASH_FN(srcip, sport, 0xffffffff); 342580482Sdarrenr hv = NAT_HASH_FN(dst.s_addr, hv + dport, ipf_nattable_sz); 342653642Sguido nat = nat_table[0][hv]; 342753642Sguido for (; nat; nat = nat->nat_hnext[0]) { 3428161356Sguido if (nat->nat_ifps[1] != NULL) { 3429161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 3430161356Sguido continue; 3431161356Sguido } else if (ifp != NULL) 3432161356Sguido nat->nat_ifps[1] = ifp; 3433161356Sguido 343453642Sguido nflags = nat->nat_flags; 343553642Sguido 3436145522Sdarrenr if (nat->nat_inip.s_addr == srcip && 343753642Sguido nat->nat_oip.s_addr == dst.s_addr && 3438145522Sdarrenr (((p == 0) && (sflags == (nflags & NAT_TCPUDPICMP))) 3439145522Sdarrenr || (p == nat->nat_p))) { 344092685Sdarrenr switch (p) 344192685Sdarrenr { 3442145522Sdarrenr#if 0 3443145522Sdarrenr case IPPROTO_GRE : 3444145522Sdarrenr if (nat->nat_call[1] != fin->fin_data[0]) 3445145522Sdarrenr continue; 3446145522Sdarrenr break; 3447145522Sdarrenr#endif 344892685Sdarrenr case IPPROTO_TCP : 344992685Sdarrenr case IPPROTO_UDP : 345092685Sdarrenr if (nat->nat_oport != dport) 345192685Sdarrenr continue; 345292685Sdarrenr if (nat->nat_inport != sport) 345392685Sdarrenr continue; 345492685Sdarrenr break; 345592685Sdarrenr default : 345692685Sdarrenr break; 345792685Sdarrenr } 345892685Sdarrenr 345992685Sdarrenr ipn = nat->nat_ptr; 346092685Sdarrenr if ((ipn != NULL) && (nat->nat_aps != NULL)) 346192685Sdarrenr if (appr_match(fin, nat) != 0) 346292685Sdarrenr continue; 346353642Sguido return nat; 346492685Sdarrenr } 346553642Sguido } 3466145522Sdarrenr 3467145522Sdarrenr /* 3468145522Sdarrenr * So if we didn't find it but there are wildcard members in the hash 3469145522Sdarrenr * table, go back and look for them. We do this search and update here 3470145522Sdarrenr * because it is modifying the NAT table and we want to do this only 3471145522Sdarrenr * for the first packet that matches. The exception, of course, is 3472145522Sdarrenr * for "dummy" (FI_IGNORE) lookups. 3473145522Sdarrenr */ 3474145522Sdarrenrfind_out_wild_ports: 3475145522Sdarrenr if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) 347667614Sdarrenr return NULL; 3477145522Sdarrenr if (nat_stats.ns_wilds == 0) 3478145522Sdarrenr return NULL; 347992685Sdarrenr 3480145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 3481145522Sdarrenr 3482145522Sdarrenr hv = NAT_HASH_FN(srcip, 0, 0xffffffff); 3483145522Sdarrenr hv = NAT_HASH_FN(dst.s_addr, hv, ipf_nattable_sz); 3484145522Sdarrenr 3485145522Sdarrenr WRITE_ENTER(&ipf_nat); 3486145522Sdarrenr 348767614Sdarrenr nat = nat_table[0][hv]; 348867614Sdarrenr for (; nat; nat = nat->nat_hnext[0]) { 3489161356Sguido if (nat->nat_ifps[1] != NULL) { 3490161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 3491161356Sguido continue; 3492161356Sguido } else if (ifp != NULL) 3493161356Sguido nat->nat_ifps[1] = ifp; 3494145522Sdarrenr 3495145522Sdarrenr if (nat->nat_p != fin->fin_p) 349667614Sdarrenr continue; 349767614Sdarrenr if ((nat->nat_inip.s_addr != srcip) || 349867614Sdarrenr (nat->nat_oip.s_addr != dst.s_addr)) 349967614Sdarrenr continue; 3500145522Sdarrenr 3501145522Sdarrenr nflags = nat->nat_flags; 3502145522Sdarrenr if (!(nflags & (NAT_TCPUDP|SI_WILDP))) 3503145522Sdarrenr continue; 3504145522Sdarrenr 3505145522Sdarrenr if (nat_wildok(nat, (int)sport, (int)dport, nflags, 3506145522Sdarrenr NAT_OUTBOUND) == 1) { 3507145522Sdarrenr if ((fin->fin_flx & FI_IGNORE) != 0) 3508145522Sdarrenr break; 3509145522Sdarrenr if ((nflags & SI_CLONE) != 0) { 3510145522Sdarrenr nat = fr_natclone(fin, nat); 3511145522Sdarrenr if (nat == NULL) 3512145522Sdarrenr break; 3513145522Sdarrenr } else { 3514145522Sdarrenr MUTEX_ENTER(&ipf_nat_new); 3515145522Sdarrenr nat_stats.ns_wilds--; 3516145522Sdarrenr MUTEX_EXIT(&ipf_nat_new); 3517145522Sdarrenr } 3518145522Sdarrenr nat->nat_inport = sport; 3519145522Sdarrenr nat->nat_oport = dport; 3520145522Sdarrenr if (nat->nat_outport == 0) 3521145522Sdarrenr nat->nat_outport = sport; 3522145522Sdarrenr nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 3523145522Sdarrenr nat_tabmove(nat); 352467614Sdarrenr break; 352567614Sdarrenr } 352667614Sdarrenr } 3527145522Sdarrenr 3528145522Sdarrenr MUTEX_DOWNGRADE(&ipf_nat); 3529145522Sdarrenr 353067614Sdarrenr return nat; 353153642Sguido} 353253642Sguido 353353642Sguido 3534145522Sdarrenr/* ------------------------------------------------------------------------ */ 3535145522Sdarrenr/* Function: nat_lookupredir */ 3536145522Sdarrenr/* Returns: nat_t* - NULL == no match, */ 3537145522Sdarrenr/* else pointer to matching NAT entry */ 3538145522Sdarrenr/* Parameters: np(I) - pointer to description of packet to find NAT table */ 3539145522Sdarrenr/* entry for. */ 3540145522Sdarrenr/* */ 3541145522Sdarrenr/* Lookup the NAT tables to search for a matching redirect */ 3542161356Sguido/* The contents of natlookup_t should imitate those found in a packet that */ 3543161356Sguido/* would be translated - ie a packet coming in for RDR or going out for MAP.*/ 3544161356Sguido/* We can do the lookup in one of two ways, imitating an inbound or */ 3545161356Sguido/* outbound packet. By default we assume outbound, unless IPN_IN is set. */ 3546161356Sguido/* For IN, the fields are set as follows: */ 3547161356Sguido/* nl_real* = source information */ 3548161356Sguido/* nl_out* = destination information (translated) */ 3549161356Sguido/* For an out packet, the fields are set like this: */ 3550161356Sguido/* nl_in* = source information (untranslated) */ 3551161356Sguido/* nl_out* = destination information (translated) */ 3552145522Sdarrenr/* ------------------------------------------------------------------------ */ 355353642Sguidonat_t *nat_lookupredir(np) 3554145522Sdarrenrnatlookup_t *np; 355553642Sguido{ 3556145522Sdarrenr fr_info_t fi; 355753642Sguido nat_t *nat; 355853642Sguido 355992685Sdarrenr bzero((char *)&fi, sizeof(fi)); 3560145522Sdarrenr if (np->nl_flags & IPN_IN) { 3561145522Sdarrenr fi.fin_data[0] = ntohs(np->nl_realport); 3562145522Sdarrenr fi.fin_data[1] = ntohs(np->nl_outport); 3563145522Sdarrenr } else { 3564145522Sdarrenr fi.fin_data[0] = ntohs(np->nl_inport); 3565145522Sdarrenr fi.fin_data[1] = ntohs(np->nl_outport); 3566145522Sdarrenr } 3567145522Sdarrenr if (np->nl_flags & IPN_TCP) 3568145522Sdarrenr fi.fin_p = IPPROTO_TCP; 3569145522Sdarrenr else if (np->nl_flags & IPN_UDP) 3570145522Sdarrenr fi.fin_p = IPPROTO_UDP; 3571145522Sdarrenr else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) 3572145522Sdarrenr fi.fin_p = IPPROTO_ICMP; 357392685Sdarrenr 357453642Sguido /* 3575145522Sdarrenr * We can do two sorts of lookups: 3576145522Sdarrenr * - IPN_IN: we have the `real' and `out' address, look for `in'. 3577145522Sdarrenr * - default: we have the `in' and `out' address, look for `real'. 357853642Sguido */ 3579145522Sdarrenr if (np->nl_flags & IPN_IN) { 3580145522Sdarrenr if ((nat = nat_inlookup(&fi, np->nl_flags, fi.fin_p, 3581145522Sdarrenr np->nl_realip, np->nl_outip))) { 3582145522Sdarrenr np->nl_inip = nat->nat_inip; 3583145522Sdarrenr np->nl_inport = nat->nat_inport; 3584145522Sdarrenr } 3585145522Sdarrenr } else { 3586145522Sdarrenr /* 3587145522Sdarrenr * If nl_inip is non null, this is a lookup based on the real 3588145522Sdarrenr * ip address. Else, we use the fake. 3589145522Sdarrenr */ 3590145522Sdarrenr if ((nat = nat_outlookup(&fi, np->nl_flags, fi.fin_p, 3591145522Sdarrenr np->nl_inip, np->nl_outip))) { 3592145522Sdarrenr 3593145522Sdarrenr if ((np->nl_flags & IPN_FINDFORWARD) != 0) { 3594145522Sdarrenr fr_info_t fin; 3595145522Sdarrenr bzero((char *)&fin, sizeof(fin)); 3596145522Sdarrenr fin.fin_p = nat->nat_p; 3597145522Sdarrenr fin.fin_data[0] = ntohs(nat->nat_outport); 3598145522Sdarrenr fin.fin_data[1] = ntohs(nat->nat_oport); 3599145522Sdarrenr if (nat_inlookup(&fin, np->nl_flags, fin.fin_p, 3600145522Sdarrenr nat->nat_outip, 3601145522Sdarrenr nat->nat_oip) != NULL) { 3602145522Sdarrenr np->nl_flags &= ~IPN_FINDFORWARD; 3603145522Sdarrenr } 3604145522Sdarrenr } 3605145522Sdarrenr 3606145522Sdarrenr np->nl_realip = nat->nat_outip; 3607145522Sdarrenr np->nl_realport = nat->nat_outport; 3608145522Sdarrenr } 3609145522Sdarrenr } 3610145522Sdarrenr 361153642Sguido return nat; 361253642Sguido} 361353642Sguido 361453642Sguido 3615145522Sdarrenr/* ------------------------------------------------------------------------ */ 3616145522Sdarrenr/* Function: nat_match */ 3617145522Sdarrenr/* Returns: int - 0 == no match, 1 == match */ 3618145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3619145522Sdarrenr/* np(I) - pointer to NAT rule */ 3620145522Sdarrenr/* */ 3621145522Sdarrenr/* Pull the matching of a packet against a NAT rule out of that complex */ 3622145522Sdarrenr/* loop inside fr_checknatin() and lay it out properly in its own function. */ 3623145522Sdarrenr/* ------------------------------------------------------------------------ */ 3624145522Sdarrenrstatic int nat_match(fin, np) 362560852Sdarrenrfr_info_t *fin; 362660852Sdarrenripnat_t *np; 362760852Sdarrenr{ 362860852Sdarrenr frtuc_t *ft; 362960852Sdarrenr 3630145522Sdarrenr if (fin->fin_v != 4) 363160852Sdarrenr return 0; 363260852Sdarrenr 363392685Sdarrenr if (np->in_p && fin->fin_p != np->in_p) 363460852Sdarrenr return 0; 3635145522Sdarrenr 363660852Sdarrenr if (fin->fin_out) { 363763523Sdarrenr if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK))) 363860852Sdarrenr return 0; 363963523Sdarrenr if (((fin->fin_fi.fi_saddr & np->in_inmsk) != np->in_inip) 364063523Sdarrenr ^ ((np->in_flags & IPN_NOTSRC) != 0)) 364160852Sdarrenr return 0; 364263523Sdarrenr if (((fin->fin_fi.fi_daddr & np->in_srcmsk) != np->in_srcip) 364363523Sdarrenr ^ ((np->in_flags & IPN_NOTDST) != 0)) 364460852Sdarrenr return 0; 364560852Sdarrenr } else { 364663523Sdarrenr if (!(np->in_redir & NAT_REDIRECT)) 364760852Sdarrenr return 0; 364863523Sdarrenr if (((fin->fin_fi.fi_saddr & np->in_srcmsk) != np->in_srcip) 364963523Sdarrenr ^ ((np->in_flags & IPN_NOTSRC) != 0)) 365063523Sdarrenr return 0; 365163523Sdarrenr if (((fin->fin_fi.fi_daddr & np->in_outmsk) != np->in_outip) 365263523Sdarrenr ^ ((np->in_flags & IPN_NOTDST) != 0)) 365363523Sdarrenr return 0; 365460852Sdarrenr } 365560852Sdarrenr 365660852Sdarrenr ft = &np->in_tuc; 3657145522Sdarrenr if (!(fin->fin_flx & FI_TCPUDP) || 3658145522Sdarrenr (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { 365960852Sdarrenr if (ft->ftu_scmp || ft->ftu_dcmp) 366060852Sdarrenr return 0; 366160852Sdarrenr return 1; 366260852Sdarrenr } 366360852Sdarrenr 3664145522Sdarrenr return fr_tcpudpchk(fin, ft); 366560852Sdarrenr} 366660852Sdarrenr 366760852Sdarrenr 3668145522Sdarrenr/* ------------------------------------------------------------------------ */ 3669145522Sdarrenr/* Function: nat_update */ 3670145522Sdarrenr/* Returns: Nil */ 3671145522Sdarrenr/* Parameters: nat(I) - pointer to NAT structure */ 3672145522Sdarrenr/* np(I) - pointer to NAT rule */ 3673145522Sdarrenr/* */ 3674145522Sdarrenr/* Updates the lifetime of a NAT table entry for non-TCP packets. Must be */ 3675145522Sdarrenr/* called with fin_rev updated - i.e. after calling nat_proto(). */ 3676145522Sdarrenr/* ------------------------------------------------------------------------ */ 3677145522Sdarrenrvoid nat_update(fin, nat, np) 367853642Sguidofr_info_t *fin; 3679145522Sdarrenrnat_t *nat; 3680145522Sdarrenripnat_t *np; 368153642Sguido{ 3682145522Sdarrenr ipftq_t *ifq, *ifq2; 3683145522Sdarrenr ipftqent_t *tqe; 3684145522Sdarrenr 3685145522Sdarrenr MUTEX_ENTER(&nat->nat_lock); 3686145522Sdarrenr tqe = &nat->nat_tqe; 3687145522Sdarrenr ifq = tqe->tqe_ifq; 3688145522Sdarrenr 3689145522Sdarrenr /* 3690145522Sdarrenr * We allow over-riding of NAT timeouts from NAT rules, even for 3691145522Sdarrenr * TCP, however, if it is TCP and there is no rule timeout set, 3692145522Sdarrenr * then do not update the timeout here. 3693145522Sdarrenr */ 3694145522Sdarrenr if (np != NULL) 3695145522Sdarrenr ifq2 = np->in_tqehead[fin->fin_rev]; 3696145522Sdarrenr else 3697145522Sdarrenr ifq2 = NULL; 3698145522Sdarrenr 3699145522Sdarrenr if (nat->nat_p == IPPROTO_TCP && ifq2 == NULL) { 3700172776Sdarrenr u_32_t end, ack; 3701172776Sdarrenr u_char tcpflags; 3702172776Sdarrenr tcphdr_t *tcp; 3703172776Sdarrenr int dsize; 3704172776Sdarrenr 3705172776Sdarrenr tcp = fin->fin_dp; 3706172776Sdarrenr tcpflags = tcp->th_flags; 3707172776Sdarrenr dsize = fin->fin_dlen - (TCP_OFF(tcp) << 2) + 3708172776Sdarrenr ((tcpflags & TH_SYN) ? 1 : 0) + 3709172776Sdarrenr ((tcpflags & TH_FIN) ? 1 : 0); 3710172776Sdarrenr 3711172776Sdarrenr ack = ntohl(tcp->th_ack); 3712172776Sdarrenr end = ntohl(tcp->th_seq) + dsize; 3713172776Sdarrenr 3714172776Sdarrenr if (SEQ_GT(ack, nat->nat_seqnext[1 - fin->fin_rev])) 3715172776Sdarrenr nat->nat_seqnext[1 - fin->fin_rev] = ack; 3716172776Sdarrenr 3717172776Sdarrenr if (nat->nat_seqnext[fin->fin_rev] == 0) 3718172776Sdarrenr nat->nat_seqnext[fin->fin_rev] = end; 3719172776Sdarrenr 3720145522Sdarrenr (void) fr_tcp_age(&nat->nat_tqe, fin, nat_tqb, 0); 3721145522Sdarrenr } else { 3722145522Sdarrenr if (ifq2 == NULL) { 3723145522Sdarrenr if (nat->nat_p == IPPROTO_UDP) 3724145522Sdarrenr ifq2 = &nat_udptq; 3725145522Sdarrenr else if (nat->nat_p == IPPROTO_ICMP) 3726145522Sdarrenr ifq2 = &nat_icmptq; 3727145522Sdarrenr else 3728145522Sdarrenr ifq2 = &nat_iptq; 3729145522Sdarrenr } 3730145522Sdarrenr 3731145522Sdarrenr fr_movequeue(tqe, ifq, ifq2); 3732145522Sdarrenr } 3733145522Sdarrenr MUTEX_EXIT(&nat->nat_lock); 3734145522Sdarrenr} 3735145522Sdarrenr 3736145522Sdarrenr 3737145522Sdarrenr/* ------------------------------------------------------------------------ */ 3738145522Sdarrenr/* Function: fr_checknatout */ 3739145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 3740145522Sdarrenr/* 0 == no packet translation occurred, */ 3741145522Sdarrenr/* 1 == packet was successfully translated. */ 3742145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3743145522Sdarrenr/* passp(I) - pointer to filtering result flags */ 3744145522Sdarrenr/* */ 3745145522Sdarrenr/* Check to see if an outcoming packet should be changed. ICMP packets are */ 3746145522Sdarrenr/* first checked to see if they match an existing entry (if an error), */ 3747145522Sdarrenr/* otherwise a search of the current NAT table is made. If neither results */ 3748145522Sdarrenr/* in a match then a search for a matching NAT rule is made. Create a new */ 3749145522Sdarrenr/* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 3750145522Sdarrenr/* packet header(s) as required. */ 3751145522Sdarrenr/* ------------------------------------------------------------------------ */ 3752145522Sdarrenrint fr_checknatout(fin, passp) 3753145522Sdarrenrfr_info_t *fin; 3754145522Sdarrenru_32_t *passp; 3755145522Sdarrenr{ 3756145522Sdarrenr struct ifnet *ifp, *sifp; 3757145522Sdarrenr icmphdr_t *icmp = NULL; 375853642Sguido tcphdr_t *tcp = NULL; 3759145522Sdarrenr int rval, natfailed; 3760145522Sdarrenr ipnat_t *np = NULL; 3761145522Sdarrenr u_int nflags = 0; 3762145522Sdarrenr u_32_t ipa, iph; 3763145522Sdarrenr int natadd = 1; 376453642Sguido frentry_t *fr; 376553642Sguido nat_t *nat; 376653642Sguido 3767145522Sdarrenr if (nat_stats.ns_rules == 0 || fr_nat_lock != 0) 376853642Sguido return 0; 376953642Sguido 3770145522Sdarrenr natfailed = 0; 3771145522Sdarrenr fr = fin->fin_fr; 3772145522Sdarrenr sifp = fin->fin_ifp; 3773170268Sdarrenr if (fr != NULL) { 3774170268Sdarrenr ifp = fr->fr_tifs[fin->fin_rev].fd_ifp; 3775170268Sdarrenr if ((ifp != NULL) && (ifp != (void *)-1)) 3776170268Sdarrenr fin->fin_ifp = ifp; 3777170268Sdarrenr } 377892685Sdarrenr ifp = fin->fin_ifp; 377953642Sguido 3780145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 3781145522Sdarrenr switch (fin->fin_p) 3782145522Sdarrenr { 3783145522Sdarrenr case IPPROTO_TCP : 378453642Sguido nflags = IPN_TCP; 3785145522Sdarrenr break; 3786145522Sdarrenr case IPPROTO_UDP : 378753642Sguido nflags = IPN_UDP; 3788145522Sdarrenr break; 3789145522Sdarrenr case IPPROTO_ICMP : 3790145522Sdarrenr icmp = fin->fin_dp; 3791145522Sdarrenr 3792145522Sdarrenr /* 3793145522Sdarrenr * This is an incoming packet, so the destination is 3794145522Sdarrenr * the icmp_id and the source port equals 0 3795145522Sdarrenr */ 3796145522Sdarrenr if (nat_icmpquerytype4(icmp->icmp_type)) 3797145522Sdarrenr nflags = IPN_ICMPQUERY; 3798145522Sdarrenr break; 3799145522Sdarrenr default : 3800145522Sdarrenr break; 380153642Sguido } 3802145522Sdarrenr 3803145522Sdarrenr if ((nflags & IPN_TCPUDP)) 3804145522Sdarrenr tcp = fin->fin_dp; 380553642Sguido } 380653642Sguido 380792685Sdarrenr ipa = fin->fin_saddr; 380853642Sguido 380953642Sguido READ_ENTER(&ipf_nat); 381060852Sdarrenr 3811180778Sdarrenr if (((fin->fin_flx & FI_ICMPERR) != 0) && 3812145522Sdarrenr (nat = nat_icmperror(fin, &nflags, NAT_OUTBOUND))) 3813145522Sdarrenr /*EMPTY*/; 3814145522Sdarrenr else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin))) 381553642Sguido natadd = 0; 3816145522Sdarrenr else if ((nat = nat_outlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, 3817145522Sdarrenr fin->fin_src, fin->fin_dst))) { 381853642Sguido nflags = nat->nat_flags; 381953642Sguido } else { 3820145522Sdarrenr u_32_t hv, msk, nmsk; 382192685Sdarrenr 382253642Sguido /* 382353642Sguido * If there is no current entry in the nat table for this IP#, 382453642Sguido * create one for it (if there is a matching rule). 382553642Sguido */ 3826145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 3827145522Sdarrenr msk = 0xffffffff; 3828145522Sdarrenr nmsk = nat_masks; 3829145522Sdarrenr WRITE_ENTER(&ipf_nat); 383053642Sguidomaskloop: 383153642Sguido iph = ipa & htonl(msk); 383260852Sdarrenr hv = NAT_HASH_FN(iph, 0, ipf_natrules_sz); 383353642Sguido for (np = nat_rules[hv]; np; np = np->in_mnext) 383453642Sguido { 3835161356Sguido if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) 383660852Sdarrenr continue; 3837145522Sdarrenr if (np->in_v != fin->fin_v) 383860852Sdarrenr continue; 3839145522Sdarrenr if (np->in_p && (np->in_p != fin->fin_p)) 3840145522Sdarrenr continue; 3841145522Sdarrenr if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) 3842145522Sdarrenr continue; 384360852Sdarrenr if (np->in_flags & IPN_FILTER) { 3844145522Sdarrenr if (!nat_match(fin, np)) 384560852Sdarrenr continue; 384660852Sdarrenr } else if ((ipa & np->in_inmsk) != np->in_inip) 384760852Sdarrenr continue; 3848145522Sdarrenr 3849145522Sdarrenr if ((fr != NULL) && 3850145522Sdarrenr !fr_matchtag(&np->in_tag, &fr->fr_nattag)) 385192685Sdarrenr continue; 3852145522Sdarrenr 3853145522Sdarrenr if (*np->in_plabel != '\0') { 3854145522Sdarrenr if (((np->in_flags & IPN_FILTER) == 0) && 3855145522Sdarrenr (np->in_dport != tcp->th_dport)) 3856145522Sdarrenr continue; 3857145522Sdarrenr if (appr_ok(fin, tcp, np) == 0) 3858145522Sdarrenr continue; 3859145522Sdarrenr } 3860145522Sdarrenr 3861145522Sdarrenr if ((nat = nat_new(fin, np, NULL, nflags, 3862145522Sdarrenr NAT_OUTBOUND))) { 386392685Sdarrenr np->in_hits++; 386492685Sdarrenr break; 3865145522Sdarrenr } else 3866145522Sdarrenr natfailed = -1; 386753642Sguido } 3868145522Sdarrenr if ((np == NULL) && (nmsk != 0)) { 3869145522Sdarrenr while (nmsk) { 387053642Sguido msk <<= 1; 3871145522Sdarrenr if (nmsk & 0x80000000) 3872145522Sdarrenr break; 3873145522Sdarrenr nmsk <<= 1; 3874145522Sdarrenr } 3875145522Sdarrenr if (nmsk != 0) { 3876145522Sdarrenr nmsk <<= 1; 387753642Sguido goto maskloop; 3878145522Sdarrenr } 387953642Sguido } 388053642Sguido MUTEX_DOWNGRADE(&ipf_nat); 388153642Sguido } 388253642Sguido 3883145522Sdarrenr if (nat != NULL) { 3884145522Sdarrenr rval = fr_natout(fin, nat, natadd, nflags); 3885145522Sdarrenr if (rval == 1) { 3886145522Sdarrenr MUTEX_ENTER(&nat->nat_lock); 3887145522Sdarrenr nat->nat_ref++; 3888145522Sdarrenr MUTEX_EXIT(&nat->nat_lock); 3889170268Sdarrenr nat->nat_touched = fr_ticks; 3890145522Sdarrenr fin->fin_nat = nat; 3891145522Sdarrenr } 3892145522Sdarrenr } else 3893145522Sdarrenr rval = natfailed; 3894145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 3895145522Sdarrenr 3896145522Sdarrenr if (rval == -1) { 3897145522Sdarrenr if (passp != NULL) 3898145522Sdarrenr *passp = FR_BLOCK; 3899145522Sdarrenr fin->fin_flx |= FI_BADNAT; 3900145522Sdarrenr } 3901145522Sdarrenr fin->fin_ifp = sifp; 3902145522Sdarrenr return rval; 3903145522Sdarrenr} 3904145522Sdarrenr 3905145522Sdarrenr/* ------------------------------------------------------------------------ */ 3906145522Sdarrenr/* Function: fr_natout */ 3907145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 3908145522Sdarrenr/* 1 == packet was successfully translated. */ 3909145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3910145522Sdarrenr/* nat(I) - pointer to NAT structure */ 3911145522Sdarrenr/* natadd(I) - flag indicating if it is safe to add frag cache */ 3912145522Sdarrenr/* nflags(I) - NAT flags set for this packet */ 3913145522Sdarrenr/* */ 3914145522Sdarrenr/* Translate a packet coming "out" on an interface. */ 3915145522Sdarrenr/* ------------------------------------------------------------------------ */ 3916145522Sdarrenrint fr_natout(fin, nat, natadd, nflags) 3917145522Sdarrenrfr_info_t *fin; 3918145522Sdarrenrnat_t *nat; 3919145522Sdarrenrint natadd; 3920145522Sdarrenru_32_t nflags; 3921145522Sdarrenr{ 3922145522Sdarrenr icmphdr_t *icmp; 3923145522Sdarrenr u_short *csump; 3924145522Sdarrenr tcphdr_t *tcp; 3925145522Sdarrenr ipnat_t *np; 3926145522Sdarrenr int i; 3927145522Sdarrenr 3928145522Sdarrenr tcp = NULL; 3929145522Sdarrenr icmp = NULL; 3930145522Sdarrenr csump = NULL; 3931145522Sdarrenr np = nat->nat_ptr; 3932145522Sdarrenr 3933145522Sdarrenr if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL)) 3934145522Sdarrenr (void) fr_nat_newfrag(fin, 0, nat); 3935145522Sdarrenr 3936145522Sdarrenr MUTEX_ENTER(&nat->nat_lock); 3937145522Sdarrenr nat->nat_bytes[1] += fin->fin_plen; 3938145522Sdarrenr nat->nat_pkts[1]++; 3939145522Sdarrenr MUTEX_EXIT(&nat->nat_lock); 3940145522Sdarrenr 394172006Sdarrenr /* 3942145522Sdarrenr * Fix up checksums, not by recalculating them, but 3943145522Sdarrenr * simply computing adjustments. 3944145522Sdarrenr * This is only done for STREAMS based IP implementations where the 3945145522Sdarrenr * checksum has already been calculated by IP. In all other cases, 3946145522Sdarrenr * IPFilter is called before the checksum needs calculating so there 3947145522Sdarrenr * is no call to modify whatever is in the header now. 394872006Sdarrenr */ 3949145522Sdarrenr if (fin->fin_v == 4) { 395063523Sdarrenr if (nflags == IPN_ICMPERR) { 395163523Sdarrenr u_32_t s1, s2, sumd; 395263523Sdarrenr 395392685Sdarrenr s1 = LONG_SUM(ntohl(fin->fin_saddr)); 395463523Sdarrenr s2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)); 395563523Sdarrenr CALC_SUMD(s1, s2, sumd); 3956145522Sdarrenr fix_outcksum(fin, &fin->fin_ip->ip_sum, sumd); 395763523Sdarrenr } 3958153876Sguido#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 3959153876Sguido defined(linux) || defined(BRIDGE_IPF) 396063523Sdarrenr else { 3961153876Sguido /* 3962153876Sguido * Strictly speaking, this isn't necessary on BSD 3963153876Sguido * kernels because they do checksum calculation after 3964153876Sguido * this code has run BUT if ipfilter is being used 3965153876Sguido * to do NAT as a bridge, that code doesn't exist. 3966153876Sguido */ 396763523Sdarrenr if (nat->nat_dir == NAT_OUTBOUND) 3968145522Sdarrenr fix_outcksum(fin, &fin->fin_ip->ip_sum, 3969145522Sdarrenr nat->nat_ipsumd); 397063523Sdarrenr else 3971145522Sdarrenr fix_incksum(fin, &fin->fin_ip->ip_sum, 3972145522Sdarrenr nat->nat_ipsumd); 397363523Sdarrenr } 397453642Sguido#endif 3975145522Sdarrenr } 397653642Sguido 3977145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 3978145522Sdarrenr if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) { 3979145522Sdarrenr tcp = fin->fin_dp; 398053642Sguido 3981145522Sdarrenr tcp->th_sport = nat->nat_outport; 3982145522Sdarrenr fin->fin_data[0] = ntohs(nat->nat_outport); 3983145522Sdarrenr } 398453642Sguido 3985145522Sdarrenr if ((nat->nat_outport != 0) && (nflags & IPN_ICMPQUERY)) { 3986145522Sdarrenr icmp = fin->fin_dp; 3987145522Sdarrenr icmp->icmp_id = nat->nat_outport; 3988145522Sdarrenr } 3989110916Sdarrenr 3990145522Sdarrenr csump = nat_proto(fin, nat, nflags); 3991145522Sdarrenr } 3992110916Sdarrenr 3993145522Sdarrenr fin->fin_ip->ip_src = nat->nat_outip; 399453642Sguido 3995145522Sdarrenr nat_update(fin, nat, np); 399663523Sdarrenr 3997145522Sdarrenr /* 3998145522Sdarrenr * The above comments do not hold for layer 4 (or higher) checksums... 3999145522Sdarrenr */ 4000145522Sdarrenr if (csump != NULL) { 4001145522Sdarrenr if (nat->nat_dir == NAT_OUTBOUND) 4002145522Sdarrenr fix_outcksum(fin, csump, nat->nat_sumd[1]); 4003145522Sdarrenr else 4004145522Sdarrenr fix_incksum(fin, csump, nat->nat_sumd[1]); 4005145522Sdarrenr } 4006145522Sdarrenr#ifdef IPFILTER_SYNC 4007145522Sdarrenr ipfsync_update(SMC_NAT, fin, nat->nat_sync); 4008145522Sdarrenr#endif 4009145522Sdarrenr /* ------------------------------------------------------------- */ 4010145522Sdarrenr /* A few quick notes: */ 4011145522Sdarrenr /* Following are test conditions prior to calling the */ 4012145522Sdarrenr /* appr_check routine. */ 4013145522Sdarrenr /* */ 4014145522Sdarrenr /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 4015145522Sdarrenr /* with a redirect rule, we attempt to match the packet's */ 4016145522Sdarrenr /* source port against in_dport, otherwise we'd compare the */ 4017145522Sdarrenr /* packet's destination. */ 4018145522Sdarrenr /* ------------------------------------------------------------- */ 4019145522Sdarrenr if ((np != NULL) && (np->in_apr != NULL)) { 4020145522Sdarrenr i = appr_check(fin, nat); 4021145522Sdarrenr if (i == 0) 402260852Sdarrenr i = 1; 4023145522Sdarrenr } else 4024145522Sdarrenr i = 1; 4025145522Sdarrenr ATOMIC_INCL(nat_stats.ns_mapped[1]); 4026145522Sdarrenr fin->fin_flx |= FI_NATED; 4027145522Sdarrenr return i; 402853642Sguido} 402953642Sguido 403053642Sguido 4031145522Sdarrenr/* ------------------------------------------------------------------------ */ 4032145522Sdarrenr/* Function: fr_checknatin */ 4033145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 4034145522Sdarrenr/* 0 == no packet translation occurred, */ 4035145522Sdarrenr/* 1 == packet was successfully translated. */ 4036145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4037145522Sdarrenr/* passp(I) - pointer to filtering result flags */ 4038145522Sdarrenr/* */ 4039145522Sdarrenr/* Check to see if an incoming packet should be changed. ICMP packets are */ 4040145522Sdarrenr/* first checked to see if they match an existing entry (if an error), */ 4041145522Sdarrenr/* otherwise a search of the current NAT table is made. If neither results */ 4042145522Sdarrenr/* in a match then a search for a matching NAT rule is made. Create a new */ 4043145522Sdarrenr/* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 4044145522Sdarrenr/* packet header(s) as required. */ 4045145522Sdarrenr/* ------------------------------------------------------------------------ */ 4046145522Sdarrenrint fr_checknatin(fin, passp) 404753642Sguidofr_info_t *fin; 4048145522Sdarrenru_32_t *passp; 404953642Sguido{ 4050145522Sdarrenr u_int nflags, natadd; 4051145522Sdarrenr int rval, natfailed; 4052145522Sdarrenr struct ifnet *ifp; 4053145522Sdarrenr struct in_addr in; 4054145522Sdarrenr icmphdr_t *icmp; 4055145522Sdarrenr tcphdr_t *tcp; 4056145522Sdarrenr u_short dport; 4057145522Sdarrenr ipnat_t *np; 405853642Sguido nat_t *nat; 405953642Sguido u_32_t iph; 406053642Sguido 4061145522Sdarrenr if (nat_stats.ns_rules == 0 || fr_nat_lock != 0) 406253642Sguido return 0; 406353642Sguido 4064145522Sdarrenr tcp = NULL; 4065145522Sdarrenr icmp = NULL; 4066145522Sdarrenr dport = 0; 4067145522Sdarrenr natadd = 1; 4068145522Sdarrenr nflags = 0; 4069145522Sdarrenr natfailed = 0; 4070145522Sdarrenr ifp = fin->fin_ifp; 4071145522Sdarrenr 4072145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 4073145522Sdarrenr switch (fin->fin_p) 4074145522Sdarrenr { 4075145522Sdarrenr case IPPROTO_TCP : 407653642Sguido nflags = IPN_TCP; 4077145522Sdarrenr break; 4078145522Sdarrenr case IPPROTO_UDP : 407953642Sguido nflags = IPN_UDP; 4080145522Sdarrenr break; 4081145522Sdarrenr case IPPROTO_ICMP : 4082145522Sdarrenr icmp = fin->fin_dp; 4083145522Sdarrenr 4084145522Sdarrenr /* 4085145522Sdarrenr * This is an incoming packet, so the destination is 4086145522Sdarrenr * the icmp_id and the source port equals 0 4087145522Sdarrenr */ 4088145522Sdarrenr if (nat_icmpquerytype4(icmp->icmp_type)) { 4089145522Sdarrenr nflags = IPN_ICMPQUERY; 4090145522Sdarrenr dport = icmp->icmp_id; 4091145522Sdarrenr } break; 4092145522Sdarrenr default : 4093145522Sdarrenr break; 4094145522Sdarrenr } 4095145522Sdarrenr 409653642Sguido if ((nflags & IPN_TCPUDP)) { 4097145522Sdarrenr tcp = fin->fin_dp; 409853642Sguido dport = tcp->th_dport; 409953642Sguido } 410053642Sguido } 410153642Sguido 410292685Sdarrenr in = fin->fin_dst; 410353642Sguido 410453642Sguido READ_ENTER(&ipf_nat); 410553642Sguido 4106180778Sdarrenr if (((fin->fin_flx & FI_ICMPERR) != 0) && 4107145522Sdarrenr (nat = nat_icmperror(fin, &nflags, NAT_INBOUND))) 4108145522Sdarrenr /*EMPTY*/; 4109145522Sdarrenr else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin))) 411053642Sguido natadd = 0; 4111145522Sdarrenr else if ((nat = nat_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, 4112145522Sdarrenr fin->fin_src, in))) { 411353642Sguido nflags = nat->nat_flags; 411453642Sguido } else { 4115145522Sdarrenr u_32_t hv, msk, rmsk; 4116145522Sdarrenr 411753642Sguido RWLOCK_EXIT(&ipf_nat); 4118145522Sdarrenr rmsk = rdr_masks; 411992685Sdarrenr msk = 0xffffffff; 412053642Sguido WRITE_ENTER(&ipf_nat); 412153642Sguido /* 412253642Sguido * If there is no current entry in the nat table for this IP#, 412353642Sguido * create one for it (if there is a matching rule). 412453642Sguido */ 412553642Sguidomaskloop: 412653642Sguido iph = in.s_addr & htonl(msk); 412760852Sdarrenr hv = NAT_HASH_FN(iph, 0, ipf_rdrrules_sz); 412860852Sdarrenr for (np = rdr_rules[hv]; np; np = np->in_rnext) { 4129145522Sdarrenr if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) 413060852Sdarrenr continue; 4131145522Sdarrenr if (np->in_v != fin->fin_v) 4132138947Sdarrenr continue; 4133145522Sdarrenr if (np->in_p && (np->in_p != fin->fin_p)) 4134145522Sdarrenr continue; 4135145522Sdarrenr if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) 4136145522Sdarrenr continue; 413760852Sdarrenr if (np->in_flags & IPN_FILTER) { 4138145522Sdarrenr if (!nat_match(fin, np)) 413960852Sdarrenr continue; 4140145522Sdarrenr } else { 4141145522Sdarrenr if ((in.s_addr & np->in_outmsk) != np->in_outip) 4142145522Sdarrenr continue; 4143145522Sdarrenr if (np->in_pmin && 4144145522Sdarrenr ((ntohs(np->in_pmax) < ntohs(dport)) || 4145145522Sdarrenr (ntohs(dport) < ntohs(np->in_pmin)))) 4146145522Sdarrenr continue; 4147145522Sdarrenr } 4148145522Sdarrenr 4149145522Sdarrenr if (*np->in_plabel != '\0') { 4150145522Sdarrenr if (!appr_ok(fin, tcp, np)) { 4151145522Sdarrenr continue; 415253642Sguido } 4153145522Sdarrenr } 4154145522Sdarrenr 4155145522Sdarrenr nat = nat_new(fin, np, NULL, nflags, NAT_INBOUND); 4156145522Sdarrenr if (nat != NULL) { 4157145522Sdarrenr np->in_hits++; 4158145522Sdarrenr break; 4159145522Sdarrenr } else 4160145522Sdarrenr natfailed = -1; 416160852Sdarrenr } 416260852Sdarrenr 4163145522Sdarrenr if ((np == NULL) && (rmsk != 0)) { 4164145522Sdarrenr while (rmsk) { 416553642Sguido msk <<= 1; 4166145522Sdarrenr if (rmsk & 0x80000000) 4167145522Sdarrenr break; 4168145522Sdarrenr rmsk <<= 1; 4169145522Sdarrenr } 4170145522Sdarrenr if (rmsk != 0) { 4171145522Sdarrenr rmsk <<= 1; 417253642Sguido goto maskloop; 4173145522Sdarrenr } 417453642Sguido } 417553642Sguido MUTEX_DOWNGRADE(&ipf_nat); 417653642Sguido } 4177145522Sdarrenr if (nat != NULL) { 4178145522Sdarrenr rval = fr_natin(fin, nat, natadd, nflags); 4179145522Sdarrenr if (rval == 1) { 4180145522Sdarrenr MUTEX_ENTER(&nat->nat_lock); 4181145522Sdarrenr nat->nat_ref++; 4182145522Sdarrenr MUTEX_EXIT(&nat->nat_lock); 4183170268Sdarrenr nat->nat_touched = fr_ticks; 4184145522Sdarrenr fin->fin_nat = nat; 4185145522Sdarrenr } 4186145522Sdarrenr } else 4187145522Sdarrenr rval = natfailed; 4188145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 418972006Sdarrenr 4190145522Sdarrenr if (rval == -1) { 4191145522Sdarrenr if (passp != NULL) 4192145522Sdarrenr *passp = FR_BLOCK; 4193145522Sdarrenr fin->fin_flx |= FI_BADNAT; 4194145522Sdarrenr } 4195145522Sdarrenr return rval; 4196145522Sdarrenr} 4197145522Sdarrenr 4198145522Sdarrenr 4199145522Sdarrenr/* ------------------------------------------------------------------------ */ 4200145522Sdarrenr/* Function: fr_natin */ 4201145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 4202145522Sdarrenr/* 1 == packet was successfully translated. */ 4203145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4204145522Sdarrenr/* nat(I) - pointer to NAT structure */ 4205145522Sdarrenr/* natadd(I) - flag indicating if it is safe to add frag cache */ 4206145522Sdarrenr/* nflags(I) - NAT flags set for this packet */ 4207145522Sdarrenr/* Locks Held: ipf_nat (READ) */ 4208145522Sdarrenr/* */ 4209145522Sdarrenr/* Translate a packet coming "in" on an interface. */ 4210145522Sdarrenr/* ------------------------------------------------------------------------ */ 4211145522Sdarrenrint fr_natin(fin, nat, natadd, nflags) 4212145522Sdarrenrfr_info_t *fin; 4213145522Sdarrenrnat_t *nat; 4214145522Sdarrenrint natadd; 4215145522Sdarrenru_32_t nflags; 4216145522Sdarrenr{ 4217145522Sdarrenr icmphdr_t *icmp; 4218145522Sdarrenr u_short *csump; 4219145522Sdarrenr tcphdr_t *tcp; 4220145522Sdarrenr ipnat_t *np; 4221145522Sdarrenr int i; 4222145522Sdarrenr 4223145522Sdarrenr tcp = NULL; 4224145522Sdarrenr csump = NULL; 4225145522Sdarrenr np = nat->nat_ptr; 4226145522Sdarrenr fin->fin_fr = nat->nat_fr; 4227145522Sdarrenr 4228145522Sdarrenr if (np != NULL) { 4229145522Sdarrenr if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) 4230145522Sdarrenr (void) fr_nat_newfrag(fin, 0, nat); 4231145522Sdarrenr 4232145522Sdarrenr /* ------------------------------------------------------------- */ 4233145522Sdarrenr /* A few quick notes: */ 4234145522Sdarrenr /* Following are test conditions prior to calling the */ 4235145522Sdarrenr /* appr_check routine. */ 4236145522Sdarrenr /* */ 4237145522Sdarrenr /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 4238145522Sdarrenr /* with a map rule, we attempt to match the packet's */ 4239145522Sdarrenr /* source port against in_dport, otherwise we'd compare the */ 4240145522Sdarrenr /* packet's destination. */ 4241145522Sdarrenr /* ------------------------------------------------------------- */ 4242145522Sdarrenr if (np->in_apr != NULL) { 4243145522Sdarrenr i = appr_check(fin, nat); 424460852Sdarrenr if (i == -1) { 4245145522Sdarrenr return -1; 424660852Sdarrenr } 424760852Sdarrenr } 4248145522Sdarrenr } 424953642Sguido 4250145522Sdarrenr#ifdef IPFILTER_SYNC 4251145522Sdarrenr ipfsync_update(SMC_NAT, fin, nat->nat_sync); 4252145522Sdarrenr#endif 4253145522Sdarrenr 4254145522Sdarrenr MUTEX_ENTER(&nat->nat_lock); 4255145522Sdarrenr nat->nat_bytes[0] += fin->fin_plen; 4256145522Sdarrenr nat->nat_pkts[0]++; 4257145522Sdarrenr MUTEX_EXIT(&nat->nat_lock); 4258145522Sdarrenr 4259145522Sdarrenr fin->fin_ip->ip_dst = nat->nat_inip; 4260145522Sdarrenr fin->fin_fi.fi_daddr = nat->nat_inip.s_addr; 4261145522Sdarrenr if (nflags & IPN_TCPUDP) 4262145522Sdarrenr tcp = fin->fin_dp; 4263145522Sdarrenr 4264145522Sdarrenr /* 4265145522Sdarrenr * Fix up checksums, not by recalculating them, but 4266145522Sdarrenr * simply computing adjustments. 4267145522Sdarrenr * Why only do this for some platforms on inbound packets ? 4268145522Sdarrenr * Because for those that it is done, IP processing is yet to happen 4269145522Sdarrenr * and so the IPv4 header checksum has not yet been evaluated. 4270145522Sdarrenr * Perhaps it should always be done for the benefit of things like 4271145522Sdarrenr * fast forwarding (so that it doesn't need to be recomputed) but with 4272145522Sdarrenr * header checksum offloading, perhaps it is a moot point. 4273145522Sdarrenr */ 4274145522Sdarrenr#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 4275145522Sdarrenr defined(__osf__) || defined(linux) 4276145522Sdarrenr if (nat->nat_dir == NAT_OUTBOUND) 4277145522Sdarrenr fix_incksum(fin, &fin->fin_ip->ip_sum, nat->nat_ipsumd); 4278145522Sdarrenr else 4279145522Sdarrenr fix_outcksum(fin, &fin->fin_ip->ip_sum, nat->nat_ipsumd); 4280145522Sdarrenr#endif 4281145522Sdarrenr 4282145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 4283145522Sdarrenr if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) { 4284145522Sdarrenr tcp->th_dport = nat->nat_inport; 4285145522Sdarrenr fin->fin_data[1] = ntohs(nat->nat_inport); 428692685Sdarrenr } 428753642Sguido 4288145522Sdarrenr 4289145522Sdarrenr if ((nat->nat_inport != 0) && (nflags & IPN_ICMPQUERY)) { 4290145522Sdarrenr icmp = fin->fin_dp; 4291145522Sdarrenr 4292145522Sdarrenr icmp->icmp_id = nat->nat_inport; 4293145522Sdarrenr } 4294145522Sdarrenr 4295145522Sdarrenr csump = nat_proto(fin, nat, nflags); 4296145522Sdarrenr } 4297145522Sdarrenr 4298145522Sdarrenr nat_update(fin, nat, np); 4299145522Sdarrenr 4300145522Sdarrenr /* 4301145522Sdarrenr * The above comments do not hold for layer 4 (or higher) checksums... 4302145522Sdarrenr */ 4303145522Sdarrenr if (csump != NULL) { 430453642Sguido if (nat->nat_dir == NAT_OUTBOUND) 4305145522Sdarrenr fix_incksum(fin, csump, nat->nat_sumd[0]); 430653642Sguido else 4307145522Sdarrenr fix_outcksum(fin, csump, nat->nat_sumd[0]); 4308145522Sdarrenr } 4309145522Sdarrenr ATOMIC_INCL(nat_stats.ns_mapped[0]); 4310145522Sdarrenr fin->fin_flx |= FI_NATED; 4311145522Sdarrenr if (np != NULL && np->in_tag.ipt_num[0] != 0) 4312145522Sdarrenr fin->fin_nattag = &np->in_tag; 4313145522Sdarrenr return 1; 4314145522Sdarrenr} 4315130886Sdarrenr 4316130886Sdarrenr 4317145522Sdarrenr/* ------------------------------------------------------------------------ */ 4318145522Sdarrenr/* Function: nat_proto */ 4319145522Sdarrenr/* Returns: u_short* - pointer to transport header checksum to update, */ 4320145522Sdarrenr/* NULL if the transport protocol is not recognised */ 4321145522Sdarrenr/* as needing a checksum update. */ 4322145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4323145522Sdarrenr/* nat(I) - pointer to NAT structure */ 4324145522Sdarrenr/* nflags(I) - NAT flags set for this packet */ 4325145522Sdarrenr/* */ 4326145522Sdarrenr/* Return the pointer to the checksum field for each protocol so understood.*/ 4327145522Sdarrenr/* If support for making other changes to a protocol header is required, */ 4328145522Sdarrenr/* that is not strictly 'address' translation, such as clamping the MSS in */ 4329145522Sdarrenr/* TCP down to a specific value, then do it from here. */ 4330145522Sdarrenr/* ------------------------------------------------------------------------ */ 4331145522Sdarrenru_short *nat_proto(fin, nat, nflags) 4332145522Sdarrenrfr_info_t *fin; 4333145522Sdarrenrnat_t *nat; 4334145522Sdarrenru_int nflags; 4335145522Sdarrenr{ 4336145522Sdarrenr icmphdr_t *icmp; 4337145522Sdarrenr u_short *csump; 4338145522Sdarrenr tcphdr_t *tcp; 4339145522Sdarrenr udphdr_t *udp; 434053642Sguido 4341145522Sdarrenr csump = NULL; 4342145522Sdarrenr if (fin->fin_out == 0) { 4343145522Sdarrenr fin->fin_rev = (nat->nat_dir == NAT_OUTBOUND); 4344145522Sdarrenr } else { 4345145522Sdarrenr fin->fin_rev = (nat->nat_dir == NAT_INBOUND); 4346145522Sdarrenr } 434753642Sguido 4348145522Sdarrenr switch (fin->fin_p) 4349145522Sdarrenr { 4350145522Sdarrenr case IPPROTO_TCP : 4351145522Sdarrenr tcp = fin->fin_dp; 4352110916Sdarrenr 4353145522Sdarrenr csump = &tcp->th_sum; 435453642Sguido 4355145522Sdarrenr /* 4356145522Sdarrenr * Do a MSS CLAMPING on a SYN packet, 4357145522Sdarrenr * only deal IPv4 for now. 4358145522Sdarrenr */ 4359145522Sdarrenr if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0) 4360145522Sdarrenr nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump); 436160852Sdarrenr 4362145522Sdarrenr break; 4363145522Sdarrenr 4364145522Sdarrenr case IPPROTO_UDP : 4365145522Sdarrenr udp = fin->fin_dp; 4366145522Sdarrenr 4367145522Sdarrenr if (udp->uh_sum) 4368145522Sdarrenr csump = &udp->uh_sum; 4369145522Sdarrenr break; 4370145522Sdarrenr 4371145522Sdarrenr case IPPROTO_ICMP : 4372145522Sdarrenr icmp = fin->fin_dp; 4373145522Sdarrenr 4374145522Sdarrenr if ((nflags & IPN_ICMPQUERY) != 0) { 4375145522Sdarrenr if (icmp->icmp_cksum != 0) 4376145522Sdarrenr csump = &icmp->icmp_cksum; 437753642Sguido } 4378145522Sdarrenr break; 437953642Sguido } 4380145522Sdarrenr return csump; 438153642Sguido} 438253642Sguido 438353642Sguido 4384145522Sdarrenr/* ------------------------------------------------------------------------ */ 4385145522Sdarrenr/* Function: fr_natunload */ 4386145522Sdarrenr/* Returns: Nil */ 4387145522Sdarrenr/* Parameters: Nil */ 4388145522Sdarrenr/* */ 4389145522Sdarrenr/* Free all memory used by NAT structures allocated at runtime. */ 4390145522Sdarrenr/* ------------------------------------------------------------------------ */ 4391145522Sdarrenrvoid fr_natunload() 439253642Sguido{ 4393145522Sdarrenr ipftq_t *ifq, *ifqnext; 4394145522Sdarrenr 439553642Sguido (void) nat_clearlist(); 439653642Sguido (void) nat_flushtable(); 439753642Sguido 4398145522Sdarrenr /* 4399145522Sdarrenr * Proxy timeout queues are not cleaned here because although they 4400145522Sdarrenr * exist on the NAT list, appr_unload is called after fr_natunload 4401145522Sdarrenr * and the proxies actually are responsible for them being created. 4402145522Sdarrenr * Should the proxy timeouts have their own list? There's no real 4403145522Sdarrenr * justification as this is the only complication. 4404145522Sdarrenr */ 4405145522Sdarrenr for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) { 4406145522Sdarrenr ifqnext = ifq->ifq_next; 4407145522Sdarrenr if (((ifq->ifq_flags & IFQF_PROXY) == 0) && 4408145522Sdarrenr (fr_deletetimeoutqueue(ifq) == 0)) 4409145522Sdarrenr fr_freetimeoutqueue(ifq); 4410145522Sdarrenr } 4411145522Sdarrenr 441253642Sguido if (nat_table[0] != NULL) { 441353642Sguido KFREES(nat_table[0], sizeof(nat_t *) * ipf_nattable_sz); 441453642Sguido nat_table[0] = NULL; 441553642Sguido } 441653642Sguido if (nat_table[1] != NULL) { 441753642Sguido KFREES(nat_table[1], sizeof(nat_t *) * ipf_nattable_sz); 441853642Sguido nat_table[1] = NULL; 441953642Sguido } 442053642Sguido if (nat_rules != NULL) { 442153642Sguido KFREES(nat_rules, sizeof(ipnat_t *) * ipf_natrules_sz); 442253642Sguido nat_rules = NULL; 442353642Sguido } 442453642Sguido if (rdr_rules != NULL) { 442553642Sguido KFREES(rdr_rules, sizeof(ipnat_t *) * ipf_rdrrules_sz); 442653642Sguido rdr_rules = NULL; 442753642Sguido } 4428170268Sdarrenr if (ipf_hm_maptable != NULL) { 4429170268Sdarrenr KFREES(ipf_hm_maptable, sizeof(hostmap_t *) * ipf_hostmap_sz); 4430170268Sdarrenr ipf_hm_maptable = NULL; 443160852Sdarrenr } 4432145522Sdarrenr if (nat_stats.ns_bucketlen[0] != NULL) { 4433145522Sdarrenr KFREES(nat_stats.ns_bucketlen[0], 4434145522Sdarrenr sizeof(u_long *) * ipf_nattable_sz); 4435145522Sdarrenr nat_stats.ns_bucketlen[0] = NULL; 4436145522Sdarrenr } 4437145522Sdarrenr if (nat_stats.ns_bucketlen[1] != NULL) { 4438145522Sdarrenr KFREES(nat_stats.ns_bucketlen[1], 4439145522Sdarrenr sizeof(u_long *) * ipf_nattable_sz); 4440145522Sdarrenr nat_stats.ns_bucketlen[1] = NULL; 4441145522Sdarrenr } 4442145522Sdarrenr 4443145522Sdarrenr if (fr_nat_maxbucket_reset == 1) 4444145522Sdarrenr fr_nat_maxbucket = 0; 4445145522Sdarrenr 4446145522Sdarrenr if (fr_nat_init == 1) { 4447145522Sdarrenr fr_nat_init = 0; 4448145522Sdarrenr fr_sttab_destroy(nat_tqb); 4449145522Sdarrenr 4450145522Sdarrenr RW_DESTROY(&ipf_natfrag); 4451145522Sdarrenr RW_DESTROY(&ipf_nat); 4452145522Sdarrenr 4453145522Sdarrenr MUTEX_DESTROY(&ipf_nat_new); 4454145522Sdarrenr MUTEX_DESTROY(&ipf_natio); 4455145522Sdarrenr 4456145522Sdarrenr MUTEX_DESTROY(&nat_udptq.ifq_lock); 4457145522Sdarrenr MUTEX_DESTROY(&nat_icmptq.ifq_lock); 4458145522Sdarrenr MUTEX_DESTROY(&nat_iptq.ifq_lock); 4459145522Sdarrenr } 446053642Sguido} 446153642Sguido 446253642Sguido 4463145522Sdarrenr/* ------------------------------------------------------------------------ */ 4464145522Sdarrenr/* Function: fr_natexpire */ 4465145522Sdarrenr/* Returns: Nil */ 4466145522Sdarrenr/* Parameters: Nil */ 4467145522Sdarrenr/* */ 4468145522Sdarrenr/* Check all of the timeout queues for entries at the top which need to be */ 4469145522Sdarrenr/* expired. */ 4470145522Sdarrenr/* ------------------------------------------------------------------------ */ 4471145522Sdarrenrvoid fr_natexpire() 447253642Sguido{ 4473145522Sdarrenr ipftq_t *ifq, *ifqnext; 4474145522Sdarrenr ipftqent_t *tqe, *tqn; 4475161356Sguido int i; 4476153876Sguido SPL_INT(s); 447753642Sguido 447853642Sguido SPL_NET(s); 447953642Sguido WRITE_ENTER(&ipf_nat); 4480145522Sdarrenr for (ifq = nat_tqb, i = 0; ifq != NULL; ifq = ifq->ifq_next) { 4481145522Sdarrenr for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { 4482145522Sdarrenr if (tqe->tqe_die > fr_ticks) 4483145522Sdarrenr break; 4484145522Sdarrenr tqn = tqe->tqe_next; 4485145522Sdarrenr nat_delete(tqe->tqe_parent, NL_EXPIRE); 448653642Sguido } 448753642Sguido } 4488145522Sdarrenr 4489145522Sdarrenr for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) { 4490145522Sdarrenr ifqnext = ifq->ifq_next; 4491145522Sdarrenr 4492145522Sdarrenr for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { 4493145522Sdarrenr if (tqe->tqe_die > fr_ticks) 4494145522Sdarrenr break; 4495145522Sdarrenr tqn = tqe->tqe_next; 4496145522Sdarrenr nat_delete(tqe->tqe_parent, NL_EXPIRE); 4497145522Sdarrenr } 4498145522Sdarrenr } 4499145522Sdarrenr 4500145522Sdarrenr for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) { 4501145522Sdarrenr ifqnext = ifq->ifq_next; 4502145522Sdarrenr 4503145522Sdarrenr if (((ifq->ifq_flags & IFQF_DELETE) != 0) && 4504145522Sdarrenr (ifq->ifq_ref == 0)) { 4505145522Sdarrenr fr_freetimeoutqueue(ifq); 4506145522Sdarrenr } 4507145522Sdarrenr } 4508145522Sdarrenr 4509170268Sdarrenr if (fr_nat_doflush != 0) { 4510170268Sdarrenr nat_extraflush(2); 4511170268Sdarrenr fr_nat_doflush = 0; 4512170268Sdarrenr } 4513170268Sdarrenr 451453642Sguido RWLOCK_EXIT(&ipf_nat); 451553642Sguido SPL_X(s); 451653642Sguido} 451753642Sguido 451853642Sguido 4519145522Sdarrenr/* ------------------------------------------------------------------------ */ 4520145522Sdarrenr/* Function: fr_natsync */ 4521145522Sdarrenr/* Returns: Nil */ 4522145522Sdarrenr/* Parameters: ifp(I) - pointer to network interface */ 4523145522Sdarrenr/* */ 4524145522Sdarrenr/* Walk through all of the currently active NAT sessions, looking for those */ 4525145522Sdarrenr/* which need to have their translated address updated. */ 4526145522Sdarrenr/* ------------------------------------------------------------------------ */ 4527145522Sdarrenrvoid fr_natsync(ifp) 452853642Sguidovoid *ifp; 452953642Sguido{ 4530145522Sdarrenr u_32_t sum1, sum2, sumd; 453153642Sguido struct in_addr in; 4532145522Sdarrenr ipnat_t *n; 4533145522Sdarrenr nat_t *nat; 453453642Sguido void *ifp2; 4535153876Sguido SPL_INT(s); 453653642Sguido 4537145522Sdarrenr if (fr_running <= 0) 4538145522Sdarrenr return; 4539145522Sdarrenr 454053642Sguido /* 454153642Sguido * Change IP addresses for NAT sessions for any protocol except TCP 4542145522Sdarrenr * since it will break the TCP connection anyway. The only rules 4543145522Sdarrenr * which will get changed are those which are "map ... -> 0/32", 4544145522Sdarrenr * where the rule specifies the address is taken from the interface. 454553642Sguido */ 454653642Sguido SPL_NET(s); 454753642Sguido WRITE_ENTER(&ipf_nat); 4548145522Sdarrenr 4549145522Sdarrenr if (fr_running <= 0) { 4550145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 4551145522Sdarrenr return; 4552145522Sdarrenr } 4553145522Sdarrenr 4554145522Sdarrenr for (nat = nat_instances; nat; nat = nat->nat_next) { 4555145522Sdarrenr if ((nat->nat_flags & IPN_TCP) != 0) 4556145522Sdarrenr continue; 4557145522Sdarrenr n = nat->nat_ptr; 4558145522Sdarrenr if ((n == NULL) || 4559145522Sdarrenr (n->in_outip != 0) || (n->in_outmsk != 0xffffffff)) 4560145522Sdarrenr continue; 4561145522Sdarrenr if (((ifp == NULL) || (ifp == nat->nat_ifps[0]) || 4562145522Sdarrenr (ifp == nat->nat_ifps[1]))) { 4563145522Sdarrenr nat->nat_ifps[0] = GETIFP(nat->nat_ifnames[0], 4); 4564145522Sdarrenr if (nat->nat_ifnames[1][0] != '\0') { 4565145522Sdarrenr nat->nat_ifps[1] = GETIFP(nat->nat_ifnames[1], 4566145522Sdarrenr 4); 4567145522Sdarrenr } else 4568145522Sdarrenr nat->nat_ifps[1] = nat->nat_ifps[0]; 4569145522Sdarrenr ifp2 = nat->nat_ifps[0]; 4570145522Sdarrenr if (ifp2 == NULL) 4571145522Sdarrenr continue; 4572145522Sdarrenr 457353642Sguido /* 457453642Sguido * Change the map-to address to be the same as the 457553642Sguido * new one. 457653642Sguido */ 457753642Sguido sum1 = nat->nat_outip.s_addr; 4578145522Sdarrenr if (fr_ifpaddr(4, FRI_NORMAL, ifp2, &in, NULL) != -1) 457955929Sguido nat->nat_outip = in; 458053642Sguido sum2 = nat->nat_outip.s_addr; 458153642Sguido 458253642Sguido if (sum1 == sum2) 458353642Sguido continue; 458453642Sguido /* 458553642Sguido * Readjust the checksum adjustment to take into 458653642Sguido * account the new IP#. 458753642Sguido */ 458853642Sguido CALC_SUMD(sum1, sum2, sumd); 458955929Sguido /* XXX - dont change for TCP when solaris does 459055929Sguido * hardware checksumming. 459155929Sguido */ 459255929Sguido sumd += nat->nat_sumd[0]; 459355929Sguido nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 459455929Sguido nat->nat_sumd[1] = nat->nat_sumd[0]; 459553642Sguido } 4596145522Sdarrenr } 459753642Sguido 4598145522Sdarrenr for (n = nat_list; (n != NULL); n = n->in_next) { 4599145522Sdarrenr if ((ifp == NULL) || (n->in_ifps[0] == ifp)) 4600145522Sdarrenr n->in_ifps[0] = fr_resolvenic(n->in_ifnames[0], 4); 4601145522Sdarrenr if ((ifp == NULL) || (n->in_ifps[1] == ifp)) 4602145522Sdarrenr n->in_ifps[1] = fr_resolvenic(n->in_ifnames[1], 4); 4603145522Sdarrenr } 460460852Sdarrenr RWLOCK_EXIT(&ipf_nat); 460553642Sguido SPL_X(s); 460653642Sguido} 460753642Sguido 460853642Sguido 4609145522Sdarrenr/* ------------------------------------------------------------------------ */ 4610145522Sdarrenr/* Function: nat_icmpquerytype4 */ 4611145522Sdarrenr/* Returns: int - 1 == success, 0 == failure */ 4612145522Sdarrenr/* Parameters: icmptype(I) - ICMP type number */ 4613145522Sdarrenr/* */ 4614145522Sdarrenr/* Tests to see if the ICMP type number passed is a query/response type or */ 4615145522Sdarrenr/* not. */ 4616145522Sdarrenr/* ------------------------------------------------------------------------ */ 4617153876Sguidostatic int nat_icmpquerytype4(icmptype) 4618145522Sdarrenrint icmptype; 4619145522Sdarrenr{ 4620145522Sdarrenr 4621145522Sdarrenr /* 4622145522Sdarrenr * For the ICMP query NAT code, it is essential that both the query 4623145522Sdarrenr * and the reply match on the NAT rule. Because the NAT structure 4624145522Sdarrenr * does not keep track of the icmptype, and a single NAT structure 4625145522Sdarrenr * is used for all icmp types with the same src, dest and id, we 4626145522Sdarrenr * simply define the replies as queries as well. The funny thing is, 4627145522Sdarrenr * altough it seems silly to call a reply a query, this is exactly 4628145522Sdarrenr * as it is defined in the IPv4 specification 4629145522Sdarrenr */ 4630145522Sdarrenr 4631145522Sdarrenr switch (icmptype) 4632145522Sdarrenr { 4633145522Sdarrenr 4634145522Sdarrenr case ICMP_ECHOREPLY: 4635145522Sdarrenr case ICMP_ECHO: 4636145522Sdarrenr /* route aedvertisement/solliciation is currently unsupported: */ 4637145522Sdarrenr /* it would require rewriting the ICMP data section */ 4638145522Sdarrenr case ICMP_TSTAMP: 4639145522Sdarrenr case ICMP_TSTAMPREPLY: 4640145522Sdarrenr case ICMP_IREQ: 4641145522Sdarrenr case ICMP_IREQREPLY: 4642145522Sdarrenr case ICMP_MASKREQ: 4643145522Sdarrenr case ICMP_MASKREPLY: 4644145522Sdarrenr return 1; 4645145522Sdarrenr default: 4646145522Sdarrenr return 0; 4647145522Sdarrenr } 4648145522Sdarrenr} 4649145522Sdarrenr 4650145522Sdarrenr 4651145522Sdarrenr/* ------------------------------------------------------------------------ */ 4652145522Sdarrenr/* Function: nat_log */ 4653145522Sdarrenr/* Returns: Nil */ 4654145522Sdarrenr/* Parameters: nat(I) - pointer to NAT structure */ 4655145522Sdarrenr/* type(I) - type of log entry to create */ 4656145522Sdarrenr/* */ 4657145522Sdarrenr/* Creates a NAT log entry. */ 4658145522Sdarrenr/* ------------------------------------------------------------------------ */ 465953642Sguidovoid nat_log(nat, type) 466053642Sguidostruct nat *nat; 466153642Sguidou_int type; 466253642Sguido{ 4663145522Sdarrenr#ifdef IPFILTER_LOG 4664139005Smlaier# ifndef LARGE_NAT 466553642Sguido struct ipnat *np; 4666138979Sdarrenr int rulen; 4667138979Sdarrenr# endif 466853642Sguido struct natlog natl; 466953642Sguido void *items[1]; 467053642Sguido size_t sizes[1]; 4671138979Sdarrenr int types[1]; 467253642Sguido 467353642Sguido natl.nl_inip = nat->nat_inip; 467453642Sguido natl.nl_outip = nat->nat_outip; 467553642Sguido natl.nl_origip = nat->nat_oip; 4676145522Sdarrenr natl.nl_bytes[0] = nat->nat_bytes[0]; 4677145522Sdarrenr natl.nl_bytes[1] = nat->nat_bytes[1]; 4678145522Sdarrenr natl.nl_pkts[0] = nat->nat_pkts[0]; 4679145522Sdarrenr natl.nl_pkts[1] = nat->nat_pkts[1]; 468053642Sguido natl.nl_origport = nat->nat_oport; 468153642Sguido natl.nl_inport = nat->nat_inport; 468253642Sguido natl.nl_outport = nat->nat_outport; 468357096Sguido natl.nl_p = nat->nat_p; 468453642Sguido natl.nl_type = type; 468553642Sguido natl.nl_rule = -1; 4686145522Sdarrenr# ifndef LARGE_NAT 468753642Sguido if (nat->nat_ptr != NULL) { 468853642Sguido for (rulen = 0, np = nat_list; np; np = np->in_next, rulen++) 468953642Sguido if (np == nat->nat_ptr) { 469053642Sguido natl.nl_rule = rulen; 469153642Sguido break; 469253642Sguido } 469353642Sguido } 4694145522Sdarrenr# endif 469553642Sguido items[0] = &natl; 469653642Sguido sizes[0] = sizeof(natl); 469753642Sguido types[0] = 0; 469853642Sguido 469953642Sguido (void) ipllog(IPL_LOGNAT, NULL, items, sizes, types, 1); 4700145522Sdarrenr#endif 470153642Sguido} 470292685Sdarrenr 470392685Sdarrenr 470492685Sdarrenr#if defined(__OpenBSD__) 4705145522Sdarrenr/* ------------------------------------------------------------------------ */ 4706145522Sdarrenr/* Function: nat_ifdetach */ 4707145522Sdarrenr/* Returns: Nil */ 4708145522Sdarrenr/* Parameters: ifp(I) - pointer to network interface */ 4709145522Sdarrenr/* */ 4710145522Sdarrenr/* Compatibility interface for OpenBSD to trigger the correct updating of */ 4711145522Sdarrenr/* interface references within IPFilter. */ 4712145522Sdarrenr/* ------------------------------------------------------------------------ */ 471392685Sdarrenrvoid nat_ifdetach(ifp) 471492685Sdarrenrvoid *ifp; 471592685Sdarrenr{ 4716145522Sdarrenr frsync(ifp); 471792685Sdarrenr return; 471892685Sdarrenr} 471992685Sdarrenr#endif 4720110916Sdarrenr 4721110916Sdarrenr 4722145522Sdarrenr/* ------------------------------------------------------------------------ */ 4723170268Sdarrenr/* Function: fr_ipnatderef */ 4724170268Sdarrenr/* Returns: Nil */ 4725170268Sdarrenr/* Parameters: isp(I) - pointer to pointer to NAT rule */ 4726170268Sdarrenr/* Write Locks: ipf_nat */ 4727170268Sdarrenr/* */ 4728170268Sdarrenr/* ------------------------------------------------------------------------ */ 4729170268Sdarrenrvoid fr_ipnatderef(inp) 4730170268Sdarrenripnat_t **inp; 4731170268Sdarrenr{ 4732170268Sdarrenr ipnat_t *in; 4733170268Sdarrenr 4734170268Sdarrenr in = *inp; 4735170268Sdarrenr *inp = NULL; 4736170268Sdarrenr in->in_space++; 4737170268Sdarrenr in->in_use--; 4738170268Sdarrenr if (in->in_use == 0 && (in->in_flags & IPN_DELETE)) { 4739170268Sdarrenr if (in->in_apr) 4740170268Sdarrenr appr_free(in->in_apr); 4741172776Sdarrenr MUTEX_DESTROY(&in->in_lock); 4742170268Sdarrenr KFREE(in); 4743170268Sdarrenr nat_stats.ns_rules--; 4744172776Sdarrenr#if SOLARIS && !defined(_INET_IP_STACK_H) 4745170268Sdarrenr if (nat_stats.ns_rules == 0) 4746170268Sdarrenr pfil_delayed_copy = 1; 4747170268Sdarrenr#endif 4748170268Sdarrenr } 4749170268Sdarrenr} 4750170268Sdarrenr 4751170268Sdarrenr 4752170268Sdarrenr/* ------------------------------------------------------------------------ */ 4753145522Sdarrenr/* Function: fr_natderef */ 4754145522Sdarrenr/* Returns: Nil */ 4755145522Sdarrenr/* Parameters: isp(I) - pointer to pointer to NAT table entry */ 4756145522Sdarrenr/* */ 4757145522Sdarrenr/* Decrement the reference counter for this NAT table entry and free it if */ 4758145522Sdarrenr/* there are no more things using it. */ 4759172776Sdarrenr/* */ 4760172776Sdarrenr/* IF nat_ref == 1 when this function is called, then we have an orphan nat */ 4761172776Sdarrenr/* structure *because* it only gets called on paths _after_ nat_ref has been*/ 4762172776Sdarrenr/* incremented. If nat_ref == 1 then we shouldn't decrement it here */ 4763172776Sdarrenr/* because nat_delete() will do that and send nat_ref to -1. */ 4764172776Sdarrenr/* */ 4765172776Sdarrenr/* Holding the lock on nat_lock is required to serialise nat_delete() being */ 4766172776Sdarrenr/* called from a NAT flush ioctl with a deref happening because of a packet.*/ 4767145522Sdarrenr/* ------------------------------------------------------------------------ */ 4768145522Sdarrenrvoid fr_natderef(natp) 4769145522Sdarrenrnat_t **natp; 4770145522Sdarrenr{ 4771145522Sdarrenr nat_t *nat; 4772145522Sdarrenr 4773145522Sdarrenr nat = *natp; 4774145522Sdarrenr *natp = NULL; 4775172776Sdarrenr 4776172776Sdarrenr MUTEX_ENTER(&nat->nat_lock); 4777172776Sdarrenr if (nat->nat_ref > 1) { 4778172776Sdarrenr nat->nat_ref--; 4779172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 4780172776Sdarrenr return; 4781172776Sdarrenr } 4782172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 4783172776Sdarrenr 4784145522Sdarrenr WRITE_ENTER(&ipf_nat); 4785172776Sdarrenr nat_delete(nat, NL_EXPIRE); 4786145522Sdarrenr RWLOCK_EXIT(&ipf_nat); 4787145522Sdarrenr} 4788145522Sdarrenr 4789145522Sdarrenr 4790145522Sdarrenr/* ------------------------------------------------------------------------ */ 4791145522Sdarrenr/* Function: fr_natclone */ 4792145522Sdarrenr/* Returns: ipstate_t* - NULL == cloning failed, */ 4793145522Sdarrenr/* else pointer to new state structure */ 4794145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4795145522Sdarrenr/* is(I) - pointer to master state structure */ 4796145522Sdarrenr/* Write Lock: ipf_nat */ 4797145522Sdarrenr/* */ 4798145522Sdarrenr/* Create a "duplcate" state table entry from the master. */ 4799145522Sdarrenr/* ------------------------------------------------------------------------ */ 4800145522Sdarrenrstatic nat_t *fr_natclone(fin, nat) 4801145522Sdarrenrfr_info_t *fin; 4802145522Sdarrenrnat_t *nat; 4803145522Sdarrenr{ 4804145522Sdarrenr frentry_t *fr; 4805145522Sdarrenr nat_t *clone; 4806145522Sdarrenr ipnat_t *np; 4807145522Sdarrenr 4808145522Sdarrenr KMALLOC(clone, nat_t *); 4809145522Sdarrenr if (clone == NULL) 4810145522Sdarrenr return NULL; 4811145522Sdarrenr bcopy((char *)nat, (char *)clone, sizeof(*clone)); 4812145522Sdarrenr 4813145522Sdarrenr MUTEX_NUKE(&clone->nat_lock); 4814145522Sdarrenr 4815153876Sguido clone->nat_aps = NULL; 4816153876Sguido /* 4817153876Sguido * Initialize all these so that nat_delete() doesn't cause a crash. 4818153876Sguido */ 4819153876Sguido clone->nat_tqe.tqe_pnext = NULL; 4820153876Sguido clone->nat_tqe.tqe_next = NULL; 4821153876Sguido clone->nat_tqe.tqe_ifq = NULL; 4822153876Sguido clone->nat_tqe.tqe_parent = clone; 4823153876Sguido 4824145522Sdarrenr clone->nat_flags &= ~SI_CLONE; 4825145522Sdarrenr clone->nat_flags |= SI_CLONED; 4826145522Sdarrenr 4827153876Sguido if (clone->nat_hm) 4828153876Sguido clone->nat_hm->hm_ref++; 4829145522Sdarrenr 4830145522Sdarrenr if (nat_insert(clone, fin->fin_rev) == -1) { 4831145522Sdarrenr KFREE(clone); 4832145522Sdarrenr return NULL; 4833145522Sdarrenr } 4834145522Sdarrenr np = clone->nat_ptr; 4835145522Sdarrenr if (np != NULL) { 4836145522Sdarrenr if (nat_logging) 4837145522Sdarrenr nat_log(clone, (u_int)np->in_redir); 4838145522Sdarrenr np->in_use++; 4839145522Sdarrenr } 4840145522Sdarrenr fr = clone->nat_fr; 4841145522Sdarrenr if (fr != NULL) { 4842145522Sdarrenr MUTEX_ENTER(&fr->fr_lock); 4843145522Sdarrenr fr->fr_ref++; 4844145522Sdarrenr MUTEX_EXIT(&fr->fr_lock); 4845145522Sdarrenr } 4846145522Sdarrenr 4847145522Sdarrenr /* 4848145522Sdarrenr * Because the clone is created outside the normal loop of things and 4849145522Sdarrenr * TCP has special needs in terms of state, initialise the timeout 4850145522Sdarrenr * state of the new NAT from here. 4851145522Sdarrenr */ 4852145522Sdarrenr if (clone->nat_p == IPPROTO_TCP) { 4853153876Sguido (void) fr_tcp_age(&clone->nat_tqe, fin, nat_tqb, 4854145522Sdarrenr clone->nat_flags); 4855145522Sdarrenr } 4856145522Sdarrenr#ifdef IPFILTER_SYNC 4857145522Sdarrenr clone->nat_sync = ipfsync_new(SMC_NAT, fin, clone); 4858145522Sdarrenr#endif 4859145522Sdarrenr if (nat_logging) 4860145522Sdarrenr nat_log(clone, NL_CLONE); 4861145522Sdarrenr return clone; 4862145522Sdarrenr} 4863145522Sdarrenr 4864145522Sdarrenr 4865145522Sdarrenr/* ------------------------------------------------------------------------ */ 4866145522Sdarrenr/* Function: nat_wildok */ 4867145522Sdarrenr/* Returns: int - 1 == packet's ports match wildcards */ 4868145522Sdarrenr/* 0 == packet's ports don't match wildcards */ 4869145522Sdarrenr/* Parameters: nat(I) - NAT entry */ 4870145522Sdarrenr/* sport(I) - source port */ 4871145522Sdarrenr/* dport(I) - destination port */ 4872145522Sdarrenr/* flags(I) - wildcard flags */ 4873145522Sdarrenr/* dir(I) - packet direction */ 4874145522Sdarrenr/* */ 4875145522Sdarrenr/* Use NAT entry and packet direction to determine which combination of */ 4876145522Sdarrenr/* wildcard flags should be used. */ 4877145522Sdarrenr/* ------------------------------------------------------------------------ */ 4878153876Sguidostatic int nat_wildok(nat, sport, dport, flags, dir) 4879145522Sdarrenrnat_t *nat; 4880145522Sdarrenrint sport; 4881145522Sdarrenrint dport; 4882145522Sdarrenrint flags; 4883145522Sdarrenrint dir; 4884145522Sdarrenr{ 4885145522Sdarrenr /* 4886145522Sdarrenr * When called by dir is set to 4887145522Sdarrenr * nat_inlookup NAT_INBOUND (0) 4888145522Sdarrenr * nat_outlookup NAT_OUTBOUND (1) 4889145522Sdarrenr * 4890145522Sdarrenr * We simply combine the packet's direction in dir with the original 4891145522Sdarrenr * "intended" direction of that NAT entry in nat->nat_dir to decide 4892145522Sdarrenr * which combination of wildcard flags to allow. 4893145522Sdarrenr */ 4894145522Sdarrenr 4895145522Sdarrenr switch ((dir << 1) | nat->nat_dir) 4896145522Sdarrenr { 4897145522Sdarrenr case 3: /* outbound packet / outbound entry */ 4898145522Sdarrenr if (((nat->nat_inport == sport) || 4899145522Sdarrenr (flags & SI_W_SPORT)) && 4900145522Sdarrenr ((nat->nat_oport == dport) || 4901145522Sdarrenr (flags & SI_W_DPORT))) 4902145522Sdarrenr return 1; 4903145522Sdarrenr break; 4904145522Sdarrenr case 2: /* outbound packet / inbound entry */ 4905145522Sdarrenr if (((nat->nat_outport == sport) || 4906145522Sdarrenr (flags & SI_W_DPORT)) && 4907145522Sdarrenr ((nat->nat_oport == dport) || 4908145522Sdarrenr (flags & SI_W_SPORT))) 4909145522Sdarrenr return 1; 4910145522Sdarrenr break; 4911145522Sdarrenr case 1: /* inbound packet / outbound entry */ 4912145522Sdarrenr if (((nat->nat_oport == sport) || 4913145522Sdarrenr (flags & SI_W_DPORT)) && 4914145522Sdarrenr ((nat->nat_outport == dport) || 4915145522Sdarrenr (flags & SI_W_SPORT))) 4916145522Sdarrenr return 1; 4917145522Sdarrenr break; 4918145522Sdarrenr case 0: /* inbound packet / inbound entry */ 4919145522Sdarrenr if (((nat->nat_oport == sport) || 4920145522Sdarrenr (flags & SI_W_SPORT)) && 4921145522Sdarrenr ((nat->nat_outport == dport) || 4922145522Sdarrenr (flags & SI_W_DPORT))) 4923145522Sdarrenr return 1; 4924145522Sdarrenr break; 4925145522Sdarrenr default: 4926145522Sdarrenr break; 4927145522Sdarrenr } 4928145522Sdarrenr 4929145522Sdarrenr return(0); 4930145522Sdarrenr} 4931145522Sdarrenr 4932145522Sdarrenr 4933145522Sdarrenr/* ------------------------------------------------------------------------ */ 4934145522Sdarrenr/* Function: nat_mssclamp */ 4935145522Sdarrenr/* Returns: Nil */ 4936145522Sdarrenr/* Parameters: tcp(I) - pointer to TCP header */ 4937145522Sdarrenr/* maxmss(I) - value to clamp the TCP MSS to */ 4938145522Sdarrenr/* fin(I) - pointer to packet information */ 4939145522Sdarrenr/* csump(I) - pointer to TCP checksum */ 4940145522Sdarrenr/* */ 4941145522Sdarrenr/* Check for MSS option and clamp it if necessary. If found and changed, */ 4942145522Sdarrenr/* then the TCP header checksum will be updated to reflect the change in */ 4943145522Sdarrenr/* the MSS. */ 4944145522Sdarrenr/* ------------------------------------------------------------------------ */ 4945110916Sdarrenrstatic void nat_mssclamp(tcp, maxmss, fin, csump) 4946110916Sdarrenrtcphdr_t *tcp; 4947110916Sdarrenru_32_t maxmss; 4948110916Sdarrenrfr_info_t *fin; 4949110916Sdarrenru_short *csump; 4950110916Sdarrenr{ 4951110916Sdarrenr u_char *cp, *ep, opt; 4952110916Sdarrenr int hlen, advance; 4953110916Sdarrenr u_32_t mss, sumd; 4954110916Sdarrenr 4955145522Sdarrenr hlen = TCP_OFF(tcp) << 2; 4956110916Sdarrenr if (hlen > sizeof(*tcp)) { 4957110916Sdarrenr cp = (u_char *)tcp + sizeof(*tcp); 4958110916Sdarrenr ep = (u_char *)tcp + hlen; 4959110916Sdarrenr 4960110916Sdarrenr while (cp < ep) { 4961110916Sdarrenr opt = cp[0]; 4962110916Sdarrenr if (opt == TCPOPT_EOL) 4963110916Sdarrenr break; 4964110916Sdarrenr else if (opt == TCPOPT_NOP) { 4965110916Sdarrenr cp++; 4966110916Sdarrenr continue; 4967110916Sdarrenr } 4968145522Sdarrenr 4969145522Sdarrenr if (cp + 1 >= ep) 4970110916Sdarrenr break; 4971110916Sdarrenr advance = cp[1]; 4972145522Sdarrenr if ((cp + advance > ep) || (advance <= 0)) 4973110916Sdarrenr break; 4974145522Sdarrenr switch (opt) 4975145522Sdarrenr { 4976110916Sdarrenr case TCPOPT_MAXSEG: 4977110916Sdarrenr if (advance != 4) 4978110916Sdarrenr break; 4979145522Sdarrenr mss = cp[2] * 256 + cp[3]; 4980110916Sdarrenr if (mss > maxmss) { 4981145522Sdarrenr cp[2] = maxmss / 256; 4982145522Sdarrenr cp[3] = maxmss & 0xff; 4983110916Sdarrenr CALC_SUMD(mss, maxmss, sumd); 4984110916Sdarrenr fix_outcksum(fin, csump, sumd); 4985110916Sdarrenr } 4986110916Sdarrenr break; 4987110916Sdarrenr default: 4988110916Sdarrenr /* ignore unknown options */ 4989110916Sdarrenr break; 4990110916Sdarrenr } 4991145522Sdarrenr 4992145522Sdarrenr cp += advance; 4993145522Sdarrenr } 4994145522Sdarrenr } 4995145522Sdarrenr} 4996145522Sdarrenr 4997145522Sdarrenr 4998145522Sdarrenr/* ------------------------------------------------------------------------ */ 4999145522Sdarrenr/* Function: fr_setnatqueue */ 5000145522Sdarrenr/* Returns: Nil */ 5001145522Sdarrenr/* Parameters: nat(I)- pointer to NAT structure */ 5002145522Sdarrenr/* rev(I) - forward(0) or reverse(1) direction */ 5003145522Sdarrenr/* Locks: ipf_nat (read or write) */ 5004145522Sdarrenr/* */ 5005145522Sdarrenr/* Put the NAT entry on its default queue entry, using rev as a helped in */ 5006145522Sdarrenr/* determining which queue it should be placed on. */ 5007145522Sdarrenr/* ------------------------------------------------------------------------ */ 5008145522Sdarrenrvoid fr_setnatqueue(nat, rev) 5009145522Sdarrenrnat_t *nat; 5010145522Sdarrenrint rev; 5011145522Sdarrenr{ 5012145522Sdarrenr ipftq_t *oifq, *nifq; 5013145522Sdarrenr 5014145522Sdarrenr if (nat->nat_ptr != NULL) 5015145522Sdarrenr nifq = nat->nat_ptr->in_tqehead[rev]; 5016145522Sdarrenr else 5017145522Sdarrenr nifq = NULL; 5018145522Sdarrenr 5019145522Sdarrenr if (nifq == NULL) { 5020145522Sdarrenr switch (nat->nat_p) 5021145522Sdarrenr { 5022145522Sdarrenr case IPPROTO_UDP : 5023145522Sdarrenr nifq = &nat_udptq; 5024145522Sdarrenr break; 5025145522Sdarrenr case IPPROTO_ICMP : 5026145522Sdarrenr nifq = &nat_icmptq; 5027145522Sdarrenr break; 5028145522Sdarrenr case IPPROTO_TCP : 5029145522Sdarrenr nifq = nat_tqb + nat->nat_tqe.tqe_state[rev]; 5030145522Sdarrenr break; 5031145522Sdarrenr default : 5032145522Sdarrenr nifq = &nat_iptq; 5033145522Sdarrenr break; 5034145522Sdarrenr } 5035145522Sdarrenr } 5036145522Sdarrenr 5037145522Sdarrenr oifq = nat->nat_tqe.tqe_ifq; 5038145522Sdarrenr /* 5039145522Sdarrenr * If it's currently on a timeout queue, move it from one queue to 5040145522Sdarrenr * another, else put it on the end of the newly determined queue. 5041145522Sdarrenr */ 5042145522Sdarrenr if (oifq != NULL) 5043145522Sdarrenr fr_movequeue(&nat->nat_tqe, oifq, nifq); 5044145522Sdarrenr else 5045145522Sdarrenr fr_queueappend(&nat->nat_tqe, nifq, nat); 5046145522Sdarrenr return; 5047145522Sdarrenr} 5048170268Sdarrenr 5049170268Sdarrenr 5050170268Sdarrenr/* ------------------------------------------------------------------------ */ 5051170268Sdarrenr/* Function: nat_getnext */ 5052170268Sdarrenr/* Returns: int - 0 == ok, else error */ 5053170268Sdarrenr/* Parameters: t(I) - pointer to ipftoken structure */ 5054170268Sdarrenr/* itp(I) - pointer to ipfgeniter_t structure */ 5055170268Sdarrenr/* */ 5056170268Sdarrenr/* Fetch the next nat/ipnat structure pointer from the linked list and */ 5057170268Sdarrenr/* copy it out to the storage space pointed to by itp_data. The next item */ 5058170268Sdarrenr/* in the list to look at is put back in the ipftoken struture. */ 5059170268Sdarrenr/* If we call ipf_freetoken, the accompanying pointer is set to NULL because*/ 5060170268Sdarrenr/* ipf_freetoken will call a deref function for us and we dont want to call */ 5061170268Sdarrenr/* that twice (second time would be in the second switch statement below. */ 5062170268Sdarrenr/* ------------------------------------------------------------------------ */ 5063170268Sdarrenrstatic int nat_getnext(t, itp) 5064170268Sdarrenripftoken_t *t; 5065170268Sdarrenripfgeniter_t *itp; 5066170268Sdarrenr{ 5067170268Sdarrenr hostmap_t *hm, *nexthm = NULL, zerohm; 5068170268Sdarrenr ipnat_t *ipn, *nextipnat = NULL, zeroipn; 5069170268Sdarrenr nat_t *nat, *nextnat = NULL, zeronat; 5070170268Sdarrenr int error = 0, count; 5071170268Sdarrenr char *dst; 5072170268Sdarrenr 5073172776Sdarrenr count = itp->igi_nitems; 5074172776Sdarrenr if (count < 1) 5075172776Sdarrenr return ENOSPC; 5076170268Sdarrenr 5077170268Sdarrenr READ_ENTER(&ipf_nat); 5078170268Sdarrenr 5079170268Sdarrenr switch (itp->igi_type) 5080170268Sdarrenr { 5081170268Sdarrenr case IPFGENITER_HOSTMAP : 5082170268Sdarrenr hm = t->ipt_data; 5083170268Sdarrenr if (hm == NULL) { 5084170268Sdarrenr nexthm = ipf_hm_maplist; 5085170268Sdarrenr } else { 5086170268Sdarrenr nexthm = hm->hm_next; 5087170268Sdarrenr } 5088170268Sdarrenr break; 5089170268Sdarrenr 5090170268Sdarrenr case IPFGENITER_IPNAT : 5091170268Sdarrenr ipn = t->ipt_data; 5092170268Sdarrenr if (ipn == NULL) { 5093170268Sdarrenr nextipnat = nat_list; 5094170268Sdarrenr } else { 5095170268Sdarrenr nextipnat = ipn->in_next; 5096170268Sdarrenr } 5097170268Sdarrenr break; 5098170268Sdarrenr 5099170268Sdarrenr case IPFGENITER_NAT : 5100170268Sdarrenr nat = t->ipt_data; 5101170268Sdarrenr if (nat == NULL) { 5102170268Sdarrenr nextnat = nat_instances; 5103170268Sdarrenr } else { 5104170268Sdarrenr nextnat = nat->nat_next; 5105170268Sdarrenr } 5106170268Sdarrenr break; 5107170268Sdarrenr default : 5108170268Sdarrenr RWLOCK_EXIT(&ipf_nat); 5109170268Sdarrenr return EINVAL; 5110170268Sdarrenr } 5111170268Sdarrenr 5112170268Sdarrenr dst = itp->igi_data; 5113172776Sdarrenr for (;;) { 5114170268Sdarrenr switch (itp->igi_type) 5115170268Sdarrenr { 5116170268Sdarrenr case IPFGENITER_HOSTMAP : 5117170268Sdarrenr if (nexthm != NULL) { 5118170268Sdarrenr if (count == 1) { 5119170268Sdarrenr ATOMIC_INC32(nexthm->hm_ref); 5120172776Sdarrenr t->ipt_data = nexthm; 5121170268Sdarrenr } 5122170268Sdarrenr } else { 5123170268Sdarrenr bzero(&zerohm, sizeof(zerohm)); 5124170268Sdarrenr nexthm = &zerohm; 5125170268Sdarrenr count = 1; 5126172776Sdarrenr t->ipt_data = NULL; 5127170268Sdarrenr } 5128170268Sdarrenr break; 5129170268Sdarrenr 5130170268Sdarrenr case IPFGENITER_IPNAT : 5131170268Sdarrenr if (nextipnat != NULL) { 5132170268Sdarrenr if (count == 1) { 5133170268Sdarrenr MUTEX_ENTER(&nextipnat->in_lock); 5134170268Sdarrenr nextipnat->in_use++; 5135170268Sdarrenr MUTEX_EXIT(&nextipnat->in_lock); 5136172776Sdarrenr t->ipt_data = nextipnat; 5137170268Sdarrenr } 5138170268Sdarrenr } else { 5139170268Sdarrenr bzero(&zeroipn, sizeof(zeroipn)); 5140170268Sdarrenr nextipnat = &zeroipn; 5141170268Sdarrenr count = 1; 5142172776Sdarrenr t->ipt_data = NULL; 5143170268Sdarrenr } 5144170268Sdarrenr break; 5145170268Sdarrenr 5146170268Sdarrenr case IPFGENITER_NAT : 5147170268Sdarrenr if (nextnat != NULL) { 5148170268Sdarrenr if (count == 1) { 5149170268Sdarrenr MUTEX_ENTER(&nextnat->nat_lock); 5150170268Sdarrenr nextnat->nat_ref++; 5151170268Sdarrenr MUTEX_EXIT(&nextnat->nat_lock); 5152172776Sdarrenr t->ipt_data = nextnat; 5153170268Sdarrenr } 5154170268Sdarrenr } else { 5155170268Sdarrenr bzero(&zeronat, sizeof(zeronat)); 5156170268Sdarrenr nextnat = &zeronat; 5157170268Sdarrenr count = 1; 5158172776Sdarrenr t->ipt_data = NULL; 5159170268Sdarrenr } 5160170268Sdarrenr break; 5161170268Sdarrenr default : 5162170268Sdarrenr break; 5163170268Sdarrenr } 5164170268Sdarrenr RWLOCK_EXIT(&ipf_nat); 5165170268Sdarrenr 5166172776Sdarrenr /* 5167172776Sdarrenr * Copying out to user space needs to be done without the lock. 5168172776Sdarrenr */ 5169170268Sdarrenr switch (itp->igi_type) 5170170268Sdarrenr { 5171170268Sdarrenr case IPFGENITER_HOSTMAP : 5172170268Sdarrenr error = COPYOUT(nexthm, dst, sizeof(*nexthm)); 5173170268Sdarrenr if (error != 0) 5174170268Sdarrenr error = EFAULT; 5175170268Sdarrenr else 5176170268Sdarrenr dst += sizeof(*nexthm); 5177170268Sdarrenr break; 5178170268Sdarrenr 5179170268Sdarrenr case IPFGENITER_IPNAT : 5180170268Sdarrenr error = COPYOUT(nextipnat, dst, sizeof(*nextipnat)); 5181170268Sdarrenr if (error != 0) 5182170268Sdarrenr error = EFAULT; 5183170268Sdarrenr else 5184170268Sdarrenr dst += sizeof(*nextipnat); 5185170268Sdarrenr break; 5186170268Sdarrenr 5187170268Sdarrenr case IPFGENITER_NAT : 5188170268Sdarrenr error = COPYOUT(nextnat, dst, sizeof(*nextnat)); 5189170268Sdarrenr if (error != 0) 5190170268Sdarrenr error = EFAULT; 5191170268Sdarrenr else 5192170268Sdarrenr dst += sizeof(*nextnat); 5193170268Sdarrenr break; 5194170268Sdarrenr } 5195170268Sdarrenr 5196170268Sdarrenr if ((count == 1) || (error != 0)) 5197170268Sdarrenr break; 5198170268Sdarrenr 5199172776Sdarrenr count--; 5200172776Sdarrenr 5201170268Sdarrenr READ_ENTER(&ipf_nat); 5202170268Sdarrenr 5203172776Sdarrenr /* 5204172776Sdarrenr * We need to have the lock again here to make sure that 5205172776Sdarrenr * using _next is consistent. 5206172776Sdarrenr */ 5207170268Sdarrenr switch (itp->igi_type) 5208170268Sdarrenr { 5209170268Sdarrenr case IPFGENITER_HOSTMAP : 5210172776Sdarrenr nexthm = nexthm->hm_next; 5211170268Sdarrenr break; 5212170268Sdarrenr case IPFGENITER_IPNAT : 5213172776Sdarrenr nextipnat = nextipnat->in_next; 5214170268Sdarrenr break; 5215170268Sdarrenr case IPFGENITER_NAT : 5216172776Sdarrenr nextnat = nextnat->nat_next; 5217170268Sdarrenr break; 5218170268Sdarrenr } 5219170268Sdarrenr } 5220170268Sdarrenr 5221172776Sdarrenr 5222172776Sdarrenr switch (itp->igi_type) 5223172776Sdarrenr { 5224172776Sdarrenr case IPFGENITER_HOSTMAP : 5225172776Sdarrenr if (hm != NULL) { 5226172776Sdarrenr WRITE_ENTER(&ipf_nat); 5227172776Sdarrenr fr_hostmapdel(&hm); 5228172776Sdarrenr RWLOCK_EXIT(&ipf_nat); 5229172776Sdarrenr } 5230172776Sdarrenr break; 5231172776Sdarrenr case IPFGENITER_IPNAT : 5232172776Sdarrenr if (ipn != NULL) { 5233172776Sdarrenr fr_ipnatderef(&ipn); 5234172776Sdarrenr } 5235172776Sdarrenr break; 5236172776Sdarrenr case IPFGENITER_NAT : 5237172776Sdarrenr if (nat != NULL) { 5238172776Sdarrenr fr_natderef(&nat); 5239172776Sdarrenr } 5240172776Sdarrenr break; 5241172776Sdarrenr default : 5242172776Sdarrenr break; 5243172776Sdarrenr } 5244172776Sdarrenr 5245170268Sdarrenr return error; 5246170268Sdarrenr} 5247170268Sdarrenr 5248170268Sdarrenr 5249170268Sdarrenr/* ------------------------------------------------------------------------ */ 5250170268Sdarrenr/* Function: nat_iterator */ 5251170268Sdarrenr/* Returns: int - 0 == ok, else error */ 5252170268Sdarrenr/* Parameters: token(I) - pointer to ipftoken structure */ 5253170268Sdarrenr/* itp(I) - pointer to ipfgeniter_t structure */ 5254170268Sdarrenr/* */ 5255170268Sdarrenr/* This function acts as a handler for the SIOCGENITER ioctls that use a */ 5256170268Sdarrenr/* generic structure to iterate through a list. There are three different */ 5257170268Sdarrenr/* linked lists of NAT related information to go through: NAT rules, active */ 5258170268Sdarrenr/* NAT mappings and the NAT fragment cache. */ 5259170268Sdarrenr/* ------------------------------------------------------------------------ */ 5260170268Sdarrenrstatic int nat_iterator(token, itp) 5261170268Sdarrenripftoken_t *token; 5262170268Sdarrenripfgeniter_t *itp; 5263170268Sdarrenr{ 5264170268Sdarrenr int error; 5265170268Sdarrenr 5266170268Sdarrenr if (itp->igi_data == NULL) 5267170268Sdarrenr return EFAULT; 5268170268Sdarrenr 5269170268Sdarrenr token->ipt_subtype = itp->igi_type; 5270170268Sdarrenr 5271170268Sdarrenr switch (itp->igi_type) 5272170268Sdarrenr { 5273170268Sdarrenr case IPFGENITER_HOSTMAP : 5274170268Sdarrenr case IPFGENITER_IPNAT : 5275170268Sdarrenr case IPFGENITER_NAT : 5276170268Sdarrenr error = nat_getnext(token, itp); 5277170268Sdarrenr break; 5278170268Sdarrenr 5279170268Sdarrenr case IPFGENITER_NATFRAG : 5280170268Sdarrenr#ifdef USE_MUTEXES 5281170268Sdarrenr error = fr_nextfrag(token, itp, &ipfr_natlist, 5282170268Sdarrenr &ipfr_nattail, &ipf_natfrag); 5283170268Sdarrenr#else 5284170268Sdarrenr error = fr_nextfrag(token, itp, &ipfr_natlist, &ipfr_nattail); 5285170268Sdarrenr#endif 5286170268Sdarrenr break; 5287170268Sdarrenr default : 5288170268Sdarrenr error = EINVAL; 5289170268Sdarrenr break; 5290170268Sdarrenr } 5291170268Sdarrenr 5292170268Sdarrenr return error; 5293170268Sdarrenr} 5294170268Sdarrenr 5295170268Sdarrenr 5296170268Sdarrenr/* ------------------------------------------------------------------------ */ 5297170268Sdarrenr/* Function: nat_extraflush */ 5298170268Sdarrenr/* Returns: int - 0 == success, -1 == failure */ 5299170268Sdarrenr/* Parameters: which(I) - how to flush the active NAT table */ 5300170268Sdarrenr/* Write Locks: ipf_nat */ 5301170268Sdarrenr/* */ 5302170268Sdarrenr/* Flush nat tables. Three actions currently defined: */ 5303170268Sdarrenr/* which == 0 : flush all nat table entries */ 5304170268Sdarrenr/* which == 1 : flush TCP connections which have started to close but are */ 5305170268Sdarrenr/* stuck for some reason. */ 5306170268Sdarrenr/* which == 2 : flush TCP connections which have been idle for a long time, */ 5307170268Sdarrenr/* starting at > 4 days idle and working back in successive half-*/ 5308170268Sdarrenr/* days to at most 12 hours old. If this fails to free enough */ 5309170268Sdarrenr/* slots then work backwards in half hour slots to 30 minutes. */ 5310170268Sdarrenr/* If that too fails, then work backwards in 30 second intervals */ 5311170268Sdarrenr/* for the last 30 minutes to at worst 30 seconds idle. */ 5312170268Sdarrenr/* ------------------------------------------------------------------------ */ 5313170268Sdarrenrstatic int nat_extraflush(which) 5314170268Sdarrenrint which; 5315170268Sdarrenr{ 5316170268Sdarrenr ipftq_t *ifq, *ifqnext; 5317170268Sdarrenr nat_t *nat, **natp; 5318170268Sdarrenr ipftqent_t *tqn; 5319170268Sdarrenr int removed; 5320170268Sdarrenr SPL_INT(s); 5321170268Sdarrenr 5322170268Sdarrenr removed = 0; 5323170268Sdarrenr 5324170268Sdarrenr SPL_NET(s); 5325170268Sdarrenr 5326170268Sdarrenr switch (which) 5327170268Sdarrenr { 5328170268Sdarrenr case 0 : 5329170268Sdarrenr /* 5330170268Sdarrenr * Style 0 flush removes everything... 5331170268Sdarrenr */ 5332170268Sdarrenr for (natp = &nat_instances; ((nat = *natp) != NULL); ) { 5333170268Sdarrenr nat_delete(nat, NL_FLUSH); 5334170268Sdarrenr removed++; 5335170268Sdarrenr } 5336170268Sdarrenr break; 5337170268Sdarrenr 5338170268Sdarrenr case 1 : 5339170268Sdarrenr /* 5340170268Sdarrenr * Since we're only interested in things that are closing, 5341170268Sdarrenr * we can start with the appropriate timeout queue. 5342170268Sdarrenr */ 5343170268Sdarrenr for (ifq = nat_tqb + IPF_TCPS_CLOSE_WAIT; ifq != NULL; 5344170268Sdarrenr ifq = ifq->ifq_next) { 5345170268Sdarrenr 5346170268Sdarrenr for (tqn = ifq->ifq_head; tqn != NULL; ) { 5347170268Sdarrenr nat = tqn->tqe_parent; 5348170268Sdarrenr tqn = tqn->tqe_next; 5349170268Sdarrenr if (nat->nat_p != IPPROTO_TCP) 5350170268Sdarrenr break; 5351170268Sdarrenr nat_delete(nat, NL_EXPIRE); 5352170268Sdarrenr removed++; 5353170268Sdarrenr } 5354170268Sdarrenr } 5355170268Sdarrenr 5356170268Sdarrenr /* 5357170268Sdarrenr * Also need to look through the user defined queues. 5358170268Sdarrenr */ 5359170268Sdarrenr for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) { 5360170268Sdarrenr ifqnext = ifq->ifq_next; 5361170268Sdarrenr for (tqn = ifq->ifq_head; tqn != NULL; ) { 5362170268Sdarrenr nat = tqn->tqe_parent; 5363170268Sdarrenr tqn = tqn->tqe_next; 5364170268Sdarrenr if (nat->nat_p != IPPROTO_TCP) 5365170268Sdarrenr continue; 5366170268Sdarrenr 5367170268Sdarrenr if ((nat->nat_tcpstate[0] > 5368170268Sdarrenr IPF_TCPS_ESTABLISHED) && 5369170268Sdarrenr (nat->nat_tcpstate[1] > 5370170268Sdarrenr IPF_TCPS_ESTABLISHED)) { 5371170268Sdarrenr nat_delete(nat, NL_EXPIRE); 5372170268Sdarrenr removed++; 5373170268Sdarrenr } 5374170268Sdarrenr } 5375170268Sdarrenr } 5376170268Sdarrenr break; 5377170268Sdarrenr 5378170268Sdarrenr /* 5379170268Sdarrenr * Args 5-11 correspond to flushing those particular states 5380170268Sdarrenr * for TCP connections. 5381170268Sdarrenr */ 5382170268Sdarrenr case IPF_TCPS_CLOSE_WAIT : 5383170268Sdarrenr case IPF_TCPS_FIN_WAIT_1 : 5384170268Sdarrenr case IPF_TCPS_CLOSING : 5385170268Sdarrenr case IPF_TCPS_LAST_ACK : 5386170268Sdarrenr case IPF_TCPS_FIN_WAIT_2 : 5387170268Sdarrenr case IPF_TCPS_TIME_WAIT : 5388170268Sdarrenr case IPF_TCPS_CLOSED : 5389170268Sdarrenr tqn = nat_tqb[which].ifq_head; 5390170268Sdarrenr while (tqn != NULL) { 5391170268Sdarrenr nat = tqn->tqe_parent; 5392170268Sdarrenr tqn = tqn->tqe_next; 5393170268Sdarrenr nat_delete(nat, NL_FLUSH); 5394170268Sdarrenr removed++; 5395170268Sdarrenr } 5396170268Sdarrenr break; 5397170268Sdarrenr 5398170268Sdarrenr default : 5399170268Sdarrenr if (which < 30) 5400170268Sdarrenr break; 5401170268Sdarrenr 5402170268Sdarrenr /* 5403170268Sdarrenr * Take a large arbitrary number to mean the number of seconds 5404170268Sdarrenr * for which which consider to be the maximum value we'll allow 5405170268Sdarrenr * the expiration to be. 5406170268Sdarrenr */ 5407170268Sdarrenr which = IPF_TTLVAL(which); 5408170268Sdarrenr for (natp = &nat_instances; ((nat = *natp) != NULL); ) { 5409170268Sdarrenr if (fr_ticks - nat->nat_touched > which) { 5410170268Sdarrenr nat_delete(nat, NL_FLUSH); 5411170268Sdarrenr removed++; 5412170268Sdarrenr } else 5413170268Sdarrenr natp = &nat->nat_next; 5414170268Sdarrenr } 5415170268Sdarrenr break; 5416170268Sdarrenr } 5417170268Sdarrenr 5418170268Sdarrenr if (which != 2) { 5419170268Sdarrenr SPL_X(s); 5420170268Sdarrenr return removed; 5421170268Sdarrenr } 5422170268Sdarrenr 5423170268Sdarrenr /* 5424170268Sdarrenr * Asked to remove inactive entries because the table is full. 5425170268Sdarrenr */ 5426170268Sdarrenr if (fr_ticks - nat_last_force_flush > IPF_TTLVAL(5)) { 5427170268Sdarrenr nat_last_force_flush = fr_ticks; 5428170268Sdarrenr removed = ipf_queueflush(nat_flush_entry, nat_tqb, nat_utqe); 5429170268Sdarrenr } 5430170268Sdarrenr 5431170268Sdarrenr SPL_X(s); 5432170268Sdarrenr return removed; 5433170268Sdarrenr} 5434170268Sdarrenr 5435170268Sdarrenr 5436170268Sdarrenr/* ------------------------------------------------------------------------ */ 5437170268Sdarrenr/* Function: nat_flush_entry */ 5438170268Sdarrenr/* Returns: 0 - always succeeds */ 5439170268Sdarrenr/* Parameters: entry(I) - pointer to NAT entry */ 5440170268Sdarrenr/* Write Locks: ipf_nat */ 5441170268Sdarrenr/* */ 5442170268Sdarrenr/* This function is a stepping stone between ipf_queueflush() and */ 5443170268Sdarrenr/* nat_dlete(). It is used so we can provide a uniform interface via the */ 5444170268Sdarrenr/* ipf_queueflush() function. Since the nat_delete() function returns void */ 5445170268Sdarrenr/* we translate that to mean it always succeeds in deleting something. */ 5446170268Sdarrenr/* ------------------------------------------------------------------------ */ 5447170268Sdarrenrstatic int nat_flush_entry(entry) 5448170268Sdarrenrvoid *entry; 5449170268Sdarrenr{ 5450170268Sdarrenr nat_delete(entry, NL_FLUSH); 5451170268Sdarrenr return 0; 5452170268Sdarrenr} 5453172776Sdarrenr 5454172776Sdarrenr 5455172776Sdarrenr/* ------------------------------------------------------------------------ */ 5456172776Sdarrenr/* Function: nat_gettable */ 5457172776Sdarrenr/* Returns: int - 0 = success, else error */ 5458172776Sdarrenr/* Parameters: data(I) - pointer to ioctl data */ 5459172776Sdarrenr/* */ 5460172776Sdarrenr/* This function handles ioctl requests for tables of nat information. */ 5461172776Sdarrenr/* At present the only table it deals with is the hash bucket statistics. */ 5462172776Sdarrenr/* ------------------------------------------------------------------------ */ 5463172776Sdarrenrstatic int nat_gettable(data) 5464172776Sdarrenrchar *data; 5465172776Sdarrenr{ 5466172776Sdarrenr ipftable_t table; 5467172776Sdarrenr int error; 5468172776Sdarrenr 5469172776Sdarrenr error = fr_inobj(data, &table, IPFOBJ_GTABLE); 5470172776Sdarrenr if (error != 0) 5471172776Sdarrenr return error; 5472172776Sdarrenr 5473172776Sdarrenr switch (table.ita_type) 5474172776Sdarrenr { 5475172776Sdarrenr case IPFTABLE_BUCKETS_NATIN : 5476172776Sdarrenr error = COPYOUT(nat_stats.ns_bucketlen[0], table.ita_table, 5477172776Sdarrenr ipf_nattable_sz * sizeof(u_long)); 5478172776Sdarrenr break; 5479172776Sdarrenr 5480172776Sdarrenr case IPFTABLE_BUCKETS_NATOUT : 5481172776Sdarrenr error = COPYOUT(nat_stats.ns_bucketlen[1], table.ita_table, 5482172776Sdarrenr ipf_nattable_sz * sizeof(u_long)); 5483172776Sdarrenr break; 5484172776Sdarrenr 5485172776Sdarrenr default : 5486172776Sdarrenr return EINVAL; 5487172776Sdarrenr } 5488172776Sdarrenr 5489172776Sdarrenr if (error != 0) { 5490172776Sdarrenr error = EFAULT; 5491172776Sdarrenr } 5492172776Sdarrenr return error; 5493172776Sdarrenr} 5494