1145522Sdarrenr/* $FreeBSD: releng/10.3/sys/contrib/ipfilter/netinet/ip_nat.c 292979 2015-12-31 06:01:07Z cy $ */ 2145522Sdarrenr 353642Sguido/* 4255332Scy * Copyright (C) 2012 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> 19255332Scy#if defined(_KERNEL) && \ 20255332Scy (defined(__NetBSD_Version) && (__NetBSD_Version >= 399002000)) 21170268Sdarrenr# include <sys/kauth.h> 22170268Sdarrenr#endif 23145522Sdarrenr#if !defined(_KERNEL) 2453642Sguido# include <stdio.h> 2553642Sguido# include <string.h> 2653642Sguido# include <stdlib.h> 27255332Scy# define KERNEL 28255332Scy# ifdef _OpenBSD__ 29145522Sdarrenrstruct file; 30145522Sdarrenr# endif 31145522Sdarrenr# include <sys/uio.h> 32255332Scy# undef KERNEL 3353642Sguido#endif 34255332Scy#if defined(_KERNEL) && \ 35255332Scy defined(__FreeBSD_version) && (__FreeBSD_version >= 220000) 3653642Sguido# include <sys/filio.h> 3753642Sguido# include <sys/fcntl.h> 3853642Sguido#else 3953642Sguido# include <sys/ioctl.h> 4053642Sguido#endif 41153876Sguido#if !defined(AIX) 42153876Sguido# include <sys/fcntl.h> 43153876Sguido#endif 44145522Sdarrenr#if !defined(linux) 4553642Sguido# include <sys/protosw.h> 4653642Sguido#endif 4753642Sguido#include <sys/socket.h> 48145522Sdarrenr#if defined(_KERNEL) 4953642Sguido# include <sys/systm.h> 50145522Sdarrenr# if !defined(__SVR4) && !defined(__svr4__) 5153642Sguido# include <sys/mbuf.h> 5253642Sguido# endif 53145522Sdarrenr#endif 54145522Sdarrenr#if defined(__SVR4) || defined(__svr4__) 5553642Sguido# include <sys/filio.h> 5653642Sguido# include <sys/byteorder.h> 57255332Scy# ifdef KERNEL 5853642Sguido# include <sys/dditypes.h> 5953642Sguido# endif 6053642Sguido# include <sys/stream.h> 6153642Sguido# include <sys/kmem.h> 6253642Sguido#endif 6353642Sguido#if __FreeBSD_version >= 300000 6453642Sguido# include <sys/queue.h> 6553642Sguido#endif 6653642Sguido#include <net/if.h> 6753642Sguido#if __FreeBSD_version >= 300000 6853642Sguido# include <net/if_var.h> 6953642Sguido#endif 7053642Sguido#ifdef sun 7153642Sguido# include <net/af.h> 7253642Sguido#endif 7353642Sguido#include <netinet/in.h> 7453642Sguido#include <netinet/in_systm.h> 7553642Sguido#include <netinet/ip.h> 7653642Sguido 7753642Sguido#ifdef RFC1825 7853642Sguido# include <vpn/md5.h> 7953642Sguido# include <vpn/ipsec.h> 8053642Sguidoextern struct ifnet vpnif; 8153642Sguido#endif 8253642Sguido 83145522Sdarrenr#if !defined(linux) 8453642Sguido# include <netinet/ip_var.h> 8553642Sguido#endif 8653642Sguido#include <netinet/tcp.h> 8753642Sguido#include <netinet/udp.h> 8853642Sguido#include <netinet/ip_icmp.h> 8953642Sguido#include "netinet/ip_compat.h" 9053642Sguido#include <netinet/tcpip.h> 91255332Scy#include "netinet/ipl.h" 9253642Sguido#include "netinet/ip_fil.h" 9353642Sguido#include "netinet/ip_nat.h" 9453642Sguido#include "netinet/ip_frag.h" 9553642Sguido#include "netinet/ip_state.h" 9692685Sdarrenr#include "netinet/ip_proxy.h" 97255332Scy#include "netinet/ip_lookup.h" 98255332Scy#include "netinet/ip_dstlist.h" 99145522Sdarrenr#include "netinet/ip_sync.h" 100255332Scy#if FREEBSD_GE_REV(300000) 10153642Sguido# include <sys/malloc.h> 10253642Sguido#endif 103255332Scy#ifdef HAS_SYS_MD5_H 104255332Scy# include <sys/md5.h> 105255332Scy#else 106255332Scy# include "md5.h" 107255332Scy#endif 108145522Sdarrenr/* END OF INCLUDES */ 109145522Sdarrenr 11053642Sguido#undef SOCKADDR_IN 11153642Sguido#define SOCKADDR_IN struct sockaddr_in 11253642Sguido 11380482Sdarrenr#if !defined(lint) 11480482Sdarrenrstatic const char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed"; 11580482Sdarrenrstatic const char rcsid[] = "@(#)$FreeBSD: releng/10.3/sys/contrib/ipfilter/netinet/ip_nat.c 292979 2015-12-31 06:01:07Z cy $"; 116172776Sdarrenr/* static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.195.2.102 2007/10/16 10:08:10 darrenr Exp $"; */ 11780482Sdarrenr#endif 11880482Sdarrenr 119145522Sdarrenr 120255332Scy#define NATFSUM(n,v,f) ((v) == 4 ? (n)->f.in4.s_addr : (n)->f.i6[0] + \ 121255332Scy (n)->f.i6[1] + (n)->f.i6[2] + (n)->f.i6[3]) 122255332Scy#define NBUMP(x) softn->(x)++ 123255332Scy#define NBUMPD(x, y) do { \ 124255332Scy softn->x.y++; \ 125255332Scy DT(y); \ 126255332Scy } while (0) 127255332Scy#define NBUMPSIDE(y,x) softn->ipf_nat_stats.ns_side[y].x++ 128255332Scy#define NBUMPSIDED(y,x) do { softn->ipf_nat_stats.ns_side[y].x++; \ 129255332Scy DT(x); } while (0) 130255332Scy#define NBUMPSIDEX(y,x,z) \ 131255332Scy do { softn->ipf_nat_stats.ns_side[y].x++; \ 132255332Scy DT(z); } while (0) 133255332Scy#define NBUMPSIDEDF(y,x)do { softn->ipf_nat_stats.ns_side[y].x++; \ 134255332Scy DT1(x, fr_info_t *, fin); } while (0) 135255332Scy 136255332Scyfrentry_t ipfnatblock; 137255332Scy 138255332Scystatic ipftuneable_t ipf_nat_tuneables[] = { 139255332Scy /* nat */ 140255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_lock) }, 141255332Scy "nat_lock", 0, 1, 142255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_lock), 143255332Scy IPFT_RDONLY, NULL, NULL }, 144255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_sz) }, 145255332Scy "nat_table_size", 1, 0x7fffffff, 146255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_table_sz), 147255332Scy 0, NULL, ipf_nat_rehash }, 148255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_max) }, 149255332Scy "nat_table_max", 1, 0x7fffffff, 150255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_table_max), 151255332Scy 0, NULL, NULL }, 152255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maprules_sz) }, 153255332Scy "nat_rules_size", 1, 0x7fffffff, 154255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_maprules_sz), 155255332Scy 0, NULL, ipf_nat_rehash_rules }, 156255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_rdrrules_sz) }, 157255332Scy "rdr_rules_size", 1, 0x7fffffff, 158255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_rdrrules_sz), 159255332Scy 0, NULL, ipf_nat_rehash_rules }, 160255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_hostmap_sz) }, 161255332Scy "hostmap_size", 1, 0x7fffffff, 162255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_hostmap_sz), 163255332Scy 0, NULL, ipf_nat_hostmap_rehash }, 164255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maxbucket) }, 165255332Scy "nat_maxbucket",1, 0x7fffffff, 166255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_maxbucket), 167255332Scy 0, NULL, NULL }, 168255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_logging) }, 169255332Scy "nat_logging", 0, 1, 170255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_logging), 171255332Scy 0, NULL, NULL }, 172255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_doflush) }, 173255332Scy "nat_doflush", 0, 1, 174255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_doflush), 175255332Scy 0, NULL, NULL }, 176255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_low) }, 177255332Scy "nat_table_wm_low", 1, 99, 178255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_low), 179255332Scy 0, NULL, NULL }, 180255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_high) }, 181255332Scy "nat_table_wm_high", 2, 100, 182255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_high), 183255332Scy 0, NULL, NULL }, 184255332Scy { { 0 }, 185255332Scy NULL, 0, 0, 186255332Scy 0, 187255332Scy 0, NULL, NULL } 188255332Scy}; 189255332Scy 190145522Sdarrenr/* ======================================================================== */ 191145522Sdarrenr/* How the NAT is organised and works. */ 192145522Sdarrenr/* */ 193145522Sdarrenr/* Inside (interface y) NAT Outside (interface x) */ 194145522Sdarrenr/* -------------------- -+- ------------------------------------- */ 195255332Scy/* Packet going | out, processsed by ipf_nat_checkout() for x */ 196145522Sdarrenr/* ------------> | ------------> */ 197145522Sdarrenr/* src=10.1.1.1 | src=192.1.1.1 */ 198145522Sdarrenr/* | */ 199255332Scy/* | in, processed by ipf_nat_checkin() for x */ 200145522Sdarrenr/* <------------ | <------------ */ 201145522Sdarrenr/* dst=10.1.1.1 | dst=192.1.1.1 */ 202145522Sdarrenr/* -------------------- -+- ------------------------------------- */ 203255332Scy/* ipf_nat_checkout() - changes ip_src and if required, sport */ 204145522Sdarrenr/* - creates a new mapping, if required. */ 205255332Scy/* ipf_nat_checkin() - changes ip_dst and if required, dport */ 206145522Sdarrenr/* */ 207145522Sdarrenr/* In the NAT table, internal source is recorded as "in" and externally */ 208145522Sdarrenr/* seen as "out". */ 209145522Sdarrenr/* ======================================================================== */ 210145522Sdarrenr 211145522Sdarrenr 212255332Scy#if SOLARIS && !defined(INSTANCES) 213255332Scyextern int pfil_delayed_copy; 214255332Scy#endif 215255332Scy 216255332Scystatic int ipf_nat_flush_entry __P((ipf_main_softc_t *, void *)); 217255332Scystatic int ipf_nat_getent __P((ipf_main_softc_t *, caddr_t, int)); 218255332Scystatic int ipf_nat_getsz __P((ipf_main_softc_t *, caddr_t, int)); 219255332Scystatic int ipf_nat_putent __P((ipf_main_softc_t *, caddr_t, int)); 220255332Scystatic void ipf_nat_addmap __P((ipf_nat_softc_t *, ipnat_t *)); 221255332Scystatic void ipf_nat_addrdr __P((ipf_nat_softc_t *, ipnat_t *)); 222255332Scystatic int ipf_nat_builddivertmp __P((ipf_nat_softc_t *, ipnat_t *)); 223255332Scystatic int ipf_nat_clearlist __P((ipf_main_softc_t *, ipf_nat_softc_t *)); 224255332Scystatic int ipf_nat_cmp_rules __P((ipnat_t *, ipnat_t *)); 225255332Scystatic int ipf_nat_decap __P((fr_info_t *, nat_t *)); 226255332Scystatic void ipf_nat_delrule __P((ipf_main_softc_t *, ipf_nat_softc_t *, 227255332Scy ipnat_t *, int)); 228255332Scystatic int ipf_nat_extraflush __P((ipf_main_softc_t *, ipf_nat_softc_t *, int)); 229255332Scystatic int ipf_nat_finalise __P((fr_info_t *, nat_t *)); 230255332Scystatic int ipf_nat_flushtable __P((ipf_main_softc_t *, ipf_nat_softc_t *)); 231255332Scystatic int ipf_nat_getnext __P((ipf_main_softc_t *, ipftoken_t *, 232255332Scy ipfgeniter_t *, ipfobj_t *)); 233255332Scystatic int ipf_nat_gettable __P((ipf_main_softc_t *, ipf_nat_softc_t *, 234255332Scy char *)); 235255332Scystatic hostmap_t *ipf_nat_hostmap __P((ipf_nat_softc_t *, ipnat_t *, 236255332Scy struct in_addr, struct in_addr, 237255332Scy struct in_addr, u_32_t)); 238255332Scystatic int ipf_nat_icmpquerytype __P((int)); 239255332Scystatic int ipf_nat_iterator __P((ipf_main_softc_t *, ipftoken_t *, 240255332Scy ipfgeniter_t *, ipfobj_t *)); 241255332Scystatic int ipf_nat_match __P((fr_info_t *, ipnat_t *)); 242255332Scystatic int ipf_nat_matcharray __P((nat_t *, int *, u_long)); 243255332Scystatic int ipf_nat_matchflush __P((ipf_main_softc_t *, ipf_nat_softc_t *, 244255332Scy caddr_t)); 245255332Scystatic void ipf_nat_mssclamp __P((tcphdr_t *, u_32_t, fr_info_t *, 246255332Scy u_short *)); 247255332Scystatic int ipf_nat_newmap __P((fr_info_t *, nat_t *, natinfo_t *)); 248255332Scystatic int ipf_nat_newdivert __P((fr_info_t *, nat_t *, natinfo_t *)); 249255332Scystatic int ipf_nat_newrdr __P((fr_info_t *, nat_t *, natinfo_t *)); 250255332Scystatic int ipf_nat_newrewrite __P((fr_info_t *, nat_t *, natinfo_t *)); 251255332Scystatic int ipf_nat_nextaddr __P((fr_info_t *, nat_addr_t *, u_32_t *, 252255332Scy u_32_t *)); 253255332Scystatic int ipf_nat_nextaddrinit __P((ipf_main_softc_t *, char *, 254255332Scy nat_addr_t *, int, void *)); 255255332Scystatic int ipf_nat_resolverule __P((ipf_main_softc_t *, ipnat_t *)); 256255332Scystatic int ipf_nat_ruleaddrinit __P((ipf_main_softc_t *, 257255332Scy ipf_nat_softc_t *, ipnat_t *)); 258255332Scystatic void ipf_nat_rule_fini __P((ipf_main_softc_t *, ipnat_t *)); 259255332Scystatic int ipf_nat_rule_init __P((ipf_main_softc_t *, ipf_nat_softc_t *, 260255332Scy ipnat_t *)); 261255332Scystatic int ipf_nat_siocaddnat __P((ipf_main_softc_t *, ipf_nat_softc_t *, 262255332Scy ipnat_t *, int)); 263255332Scystatic void ipf_nat_siocdelnat __P((ipf_main_softc_t *, ipf_nat_softc_t *, 264255332Scy ipnat_t *, int)); 265255332Scystatic void ipf_nat_tabmove __P((ipf_nat_softc_t *, nat_t *)); 266255332Scy 267255332Scy/* ------------------------------------------------------------------------ */ 268255332Scy/* Function: ipf_nat_main_load */ 269255332Scy/* Returns: int - 0 == success, -1 == failure */ 270255332Scy/* Parameters: Nil */ 271255332Scy/* */ 272255332Scy/* The only global NAT structure that needs to be initialised is the filter */ 273255332Scy/* rule that is used with blocking packets. */ 274255332Scy/* ------------------------------------------------------------------------ */ 275255332Scyint 276255332Scyipf_nat_main_load() 277255332Scy{ 278255332Scy bzero((char *)&ipfnatblock, sizeof(ipfnatblock)); 279255332Scy ipfnatblock.fr_flags = FR_BLOCK|FR_QUICK; 280255332Scy ipfnatblock.fr_ref = 1; 281255332Scy 282255332Scy return 0; 283255332Scy} 284255332Scy 285255332Scy 286255332Scy/* ------------------------------------------------------------------------ */ 287255332Scy/* Function: ipf_nat_main_unload */ 288255332Scy/* Returns: int - 0 == success, -1 == failure */ 289255332Scy/* Parameters: Nil */ 290255332Scy/* */ 291255332Scy/* A null-op function that exists as a placeholder so that the flow in */ 292255332Scy/* other functions is obvious. */ 293255332Scy/* ------------------------------------------------------------------------ */ 294255332Scyint 295255332Scyipf_nat_main_unload() 296255332Scy{ 297255332Scy return 0; 298255332Scy} 299255332Scy 300255332Scy 301255332Scy/* ------------------------------------------------------------------------ */ 302255332Scy/* Function: ipf_nat_soft_create */ 303255332Scy/* Returns: void * - NULL = failure, else pointer to NAT context */ 304255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 305255332Scy/* */ 306255332Scy/* Allocate the initial soft context structure for NAT and populate it with */ 307255332Scy/* some default values. Creating the tables is left until we call _init so */ 308255332Scy/* that sizes can be changed before we get under way. */ 309255332Scy/* ------------------------------------------------------------------------ */ 310255332Scyvoid * 311255332Scyipf_nat_soft_create(softc) 312255332Scy ipf_main_softc_t *softc; 313255332Scy{ 314255332Scy ipf_nat_softc_t *softn; 315255332Scy 316255332Scy KMALLOC(softn, ipf_nat_softc_t *); 317255332Scy if (softn == NULL) 318255332Scy return NULL; 319255332Scy 320255332Scy bzero((char *)softn, sizeof(*softn)); 321255332Scy 322255332Scy softn->ipf_nat_tune = ipf_tune_array_copy(softn, 323255332Scy sizeof(ipf_nat_tuneables), 324255332Scy ipf_nat_tuneables); 325255332Scy if (softn->ipf_nat_tune == NULL) { 326255332Scy ipf_nat_soft_destroy(softc, softn); 327255332Scy return NULL; 328255332Scy } 329255332Scy if (ipf_tune_array_link(softc, softn->ipf_nat_tune) == -1) { 330255332Scy ipf_nat_soft_destroy(softc, softn); 331255332Scy return NULL; 332255332Scy } 333255332Scy 334255332Scy softn->ipf_nat_list_tail = &softn->ipf_nat_list; 335255332Scy 336255332Scy softn->ipf_nat_table_max = NAT_TABLE_MAX; 337255332Scy softn->ipf_nat_table_sz = NAT_TABLE_SZ; 338255332Scy softn->ipf_nat_maprules_sz = NAT_SIZE; 339255332Scy softn->ipf_nat_rdrrules_sz = RDR_SIZE; 340255332Scy softn->ipf_nat_hostmap_sz = HOSTMAP_SIZE; 341255332Scy softn->ipf_nat_doflush = 0; 342145522Sdarrenr#ifdef IPFILTER_LOG 343255332Scy softn->ipf_nat_logging = 1; 344145522Sdarrenr#else 345255332Scy softn->ipf_nat_logging = 0; 346145522Sdarrenr#endif 34753642Sguido 348255332Scy softn->ipf_nat_defage = DEF_NAT_AGE; 349255332Scy softn->ipf_nat_defipage = IPF_TTLVAL(60); 350255332Scy softn->ipf_nat_deficmpage = IPF_TTLVAL(3); 351255332Scy softn->ipf_nat_table_wm_high = 99; 352255332Scy softn->ipf_nat_table_wm_low = 90; 35353642Sguido 354255332Scy return softn; 355255332Scy} 35653642Sguido 357255332Scy/* ------------------------------------------------------------------------ */ 358255332Scy/* Function: ipf_nat_soft_destroy */ 359255332Scy/* Returns: Nil */ 360255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 361255332Scy/* */ 362255332Scy/* ------------------------------------------------------------------------ */ 363255332Scyvoid 364255332Scyipf_nat_soft_destroy(softc, arg) 365255332Scy ipf_main_softc_t *softc; 366255332Scy void *arg; 367255332Scy{ 368255332Scy ipf_nat_softc_t *softn = arg; 36953642Sguido 370255332Scy if (softn->ipf_nat_tune != NULL) { 371255332Scy ipf_tune_array_unlink(softc, softn->ipf_nat_tune); 372255332Scy KFREES(softn->ipf_nat_tune, sizeof(ipf_nat_tuneables)); 373255332Scy softn->ipf_nat_tune = NULL; 374255332Scy } 375255332Scy 376255332Scy KFREE(softn); 377255332Scy} 378255332Scy 379255332Scy 380145522Sdarrenr/* ------------------------------------------------------------------------ */ 381255332Scy/* Function: ipf_nat_init */ 382145522Sdarrenr/* Returns: int - 0 == success, -1 == failure */ 383255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 384145522Sdarrenr/* */ 385145522Sdarrenr/* Initialise all of the NAT locks, tables and other structures. */ 386145522Sdarrenr/* ------------------------------------------------------------------------ */ 387255332Scyint 388255332Scyipf_nat_soft_init(softc, arg) 389255332Scy ipf_main_softc_t *softc; 390255332Scy void *arg; 39153642Sguido{ 392255332Scy ipf_nat_softc_t *softn = arg; 393255332Scy ipftq_t *tq; 394145522Sdarrenr int i; 395145522Sdarrenr 396255332Scy KMALLOCS(softn->ipf_nat_table[0], nat_t **, \ 397255332Scy sizeof(nat_t *) * softn->ipf_nat_table_sz); 398255332Scy 399255332Scy if (softn->ipf_nat_table[0] != NULL) { 400255332Scy bzero((char *)softn->ipf_nat_table[0], 401255332Scy softn->ipf_nat_table_sz * sizeof(nat_t *)); 402255332Scy } else { 40353642Sguido return -1; 404255332Scy } 40553642Sguido 406255332Scy KMALLOCS(softn->ipf_nat_table[1], nat_t **, \ 407255332Scy sizeof(nat_t *) * softn->ipf_nat_table_sz); 408255332Scy 409255332Scy if (softn->ipf_nat_table[1] != NULL) { 410255332Scy bzero((char *)softn->ipf_nat_table[1], 411255332Scy softn->ipf_nat_table_sz * sizeof(nat_t *)); 412255332Scy } else { 413145522Sdarrenr return -2; 414255332Scy } 41553642Sguido 416255332Scy KMALLOCS(softn->ipf_nat_map_rules, ipnat_t **, \ 417255332Scy sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz); 418255332Scy 419255332Scy if (softn->ipf_nat_map_rules != NULL) { 420255332Scy bzero((char *)softn->ipf_nat_map_rules, 421255332Scy softn->ipf_nat_maprules_sz * sizeof(ipnat_t *)); 422255332Scy } else { 423145522Sdarrenr return -3; 424255332Scy } 42553642Sguido 426255332Scy KMALLOCS(softn->ipf_nat_rdr_rules, ipnat_t **, \ 427255332Scy sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz); 428255332Scy 429255332Scy if (softn->ipf_nat_rdr_rules != NULL) { 430255332Scy bzero((char *)softn->ipf_nat_rdr_rules, 431255332Scy softn->ipf_nat_rdrrules_sz * sizeof(ipnat_t *)); 432255332Scy } else { 433145522Sdarrenr return -4; 434255332Scy } 43560852Sdarrenr 436255332Scy KMALLOCS(softn->ipf_hm_maptable, hostmap_t **, \ 437255332Scy sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); 438255332Scy 439255332Scy if (softn->ipf_hm_maptable != NULL) { 440255332Scy bzero((char *)softn->ipf_hm_maptable, 441255332Scy sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); 442255332Scy } else { 443145522Sdarrenr return -5; 444255332Scy } 445255332Scy softn->ipf_hm_maplist = NULL; 446145522Sdarrenr 447255332Scy KMALLOCS(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, u_int *, 448255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 449255332Scy 450255332Scy if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen == NULL) { 451145522Sdarrenr return -6; 452255332Scy } 453255332Scy bzero((char *)softn->ipf_nat_stats.ns_side[0].ns_bucketlen, 454255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 455145522Sdarrenr 456255332Scy KMALLOCS(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, u_int *, 457255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 458255332Scy 459255332Scy if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen == NULL) { 460145522Sdarrenr return -7; 461255332Scy } 462145522Sdarrenr 463255332Scy bzero((char *)softn->ipf_nat_stats.ns_side[1].ns_bucketlen, 464255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 465145522Sdarrenr 466255332Scy if (softn->ipf_nat_maxbucket == 0) { 467255332Scy for (i = softn->ipf_nat_table_sz; i > 0; i >>= 1) 468255332Scy softn->ipf_nat_maxbucket++; 469255332Scy softn->ipf_nat_maxbucket *= 2; 470145522Sdarrenr } 471145522Sdarrenr 472255332Scy ipf_sttab_init(softc, softn->ipf_nat_tcptq); 473145522Sdarrenr /* 474145522Sdarrenr * Increase this because we may have "keep state" following this too 475145522Sdarrenr * and packet storms can occur if this is removed too quickly. 476145522Sdarrenr */ 477255332Scy softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack; 478255332Scy softn->ipf_nat_tcptq[IPF_TCP_NSTATES - 1].ifq_next = 479255332Scy &softn->ipf_nat_udptq; 480145522Sdarrenr 481255332Scy IPFTQ_INIT(&softn->ipf_nat_udptq, softn->ipf_nat_defage, 482255332Scy "nat ipftq udp tab"); 483255332Scy softn->ipf_nat_udptq.ifq_next = &softn->ipf_nat_udpacktq; 484255332Scy 485255332Scy IPFTQ_INIT(&softn->ipf_nat_udpacktq, softn->ipf_nat_defage, 486255332Scy "nat ipftq udpack tab"); 487255332Scy softn->ipf_nat_udpacktq.ifq_next = &softn->ipf_nat_icmptq; 488255332Scy 489255332Scy IPFTQ_INIT(&softn->ipf_nat_icmptq, softn->ipf_nat_deficmpage, 490255332Scy "nat icmp ipftq tab"); 491255332Scy softn->ipf_nat_icmptq.ifq_next = &softn->ipf_nat_icmpacktq; 492255332Scy 493255332Scy IPFTQ_INIT(&softn->ipf_nat_icmpacktq, softn->ipf_nat_defage, 494255332Scy "nat icmpack ipftq tab"); 495255332Scy softn->ipf_nat_icmpacktq.ifq_next = &softn->ipf_nat_iptq; 496255332Scy 497255332Scy IPFTQ_INIT(&softn->ipf_nat_iptq, softn->ipf_nat_defipage, 498255332Scy "nat ip ipftq tab"); 499255332Scy softn->ipf_nat_iptq.ifq_next = &softn->ipf_nat_pending; 500255332Scy 501255332Scy IPFTQ_INIT(&softn->ipf_nat_pending, 1, "nat pending ipftq tab"); 502255332Scy softn->ipf_nat_pending.ifq_next = NULL; 503255332Scy 504255332Scy for (i = 0, tq = softn->ipf_nat_tcptq; i < IPF_TCP_NSTATES; i++, tq++) { 505255332Scy if (tq->ifq_ttl < softn->ipf_nat_deficmpage) 506255332Scy tq->ifq_ttl = softn->ipf_nat_deficmpage; 507145522Sdarrenr#ifdef LARGE_NAT 508255332Scy else if (tq->ifq_ttl > softn->ipf_nat_defage) 509255332Scy tq->ifq_ttl = softn->ipf_nat_defage; 510145522Sdarrenr#endif 511145522Sdarrenr } 512145522Sdarrenr 513145522Sdarrenr /* 514145522Sdarrenr * Increase this because we may have "keep state" following 515145522Sdarrenr * this too and packet storms can occur if this is removed 516145522Sdarrenr * too quickly. 517145522Sdarrenr */ 518255332Scy softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack; 519145522Sdarrenr 520255332Scy MUTEX_INIT(&softn->ipf_nat_new, "ipf nat new mutex"); 521255332Scy MUTEX_INIT(&softn->ipf_nat_io, "ipf nat io mutex"); 522145522Sdarrenr 523255332Scy softn->ipf_nat_inited = 1; 524145522Sdarrenr 52553642Sguido return 0; 52653642Sguido} 52753642Sguido 52853642Sguido 529145522Sdarrenr/* ------------------------------------------------------------------------ */ 530255332Scy/* Function: ipf_nat_soft_fini */ 531145522Sdarrenr/* Returns: Nil */ 532255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 533255332Scy/* */ 534255332Scy/* Free all memory used by NAT structures allocated at runtime. */ 535255332Scy/* ------------------------------------------------------------------------ */ 536255332Scyint 537255332Scyipf_nat_soft_fini(softc, arg) 538255332Scy ipf_main_softc_t *softc; 539255332Scy void *arg; 540255332Scy{ 541255332Scy ipf_nat_softc_t *softn = arg; 542255332Scy ipftq_t *ifq, *ifqnext; 543255332Scy 544255332Scy (void) ipf_nat_clearlist(softc, softn); 545255332Scy (void) ipf_nat_flushtable(softc, softn); 546255332Scy 547255332Scy /* 548255332Scy * Proxy timeout queues are not cleaned here because although they 549255332Scy * exist on the NAT list, ipf_proxy_unload is called after unload 550255332Scy * and the proxies actually are responsible for them being created. 551255332Scy * Should the proxy timeouts have their own list? There's no real 552255332Scy * justification as this is the only complication. 553255332Scy */ 554255332Scy for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) { 555255332Scy ifqnext = ifq->ifq_next; 556255332Scy if (ipf_deletetimeoutqueue(ifq) == 0) 557255332Scy ipf_freetimeoutqueue(softc, ifq); 558255332Scy } 559255332Scy 560255332Scy if (softn->ipf_nat_table[0] != NULL) { 561255332Scy KFREES(softn->ipf_nat_table[0], 562255332Scy sizeof(nat_t *) * softn->ipf_nat_table_sz); 563255332Scy softn->ipf_nat_table[0] = NULL; 564255332Scy } 565255332Scy if (softn->ipf_nat_table[1] != NULL) { 566255332Scy KFREES(softn->ipf_nat_table[1], 567255332Scy sizeof(nat_t *) * softn->ipf_nat_table_sz); 568255332Scy softn->ipf_nat_table[1] = NULL; 569255332Scy } 570255332Scy if (softn->ipf_nat_map_rules != NULL) { 571255332Scy KFREES(softn->ipf_nat_map_rules, 572255332Scy sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz); 573255332Scy softn->ipf_nat_map_rules = NULL; 574255332Scy } 575255332Scy if (softn->ipf_nat_rdr_rules != NULL) { 576255332Scy KFREES(softn->ipf_nat_rdr_rules, 577255332Scy sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz); 578255332Scy softn->ipf_nat_rdr_rules = NULL; 579255332Scy } 580255332Scy if (softn->ipf_hm_maptable != NULL) { 581255332Scy KFREES(softn->ipf_hm_maptable, 582255332Scy sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); 583255332Scy softn->ipf_hm_maptable = NULL; 584255332Scy } 585255332Scy if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) { 586255332Scy KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, 587255332Scy sizeof(u_int) * softn->ipf_nat_table_sz); 588255332Scy softn->ipf_nat_stats.ns_side[0].ns_bucketlen = NULL; 589255332Scy } 590255332Scy if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) { 591255332Scy KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, 592255332Scy sizeof(u_int) * softn->ipf_nat_table_sz); 593255332Scy softn->ipf_nat_stats.ns_side[1].ns_bucketlen = NULL; 594255332Scy } 595255332Scy 596255332Scy if (softn->ipf_nat_inited == 1) { 597255332Scy softn->ipf_nat_inited = 0; 598255332Scy ipf_sttab_destroy(softn->ipf_nat_tcptq); 599255332Scy 600255332Scy MUTEX_DESTROY(&softn->ipf_nat_new); 601255332Scy MUTEX_DESTROY(&softn->ipf_nat_io); 602255332Scy 603255332Scy MUTEX_DESTROY(&softn->ipf_nat_udptq.ifq_lock); 604255332Scy MUTEX_DESTROY(&softn->ipf_nat_udpacktq.ifq_lock); 605255332Scy MUTEX_DESTROY(&softn->ipf_nat_icmptq.ifq_lock); 606255332Scy MUTEX_DESTROY(&softn->ipf_nat_icmpacktq.ifq_lock); 607255332Scy MUTEX_DESTROY(&softn->ipf_nat_iptq.ifq_lock); 608255332Scy MUTEX_DESTROY(&softn->ipf_nat_pending.ifq_lock); 609255332Scy } 610255332Scy 611255332Scy return 0; 612255332Scy} 613255332Scy 614255332Scy 615255332Scy/* ------------------------------------------------------------------------ */ 616255332Scy/* Function: ipf_nat_setlock */ 617255332Scy/* Returns: Nil */ 618255332Scy/* Parameters: arg(I) - pointer to soft state information */ 619255332Scy/* tmp(I) - new lock value */ 620255332Scy/* */ 621255332Scy/* Set the "lock status" of NAT to the value in tmp. */ 622255332Scy/* ------------------------------------------------------------------------ */ 623255332Scyvoid 624255332Scyipf_nat_setlock(arg, tmp) 625255332Scy void *arg; 626255332Scy int tmp; 627255332Scy{ 628255332Scy ipf_nat_softc_t *softn = arg; 629255332Scy 630255332Scy softn->ipf_nat_lock = tmp; 631255332Scy} 632255332Scy 633255332Scy 634255332Scy/* ------------------------------------------------------------------------ */ 635255332Scy/* Function: ipf_nat_addrdr */ 636255332Scy/* Returns: Nil */ 637145522Sdarrenr/* Parameters: n(I) - pointer to NAT rule to add */ 638145522Sdarrenr/* */ 639145522Sdarrenr/* Adds a redirect rule to the hash table of redirect rules and the list of */ 640145522Sdarrenr/* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ 641145522Sdarrenr/* use by redirect rules. */ 642145522Sdarrenr/* ------------------------------------------------------------------------ */ 643255332Scystatic void 644255332Scyipf_nat_addrdr(softn, n) 645255332Scy ipf_nat_softc_t *softn; 646255332Scy ipnat_t *n; 64753642Sguido{ 64860852Sdarrenr ipnat_t **np; 64960852Sdarrenr u_32_t j; 65053642Sguido u_int hv; 651255332Scy u_int rhv; 65260852Sdarrenr int k; 65353642Sguido 654255332Scy if (n->in_odstatype == FRI_NORMAL) { 655255332Scy k = count4bits(n->in_odstmsk); 656255332Scy ipf_inet_mask_add(k, &softn->ipf_nat_rdr_mask); 657255332Scy j = (n->in_odstaddr & n->in_odstmsk); 658255332Scy rhv = NAT_HASH_FN(j, 0, 0xffffffff); 659255332Scy } else { 660255332Scy ipf_inet_mask_add(0, &softn->ipf_nat_rdr_mask); 661255332Scy j = 0; 662255332Scy rhv = 0; 663255332Scy } 664255332Scy hv = rhv % softn->ipf_nat_rdrrules_sz; 665255332Scy np = softn->ipf_nat_rdr_rules + hv; 66660852Sdarrenr while (*np != NULL) 66760852Sdarrenr np = &(*np)->in_rnext; 66860852Sdarrenr n->in_rnext = NULL; 66960852Sdarrenr n->in_prnext = np; 670255332Scy n->in_hv[0] = hv; 671255332Scy n->in_use++; 67260852Sdarrenr *np = n; 67353642Sguido} 67453642Sguido 67553642Sguido 676145522Sdarrenr/* ------------------------------------------------------------------------ */ 677255332Scy/* Function: ipf_nat_addmap */ 678145522Sdarrenr/* Returns: Nil */ 679145522Sdarrenr/* Parameters: n(I) - pointer to NAT rule to add */ 680145522Sdarrenr/* */ 681145522Sdarrenr/* Adds a NAT map rule to the hash table of rules and the list of loaded */ 682145522Sdarrenr/* NAT rules. Updates the bitmask indicating which netmasks are in use by */ 683145522Sdarrenr/* redirect rules. */ 684145522Sdarrenr/* ------------------------------------------------------------------------ */ 685255332Scystatic void 686255332Scyipf_nat_addmap(softn, n) 687255332Scy ipf_nat_softc_t *softn; 688255332Scy ipnat_t *n; 68960852Sdarrenr{ 69060852Sdarrenr ipnat_t **np; 69160852Sdarrenr u_32_t j; 69260852Sdarrenr u_int hv; 693255332Scy u_int rhv; 69460852Sdarrenr int k; 69560852Sdarrenr 696255332Scy if (n->in_osrcatype == FRI_NORMAL) { 697255332Scy k = count4bits(n->in_osrcmsk); 698255332Scy ipf_inet_mask_add(k, &softn->ipf_nat_map_mask); 699255332Scy j = (n->in_osrcaddr & n->in_osrcmsk); 700255332Scy rhv = NAT_HASH_FN(j, 0, 0xffffffff); 701255332Scy } else { 702255332Scy ipf_inet_mask_add(0, &softn->ipf_nat_map_mask); 703255332Scy j = 0; 704255332Scy rhv = 0; 705255332Scy } 706255332Scy hv = rhv % softn->ipf_nat_maprules_sz; 707255332Scy np = softn->ipf_nat_map_rules + hv; 70860852Sdarrenr while (*np != NULL) 70960852Sdarrenr np = &(*np)->in_mnext; 71060852Sdarrenr n->in_mnext = NULL; 71160852Sdarrenr n->in_pmnext = np; 712255332Scy n->in_hv[1] = rhv; 713255332Scy n->in_use++; 71460852Sdarrenr *np = n; 71560852Sdarrenr} 71660852Sdarrenr 71760852Sdarrenr 718145522Sdarrenr/* ------------------------------------------------------------------------ */ 719255332Scy/* Function: ipf_nat_delrdr */ 720145522Sdarrenr/* Returns: Nil */ 721145522Sdarrenr/* Parameters: n(I) - pointer to NAT rule to delete */ 722145522Sdarrenr/* */ 723145522Sdarrenr/* Removes a redirect rule from the hash table of redirect rules. */ 724145522Sdarrenr/* ------------------------------------------------------------------------ */ 725255332Scyvoid 726255332Scyipf_nat_delrdr(softn, n) 727255332Scy ipf_nat_softc_t *softn; 728255332Scy ipnat_t *n; 72960852Sdarrenr{ 730255332Scy if (n->in_odstatype == FRI_NORMAL) { 731255332Scy int k = count4bits(n->in_odstmsk); 732255332Scy ipf_inet_mask_del(k, &softn->ipf_nat_rdr_mask); 733255332Scy } else { 734255332Scy ipf_inet_mask_del(0, &softn->ipf_nat_rdr_mask); 735255332Scy } 73660852Sdarrenr if (n->in_rnext) 73760852Sdarrenr n->in_rnext->in_prnext = n->in_prnext; 73860852Sdarrenr *n->in_prnext = n->in_rnext; 739255332Scy n->in_use--; 74060852Sdarrenr} 74160852Sdarrenr 74260852Sdarrenr 743145522Sdarrenr/* ------------------------------------------------------------------------ */ 744255332Scy/* Function: ipf_nat_delmap */ 745145522Sdarrenr/* Returns: Nil */ 746145522Sdarrenr/* Parameters: n(I) - pointer to NAT rule to delete */ 747145522Sdarrenr/* */ 748145522Sdarrenr/* Removes a NAT map rule from the hash table of NAT map rules. */ 749145522Sdarrenr/* ------------------------------------------------------------------------ */ 750255332Scyvoid 751255332Scyipf_nat_delmap(softn, n) 752255332Scy ipf_nat_softc_t *softn; 753255332Scy ipnat_t *n; 75453642Sguido{ 755255332Scy if (n->in_osrcatype == FRI_NORMAL) { 756255332Scy int k = count4bits(n->in_osrcmsk); 757255332Scy ipf_inet_mask_del(k, &softn->ipf_nat_map_mask); 758255332Scy } else { 759255332Scy ipf_inet_mask_del(0, &softn->ipf_nat_map_mask); 760255332Scy } 761145522Sdarrenr if (n->in_mnext != NULL) 76260852Sdarrenr n->in_mnext->in_pmnext = n->in_pmnext; 76360852Sdarrenr *n->in_pmnext = n->in_mnext; 764255332Scy n->in_use--; 76560852Sdarrenr} 76660852Sdarrenr 76760852Sdarrenr 768145522Sdarrenr/* ------------------------------------------------------------------------ */ 769255332Scy/* Function: ipf_nat_hostmap */ 770145522Sdarrenr/* Returns: struct hostmap* - NULL if no hostmap could be created, */ 771145522Sdarrenr/* else a pointer to the hostmapping to use */ 772145522Sdarrenr/* Parameters: np(I) - pointer to NAT rule */ 773145522Sdarrenr/* real(I) - real IP address */ 774145522Sdarrenr/* map(I) - mapped IP address */ 775145522Sdarrenr/* port(I) - destination port number */ 776145522Sdarrenr/* Write Locks: ipf_nat */ 777145522Sdarrenr/* */ 778145522Sdarrenr/* Check if an ip address has already been allocated for a given mapping */ 779145522Sdarrenr/* that is not doing port based translation. If is not yet allocated, then */ 780145522Sdarrenr/* create a new entry if a non-NULL NAT rule pointer has been supplied. */ 781145522Sdarrenr/* ------------------------------------------------------------------------ */ 782255332Scystatic struct hostmap * 783255332Scyipf_nat_hostmap(softn, np, src, dst, map, port) 784255332Scy ipf_nat_softc_t *softn; 785255332Scy ipnat_t *np; 786255332Scy struct in_addr src; 787255332Scy struct in_addr dst; 788255332Scy struct in_addr map; 789255332Scy u_32_t port; 79060852Sdarrenr{ 79160852Sdarrenr hostmap_t *hm; 792255332Scy u_int hv, rhv; 79353642Sguido 794145522Sdarrenr hv = (src.s_addr ^ dst.s_addr); 795145522Sdarrenr hv += src.s_addr; 796145522Sdarrenr hv += dst.s_addr; 797255332Scy rhv = hv; 798255332Scy hv %= softn->ipf_nat_hostmap_sz; 799255332Scy for (hm = softn->ipf_hm_maptable[hv]; hm; hm = hm->hm_hnext) 800255332Scy if ((hm->hm_osrcip.s_addr == src.s_addr) && 801255332Scy (hm->hm_odstip.s_addr == dst.s_addr) && 802145522Sdarrenr ((np == NULL) || (np == hm->hm_ipnat)) && 803145522Sdarrenr ((port == 0) || (port == hm->hm_port))) { 804255332Scy softn->ipf_nat_stats.ns_hm_addref++; 80560852Sdarrenr hm->hm_ref++; 80660852Sdarrenr return hm; 80760852Sdarrenr } 80860852Sdarrenr 809255332Scy if (np == NULL) { 810255332Scy softn->ipf_nat_stats.ns_hm_nullnp++; 811145522Sdarrenr return NULL; 812255332Scy } 813145522Sdarrenr 81460852Sdarrenr KMALLOC(hm, hostmap_t *); 81560852Sdarrenr if (hm) { 816255332Scy hm->hm_next = softn->ipf_hm_maplist; 817255332Scy hm->hm_pnext = &softn->ipf_hm_maplist; 818255332Scy if (softn->ipf_hm_maplist != NULL) 819255332Scy softn->ipf_hm_maplist->hm_pnext = &hm->hm_next; 820255332Scy softn->ipf_hm_maplist = hm; 821255332Scy hm->hm_hnext = softn->ipf_hm_maptable[hv]; 822255332Scy hm->hm_phnext = softn->ipf_hm_maptable + hv; 823255332Scy if (softn->ipf_hm_maptable[hv] != NULL) 824255332Scy softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; 825255332Scy softn->ipf_hm_maptable[hv] = hm; 82660852Sdarrenr hm->hm_ipnat = np; 827255332Scy np->in_use++; 828255332Scy hm->hm_osrcip = src; 829255332Scy hm->hm_odstip = dst; 830255332Scy hm->hm_nsrcip = map; 831255332Scy hm->hm_ndstip.s_addr = 0; 83260852Sdarrenr hm->hm_ref = 1; 833145522Sdarrenr hm->hm_port = port; 834255332Scy hm->hm_hv = rhv; 835255332Scy hm->hm_v = 4; 836255332Scy softn->ipf_nat_stats.ns_hm_new++; 837255332Scy } else { 838255332Scy softn->ipf_nat_stats.ns_hm_newfail++; 83960852Sdarrenr } 84060852Sdarrenr return hm; 84153642Sguido} 84253642Sguido 84353642Sguido 844145522Sdarrenr/* ------------------------------------------------------------------------ */ 845255332Scy/* Function: ipf_nat_hostmapdel */ 846145522Sdarrenr/* Returns: Nil */ 847170268Sdarrenr/* Parameters: hmp(I) - pointer to hostmap structure pointer */ 848145522Sdarrenr/* Write Locks: ipf_nat */ 849145522Sdarrenr/* */ 850145522Sdarrenr/* Decrement the references to this hostmap structure by one. If this */ 851145522Sdarrenr/* reaches zero then remove it and free it. */ 852145522Sdarrenr/* ------------------------------------------------------------------------ */ 853255332Scyvoid 854255332Scyipf_nat_hostmapdel(softc, hmp) 855255332Scy ipf_main_softc_t *softc; 856255332Scy struct hostmap **hmp; 85760852Sdarrenr{ 858170268Sdarrenr struct hostmap *hm; 859170268Sdarrenr 860170268Sdarrenr hm = *hmp; 861170268Sdarrenr *hmp = NULL; 862170268Sdarrenr 863145522Sdarrenr hm->hm_ref--; 86460852Sdarrenr if (hm->hm_ref == 0) { 865255332Scy ipf_nat_rule_deref(softc, &hm->hm_ipnat); 866170268Sdarrenr if (hm->hm_hnext) 867170268Sdarrenr hm->hm_hnext->hm_phnext = hm->hm_phnext; 868170268Sdarrenr *hm->hm_phnext = hm->hm_hnext; 86960852Sdarrenr if (hm->hm_next) 87060852Sdarrenr hm->hm_next->hm_pnext = hm->hm_pnext; 87160852Sdarrenr *hm->hm_pnext = hm->hm_next; 87260852Sdarrenr KFREE(hm); 87360852Sdarrenr } 87460852Sdarrenr} 87560852Sdarrenr 87660852Sdarrenr 877145522Sdarrenr/* ------------------------------------------------------------------------ */ 878255332Scy/* Function: ipf_fix_outcksum */ 879145522Sdarrenr/* Returns: Nil */ 880145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 881145522Sdarrenr/* sp(I) - location of 16bit checksum to update */ 882145522Sdarrenr/* n((I) - amount to adjust checksum by */ 883145522Sdarrenr/* */ 884145522Sdarrenr/* Adjusts the 16bit checksum by "n" for packets going out. */ 885145522Sdarrenr/* ------------------------------------------------------------------------ */ 886255332Scyvoid 887255332Scyipf_fix_outcksum(cksum, sp, n, partial) 888255332Scy int cksum; 889255332Scy u_short *sp; 890255332Scy u_32_t n, partial; 89153642Sguido{ 892145522Sdarrenr u_short sumshort; 893145522Sdarrenr u_32_t sum1; 89453642Sguido 895145522Sdarrenr if (n == 0) 89653642Sguido return; 897145522Sdarrenr 898255332Scy if (cksum == 4) { 899255332Scy *sp = 0; 90055929Sguido return; 90155929Sguido } 902255332Scy if (cksum == 2) { 903255332Scy sum1 = partial; 904255332Scy sum1 = (sum1 & 0xffff) + (sum1 >> 16); 905255332Scy *sp = htons(sum1); 906255332Scy return; 907255332Scy } 90853642Sguido sum1 = (~ntohs(*sp)) & 0xffff; 90953642Sguido sum1 += (n); 91053642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 91153642Sguido /* Again */ 91253642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 91353642Sguido sumshort = ~(u_short)sum1; 91453642Sguido *(sp) = htons(sumshort); 91553642Sguido} 91653642Sguido 91753642Sguido 918145522Sdarrenr/* ------------------------------------------------------------------------ */ 919255332Scy/* Function: ipf_fix_incksum */ 920145522Sdarrenr/* Returns: Nil */ 921145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 922145522Sdarrenr/* sp(I) - location of 16bit checksum to update */ 923145522Sdarrenr/* n((I) - amount to adjust checksum by */ 924145522Sdarrenr/* */ 925145522Sdarrenr/* Adjusts the 16bit checksum by "n" for packets going in. */ 926145522Sdarrenr/* ------------------------------------------------------------------------ */ 927255332Scyvoid 928255332Scyipf_fix_incksum(cksum, sp, n, partial) 929255332Scy int cksum; 930255332Scy u_short *sp; 931255332Scy u_32_t n, partial; 93253642Sguido{ 933145522Sdarrenr u_short sumshort; 934145522Sdarrenr u_32_t sum1; 93553642Sguido 936145522Sdarrenr if (n == 0) 93753642Sguido return; 938145522Sdarrenr 939255332Scy if (cksum == 4) { 940255332Scy *sp = 0; 94155929Sguido return; 94255929Sguido } 943255332Scy if (cksum == 2) { 944255332Scy sum1 = partial; 945255332Scy sum1 = (sum1 & 0xffff) + (sum1 >> 16); 946255332Scy *sp = htons(sum1); 947255332Scy return; 948255332Scy } 949255332Scy 95053642Sguido sum1 = (~ntohs(*sp)) & 0xffff; 95153642Sguido sum1 += ~(n) & 0xffff; 95253642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 95353642Sguido /* Again */ 95453642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 95553642Sguido sumshort = ~(u_short)sum1; 95653642Sguido *(sp) = htons(sumshort); 95753642Sguido} 95853642Sguido 95953642Sguido 960145522Sdarrenr/* ------------------------------------------------------------------------ */ 961255332Scy/* Function: ipf_fix_datacksum */ 962145522Sdarrenr/* Returns: Nil */ 963145522Sdarrenr/* Parameters: sp(I) - location of 16bit checksum to update */ 964145522Sdarrenr/* n((I) - amount to adjust checksum by */ 965145522Sdarrenr/* */ 966145522Sdarrenr/* Fix_datacksum is used *only* for the adjustments of checksums in the */ 967145522Sdarrenr/* data section of an IP packet. */ 968145522Sdarrenr/* */ 969145522Sdarrenr/* The only situation in which you need to do this is when NAT'ing an */ 970145522Sdarrenr/* ICMP error message. Such a message, contains in its body the IP header */ 971145522Sdarrenr/* of the original IP packet, that causes the error. */ 972145522Sdarrenr/* */ 973145522Sdarrenr/* You can't use fix_incksum or fix_outcksum in that case, because for the */ 974145522Sdarrenr/* kernel the data section of the ICMP error is just data, and no special */ 975145522Sdarrenr/* processing like hardware cksum or ntohs processing have been done by the */ 976145522Sdarrenr/* kernel on the data section. */ 977145522Sdarrenr/* ------------------------------------------------------------------------ */ 978255332Scyvoid 979255332Scyipf_fix_datacksum(sp, n) 980255332Scy u_short *sp; 981255332Scy u_32_t n; 98267614Sdarrenr{ 983145522Sdarrenr u_short sumshort; 984145522Sdarrenr u_32_t sum1; 98567614Sdarrenr 986145522Sdarrenr if (n == 0) 98767614Sdarrenr return; 98867614Sdarrenr 98967614Sdarrenr sum1 = (~ntohs(*sp)) & 0xffff; 99067614Sdarrenr sum1 += (n); 99167614Sdarrenr sum1 = (sum1 >> 16) + (sum1 & 0xffff); 99267614Sdarrenr /* Again */ 99367614Sdarrenr sum1 = (sum1 >> 16) + (sum1 & 0xffff); 99467614Sdarrenr sumshort = ~(u_short)sum1; 99567614Sdarrenr *(sp) = htons(sumshort); 99667614Sdarrenr} 99767614Sdarrenr 99853642Sguido 999145522Sdarrenr/* ------------------------------------------------------------------------ */ 1000255332Scy/* Function: ipf_nat_ioctl */ 1001145522Sdarrenr/* Returns: int - 0 == success, != 0 == failure */ 1002255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1003255332Scy/* data(I) - pointer to ioctl data */ 1004255332Scy/* cmd(I) - ioctl command integer */ 1005255332Scy/* mode(I) - file mode bits used with open */ 1006255332Scy/* uid(I) - uid of calling process */ 1007255332Scy/* ctx(I) - pointer used as key for finding context */ 1008145522Sdarrenr/* */ 1009145522Sdarrenr/* Processes an ioctl call made to operate on the IP Filter NAT device. */ 1010145522Sdarrenr/* ------------------------------------------------------------------------ */ 1011255332Scyint 1012255332Scyipf_nat_ioctl(softc, data, cmd, mode, uid, ctx) 1013255332Scy ipf_main_softc_t *softc; 1014255332Scy ioctlcmd_t cmd; 1015255332Scy caddr_t data; 1016255332Scy int mode, uid; 1017255332Scy void *ctx; 101853642Sguido{ 1019255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 102095418Sdarrenr int error = 0, ret, arg, getlock; 1021255332Scy ipnat_t *nat, *nt, *n; 102253642Sguido ipnat_t natd; 1023170268Sdarrenr SPL_INT(s); 102453642Sguido 1025255332Scy#if BSD_GE_YEAR(199306) && defined(_KERNEL) 1026255332Scy# if NETBSD_GE_REV(399002000) 1027170268Sdarrenr if ((mode & FWRITE) && 1028170268Sdarrenr kauth_authorize_network(curlwp->l_cred, KAUTH_NETWORK_FIREWALL, 1029170268Sdarrenr KAUTH_REQ_NETWORK_FIREWALL_FW, 1030255332Scy NULL, NULL, NULL)) 1031170268Sdarrenr# else 1032192895Sjamie# if defined(__FreeBSD_version) && (__FreeBSD_version >= 500034) 1033255332Scy if (securelevel_ge(curthread->td_ucred, 3) && (mode & FWRITE)) 1034192895Sjamie# else 1035255332Scy if ((securelevel >= 3) && (mode & FWRITE)) 1036192895Sjamie# endif 1037255332Scy# endif 1038255332Scy { 1039255332Scy IPFERROR(60001); 1040170268Sdarrenr return EPERM; 1041170268Sdarrenr } 104253642Sguido#endif 104353642Sguido 1044145522Sdarrenr#if defined(__osf__) && defined(_KERNEL) 1045145522Sdarrenr getlock = 0; 1046145522Sdarrenr#else 1047145522Sdarrenr getlock = (mode & NAT_LOCKHELD) ? 0 : 1; 1048145522Sdarrenr#endif 1049145522Sdarrenr 1050255332Scy n = NULL; 1051255332Scy nt = NULL; 1052255332Scy nat = NULL; 1053145522Sdarrenr 1054255332Scy if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT) || 1055255332Scy (cmd == (ioctlcmd_t)SIOCPURGENAT)) { 105695418Sdarrenr if (mode & NAT_SYSSPACE) { 105795418Sdarrenr bcopy(data, (char *)&natd, sizeof(natd)); 1058255332Scy nat = &natd; 105995418Sdarrenr error = 0; 106095418Sdarrenr } else { 1061255332Scy bzero(&natd, sizeof(natd)); 1062255332Scy error = ipf_inobj(softc, data, NULL, &natd, 1063255332Scy IPFOBJ_IPNAT); 1064255332Scy if (error != 0) 1065255332Scy goto done; 1066255332Scy 1067255332Scy if (natd.in_size < sizeof(ipnat_t)) { 1068255332Scy error = EINVAL; 1069255332Scy goto done; 1070255332Scy } 1071255332Scy KMALLOCS(nt, ipnat_t *, natd.in_size); 1072255332Scy if (nt == NULL) { 1073255332Scy IPFERROR(60070); 1074255332Scy error = ENOMEM; 1075255332Scy goto done; 1076255332Scy } 1077255332Scy bzero(nt, natd.in_size); 1078255332Scy error = ipf_inobjsz(softc, data, nt, IPFOBJ_IPNAT, 1079255332Scy natd.in_size); 1080255332Scy if (error) 1081255332Scy goto done; 1082255332Scy nat = nt; 108395418Sdarrenr } 108453642Sguido 1085255332Scy /* 1086255332Scy * For add/delete, look to see if the NAT entry is 1087255332Scy * already present 1088255332Scy */ 108953642Sguido nat->in_flags &= IPN_USERFLAGS; 109053642Sguido if ((nat->in_redir & NAT_MAPBLK) == 0) { 1091255332Scy if (nat->in_osrcatype == FRI_NORMAL || 1092255332Scy nat->in_osrcatype == FRI_NONE) 1093255332Scy nat->in_osrcaddr &= nat->in_osrcmsk; 1094255332Scy if (nat->in_odstatype == FRI_NORMAL || 1095255332Scy nat->in_odstatype == FRI_NONE) 1096255332Scy nat->in_odstaddr &= nat->in_odstmsk; 1097255332Scy if ((nat->in_flags & (IPN_SPLIT|IPN_SIPRANGE)) == 0) { 1098255332Scy if (nat->in_nsrcatype == FRI_NORMAL) 1099255332Scy nat->in_nsrcaddr &= nat->in_nsrcmsk; 1100255332Scy if (nat->in_ndstatype == FRI_NORMAL) 1101255332Scy nat->in_ndstaddr &= nat->in_ndstmsk; 1102255332Scy } 110353642Sguido } 1104255332Scy 1105255332Scy error = ipf_nat_rule_init(softc, softn, nat); 1106255332Scy if (error != 0) 1107255332Scy goto done; 1108255332Scy 1109255332Scy MUTEX_ENTER(&softn->ipf_nat_io); 1110255332Scy for (n = softn->ipf_nat_list; n != NULL; n = n->in_next) 1111255332Scy if (ipf_nat_cmp_rules(nat, n) == 0) 111253642Sguido break; 111353642Sguido } 111453642Sguido 111553642Sguido switch (cmd) 111653642Sguido { 111755929Sguido#ifdef IPFILTER_LOG 111855929Sguido case SIOCIPFFB : 111960852Sdarrenr { 112060852Sdarrenr int tmp; 112160852Sdarrenr 1122255332Scy if (!(mode & FWRITE)) { 1123255332Scy IPFERROR(60002); 112455929Sguido error = EPERM; 1125255332Scy } else { 1126255332Scy tmp = ipf_log_clear(softc, IPL_LOGNAT); 1127255332Scy error = BCOPYOUT(&tmp, data, sizeof(tmp)); 1128255332Scy if (error != 0) { 1129255332Scy IPFERROR(60057); 1130170268Sdarrenr error = EFAULT; 1131255332Scy } 113260852Sdarrenr } 113355929Sguido break; 113460852Sdarrenr } 1135170268Sdarrenr 1136145522Sdarrenr case SIOCSETLG : 1137255332Scy if (!(mode & FWRITE)) { 1138255332Scy IPFERROR(60003); 1139145522Sdarrenr error = EPERM; 1140255332Scy } else { 1141255332Scy error = BCOPYIN(data, &softn->ipf_nat_logging, 1142255332Scy sizeof(softn->ipf_nat_logging)); 1143170268Sdarrenr if (error != 0) 1144170268Sdarrenr error = EFAULT; 1145145522Sdarrenr } 1146145522Sdarrenr break; 1147170268Sdarrenr 1148145522Sdarrenr case SIOCGETLG : 1149255332Scy error = BCOPYOUT(&softn->ipf_nat_logging, data, 1150255332Scy sizeof(softn->ipf_nat_logging)); 1151255332Scy if (error != 0) { 1152255332Scy IPFERROR(60004); 1153170268Sdarrenr error = EFAULT; 1154255332Scy } 1155145522Sdarrenr break; 1156170268Sdarrenr 1157145522Sdarrenr case FIONREAD : 1158255332Scy arg = ipf_log_bytesused(softc, IPL_LOGNAT); 1159170268Sdarrenr error = BCOPYOUT(&arg, data, sizeof(arg)); 1160255332Scy if (error != 0) { 1161255332Scy IPFERROR(60005); 1162170268Sdarrenr error = EFAULT; 1163255332Scy } 1164145522Sdarrenr break; 116555929Sguido#endif 116653642Sguido case SIOCADNAT : 116753642Sguido if (!(mode & FWRITE)) { 1168255332Scy IPFERROR(60006); 116953642Sguido error = EPERM; 1170145522Sdarrenr } else if (n != NULL) { 1171255332Scy natd.in_flineno = n->in_flineno; 1172255332Scy (void) ipf_outobj(softc, data, &natd, IPFOBJ_IPNAT); 1173255332Scy IPFERROR(60007); 117453642Sguido error = EEXIST; 1175145522Sdarrenr } else if (nt == NULL) { 1176255332Scy IPFERROR(60008); 1177145522Sdarrenr error = ENOMEM; 117853642Sguido } 1179145522Sdarrenr if (error != 0) { 1180255332Scy MUTEX_EXIT(&softn->ipf_nat_io); 118153642Sguido break; 118253642Sguido } 1183255332Scy if (nat != nt) 1184255332Scy bcopy((char *)nat, (char *)nt, sizeof(*n)); 1185255332Scy error = ipf_nat_siocaddnat(softc, softn, nt, getlock); 1186255332Scy MUTEX_EXIT(&softn->ipf_nat_io); 1187255332Scy if (error == 0) { 1188255332Scy nat = NULL; 1189145522Sdarrenr nt = NULL; 1190255332Scy } 119153642Sguido break; 1192170268Sdarrenr 119353642Sguido case SIOCRMNAT : 1194255332Scy case SIOCPURGENAT : 119553642Sguido if (!(mode & FWRITE)) { 1196255332Scy IPFERROR(60009); 119753642Sguido error = EPERM; 119853642Sguido n = NULL; 1199145522Sdarrenr } else if (n == NULL) { 1200255332Scy IPFERROR(60010); 1201145522Sdarrenr error = ESRCH; 120253642Sguido } 1203145522Sdarrenr 1204145522Sdarrenr if (error != 0) { 1205255332Scy MUTEX_EXIT(&softn->ipf_nat_io); 120653642Sguido break; 120753642Sguido } 1208255332Scy if (cmd == (ioctlcmd_t)SIOCPURGENAT) { 1209255332Scy error = ipf_outobjsz(softc, data, n, IPFOBJ_IPNAT, 1210255332Scy n->in_size); 1211255332Scy if (error) { 1212255332Scy MUTEX_EXIT(&softn->ipf_nat_io); 1213255332Scy goto done; 1214255332Scy } 1215255332Scy n->in_flags |= IPN_PURGE; 1216255332Scy } 1217255332Scy ipf_nat_siocdelnat(softc, softn, n, getlock); 1218145522Sdarrenr 1219255332Scy MUTEX_EXIT(&softn->ipf_nat_io); 122053642Sguido n = NULL; 122153642Sguido break; 1222170268Sdarrenr 122353642Sguido case SIOCGNATS : 1224255332Scy { 1225255332Scy natstat_t *nsp = &softn->ipf_nat_stats; 1226255332Scy 1227255332Scy nsp->ns_side[0].ns_table = softn->ipf_nat_table[0]; 1228255332Scy nsp->ns_side[1].ns_table = softn->ipf_nat_table[1]; 1229255332Scy nsp->ns_list = softn->ipf_nat_list; 1230255332Scy nsp->ns_maptable = softn->ipf_hm_maptable; 1231255332Scy nsp->ns_maplist = softn->ipf_hm_maplist; 1232255332Scy nsp->ns_nattab_sz = softn->ipf_nat_table_sz; 1233255332Scy nsp->ns_nattab_max = softn->ipf_nat_table_max; 1234255332Scy nsp->ns_rultab_sz = softn->ipf_nat_maprules_sz; 1235255332Scy nsp->ns_rdrtab_sz = softn->ipf_nat_rdrrules_sz; 1236255332Scy nsp->ns_hostmap_sz = softn->ipf_nat_hostmap_sz; 1237255332Scy nsp->ns_instances = softn->ipf_nat_instances; 1238255332Scy nsp->ns_ticks = softc->ipf_ticks; 1239255332Scy#ifdef IPFILTER_LOGGING 1240255332Scy nsp->ns_log_ok = ipf_log_logok(softc, IPF_LOGNAT); 1241255332Scy nsp->ns_log_fail = ipf_log_failures(softc, IPF_LOGNAT); 1242255332Scy#else 1243255332Scy nsp->ns_log_ok = 0; 1244255332Scy nsp->ns_log_fail = 0; 1245255332Scy#endif 1246255332Scy error = ipf_outobj(softc, data, nsp, IPFOBJ_NATSTAT); 124753642Sguido break; 1248255332Scy } 1249170268Sdarrenr 125053642Sguido case SIOCGNATL : 125153642Sguido { 125253642Sguido natlookup_t nl; 125353642Sguido 1254255332Scy error = ipf_inobj(softc, data, NULL, &nl, IPFOBJ_NATLOOKUP); 1255145522Sdarrenr if (error == 0) { 1256173181Sdarrenr void *ptr; 1257173181Sdarrenr 1258173181Sdarrenr if (getlock) { 1259255332Scy READ_ENTER(&softc->ipf_nat); 1260173181Sdarrenr } 1261255332Scy 1262255332Scy switch (nl.nl_v) 1263255332Scy { 1264255332Scy case 4 : 1265255332Scy ptr = ipf_nat_lookupredir(&nl); 1266255332Scy break; 1267255332Scy#ifdef USE_INET6 1268255332Scy case 6 : 1269255332Scy ptr = ipf_nat6_lookupredir(&nl); 1270255332Scy break; 1271255332Scy#endif 1272255332Scy default: 1273255332Scy ptr = NULL; 1274255332Scy break; 1275255332Scy } 1276255332Scy 1277173181Sdarrenr if (getlock) { 1278255332Scy RWLOCK_EXIT(&softc->ipf_nat); 1279173181Sdarrenr } 1280173181Sdarrenr if (ptr != NULL) { 1281255332Scy error = ipf_outobj(softc, data, &nl, 1282255332Scy IPFOBJ_NATLOOKUP); 1283145522Sdarrenr } else { 1284255332Scy IPFERROR(60011); 1285145522Sdarrenr error = ESRCH; 1286145522Sdarrenr } 1287145522Sdarrenr } 128853642Sguido break; 128953642Sguido } 1290170268Sdarrenr 129160852Sdarrenr case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */ 129253642Sguido if (!(mode & FWRITE)) { 1293255332Scy IPFERROR(60012); 129453642Sguido error = EPERM; 129553642Sguido break; 129653642Sguido } 1297145522Sdarrenr if (getlock) { 1298255332Scy WRITE_ENTER(&softc->ipf_nat); 1299145522Sdarrenr } 1300170268Sdarrenr 1301170268Sdarrenr error = BCOPYIN(data, &arg, sizeof(arg)); 1302255332Scy if (error != 0) { 1303255332Scy IPFERROR(60013); 1304170268Sdarrenr error = EFAULT; 1305255332Scy } else { 1306170268Sdarrenr if (arg == 0) 1307255332Scy ret = ipf_nat_flushtable(softc, softn); 1308170268Sdarrenr else if (arg == 1) 1309255332Scy ret = ipf_nat_clearlist(softc, softn); 1310170268Sdarrenr else 1311255332Scy ret = ipf_nat_extraflush(softc, softn, arg); 1312255332Scy ipf_proxy_flush(softc->ipf_proxy_soft, arg); 1313170268Sdarrenr } 1314170268Sdarrenr 1315145522Sdarrenr if (getlock) { 1316255332Scy RWLOCK_EXIT(&softc->ipf_nat); 131760852Sdarrenr } 1318145522Sdarrenr if (error == 0) { 1319170268Sdarrenr error = BCOPYOUT(&ret, data, sizeof(ret)); 1320145522Sdarrenr } 132153642Sguido break; 1322170268Sdarrenr 1323255332Scy case SIOCMATCHFLUSH : 1324255332Scy if (!(mode & FWRITE)) { 1325255332Scy IPFERROR(60014); 1326255332Scy error = EPERM; 1327255332Scy break; 1328255332Scy } 1329255332Scy if (getlock) { 1330255332Scy WRITE_ENTER(&softc->ipf_nat); 1331255332Scy } 1332255332Scy 1333255332Scy error = ipf_nat_matchflush(softc, softn, data); 1334255332Scy 1335255332Scy if (getlock) { 1336255332Scy RWLOCK_EXIT(&softc->ipf_nat); 1337255332Scy } 1338255332Scy break; 1339255332Scy 1340145522Sdarrenr case SIOCPROXY : 1341255332Scy error = ipf_proxy_ioctl(softc, data, cmd, mode, ctx); 1342145522Sdarrenr break; 1343170268Sdarrenr 134460852Sdarrenr case SIOCSTLCK : 1345153876Sguido if (!(mode & FWRITE)) { 1346255332Scy IPFERROR(60015); 1347153876Sguido error = EPERM; 1348153876Sguido } else { 1349255332Scy error = ipf_lock(data, &softn->ipf_nat_lock); 1350153876Sguido } 135153642Sguido break; 1352170268Sdarrenr 135360852Sdarrenr case SIOCSTPUT : 1354153876Sguido if ((mode & FWRITE) != 0) { 1355255332Scy error = ipf_nat_putent(softc, data, getlock); 1356145522Sdarrenr } else { 1357255332Scy IPFERROR(60016); 135860852Sdarrenr error = EACCES; 1359145522Sdarrenr } 136060852Sdarrenr break; 1361170268Sdarrenr 136260852Sdarrenr case SIOCSTGSZ : 1363255332Scy if (softn->ipf_nat_lock) { 1364255332Scy error = ipf_nat_getsz(softc, data, getlock); 1365255332Scy } else { 1366255332Scy IPFERROR(60017); 136760852Sdarrenr error = EACCES; 1368255332Scy } 136960852Sdarrenr break; 1370170268Sdarrenr 137160852Sdarrenr case SIOCSTGET : 1372255332Scy if (softn->ipf_nat_lock) { 1373255332Scy error = ipf_nat_getent(softc, data, getlock); 1374255332Scy } else { 1375255332Scy IPFERROR(60018); 137660852Sdarrenr error = EACCES; 1377255332Scy } 137860852Sdarrenr break; 1379170268Sdarrenr 1380170268Sdarrenr case SIOCGENITER : 1381170268Sdarrenr { 1382170268Sdarrenr ipfgeniter_t iter; 1383170268Sdarrenr ipftoken_t *token; 1384255332Scy ipfobj_t obj; 1385170268Sdarrenr 1386255332Scy error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER); 1387255332Scy if (error != 0) 1388255332Scy break; 1389255332Scy 1390170268Sdarrenr SPL_SCHED(s); 1391255332Scy token = ipf_token_find(softc, iter.igi_type, uid, ctx); 1392255332Scy if (token != NULL) { 1393255332Scy error = ipf_nat_iterator(softc, token, &iter, &obj); 1394255332Scy WRITE_ENTER(&softc->ipf_tokens); 1395255332Scy ipf_token_deref(softc, token); 1396255332Scy RWLOCK_EXIT(&softc->ipf_tokens); 1397170268Sdarrenr } 1398170268Sdarrenr SPL_X(s); 1399170268Sdarrenr break; 1400170268Sdarrenr } 1401170268Sdarrenr 1402170268Sdarrenr case SIOCIPFDELTOK : 1403255332Scy error = BCOPYIN(data, &arg, sizeof(arg)); 1404170268Sdarrenr if (error == 0) { 1405170268Sdarrenr SPL_SCHED(s); 1406255332Scy error = ipf_token_del(softc, arg, uid, ctx); 1407170268Sdarrenr SPL_X(s); 1408170268Sdarrenr } else { 1409255332Scy IPFERROR(60019); 1410170268Sdarrenr error = EFAULT; 1411170268Sdarrenr } 1412170268Sdarrenr break; 1413170268Sdarrenr 1414170268Sdarrenr case SIOCGTQTAB : 1415255332Scy error = ipf_outobj(softc, data, softn->ipf_nat_tcptq, 1416255332Scy IPFOBJ_STATETQTAB); 1417170268Sdarrenr break; 1418170268Sdarrenr 1419172776Sdarrenr case SIOCGTABL : 1420255332Scy error = ipf_nat_gettable(softc, softn, data); 1421172776Sdarrenr break; 1422172776Sdarrenr 142353642Sguido default : 1424255332Scy IPFERROR(60020); 142553642Sguido error = EINVAL; 142653642Sguido break; 142753642Sguido } 142860852Sdarrenrdone: 1429255332Scy if (nat != NULL) 1430255332Scy ipf_nat_rule_fini(softc, nat); 1431170268Sdarrenr if (nt != NULL) 1432255332Scy KFREES(nt, nt->in_size); 143353642Sguido return error; 143453642Sguido} 143553642Sguido 143653642Sguido 1437145522Sdarrenr/* ------------------------------------------------------------------------ */ 1438255332Scy/* Function: ipf_nat_siocaddnat */ 1439145522Sdarrenr/* Returns: int - 0 == success, != 0 == failure */ 1440255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1441255332Scy/* softn(I) - pointer to NAT context structure */ 1442255332Scy/* n(I) - pointer to new NAT rule */ 1443145522Sdarrenr/* np(I) - pointer to where to insert new NAT rule */ 1444255332Scy/* getlock(I) - flag indicating if lock on is held */ 1445255332Scy/* Mutex Locks: ipf_nat_io */ 1446145522Sdarrenr/* */ 1447145522Sdarrenr/* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 1448145522Sdarrenr/* from information passed to the kernel, then add it to the appropriate */ 1449145522Sdarrenr/* NAT rule table(s). */ 1450145522Sdarrenr/* ------------------------------------------------------------------------ */ 1451255332Scystatic int 1452255332Scyipf_nat_siocaddnat(softc, softn, n, getlock) 1453255332Scy ipf_main_softc_t *softc; 1454255332Scy ipf_nat_softc_t *softn; 1455255332Scy ipnat_t *n; 1456255332Scy int getlock; 1457145522Sdarrenr{ 1458255332Scy int error = 0; 1459145522Sdarrenr 1460255332Scy if (ipf_nat_resolverule(softc, n) != 0) { 1461255332Scy IPFERROR(60022); 1462161356Sguido return ENOENT; 1463255332Scy } 1464145522Sdarrenr 1465255332Scy if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) { 1466255332Scy IPFERROR(60023); 1467145522Sdarrenr return EINVAL; 1468255332Scy } 1469145522Sdarrenr 1470255332Scy if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) { 1471145522Sdarrenr /* 1472255332Scy * Prerecord whether or not the destination of the divert 1473255332Scy * is local or not to the interface the packet is going 1474255332Scy * to be sent out. 1475145522Sdarrenr */ 1476255332Scy n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1], 1477255332Scy n->in_ifps[1], &n->in_ndstip6); 1478145522Sdarrenr } 1479145522Sdarrenr 1480145522Sdarrenr if (getlock) { 1481255332Scy WRITE_ENTER(&softc->ipf_nat); 1482145522Sdarrenr } 1483145522Sdarrenr n->in_next = NULL; 1484255332Scy n->in_pnext = softn->ipf_nat_list_tail; 1485255332Scy *n->in_pnext = n; 1486255332Scy softn->ipf_nat_list_tail = &n->in_next; 1487255332Scy n->in_use++; 1488145522Sdarrenr 1489145522Sdarrenr if (n->in_redir & NAT_REDIRECT) { 1490145522Sdarrenr n->in_flags &= ~IPN_NOTDST; 1491255332Scy switch (n->in_v[0]) 1492255332Scy { 1493255332Scy case 4 : 1494255332Scy ipf_nat_addrdr(softn, n); 1495255332Scy break; 1496255332Scy#ifdef USE_INET6 1497255332Scy case 6 : 1498255332Scy ipf_nat6_addrdr(softn, n); 1499255332Scy break; 1500255332Scy#endif 1501255332Scy default : 1502255332Scy break; 1503255332Scy } 1504255332Scy ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_rdr); 1505145522Sdarrenr } 1506255332Scy 1507145522Sdarrenr if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { 1508145522Sdarrenr n->in_flags &= ~IPN_NOTSRC; 1509255332Scy switch (n->in_v[0]) 1510255332Scy { 1511255332Scy case 4 : 1512255332Scy ipf_nat_addmap(softn, n); 1513255332Scy break; 1514255332Scy#ifdef USE_INET6 1515255332Scy case 6 : 1516255332Scy ipf_nat6_addmap(softn, n); 1517255332Scy break; 1518255332Scy#endif 1519255332Scy default : 1520255332Scy break; 1521255332Scy } 1522255332Scy ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_map); 1523145522Sdarrenr } 1524255332Scy 1525255332Scy if (n->in_age[0] != 0) 1526255332Scy n->in_tqehead[0] = ipf_addtimeoutqueue(softc, 1527255332Scy &softn->ipf_nat_utqe, 1528255332Scy n->in_age[0]); 1529255332Scy 1530255332Scy if (n->in_age[1] != 0) 1531255332Scy n->in_tqehead[1] = ipf_addtimeoutqueue(softc, 1532255332Scy &softn->ipf_nat_utqe, 1533255332Scy n->in_age[1]); 1534255332Scy 1535170268Sdarrenr MUTEX_INIT(&n->in_lock, "ipnat rule lock"); 1536170268Sdarrenr 1537145522Sdarrenr n = NULL; 1538255332Scy ATOMIC_INC32(softn->ipf_nat_stats.ns_rules); 1539255332Scy#if SOLARIS && !defined(INSTANCES) 1540145522Sdarrenr pfil_delayed_copy = 0; 1541145522Sdarrenr#endif 1542145522Sdarrenr if (getlock) { 1543255332Scy RWLOCK_EXIT(&softc->ipf_nat); /* WRITE */ 1544145522Sdarrenr } 1545145522Sdarrenr 1546145522Sdarrenr return error; 1547145522Sdarrenr} 1548145522Sdarrenr 1549145522Sdarrenr 1550145522Sdarrenr/* ------------------------------------------------------------------------ */ 1551255332Scy/* Function: ipf_nat_ruleaddrinit */ 1552255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1553255332Scy/* softn(I) - pointer to NAT context structure */ 1554255332Scy/* n(I) - pointer to NAT rule */ 1555255332Scy/* */ 1556255332Scy/* Initialise all of the NAT address structures in a NAT rule. */ 1557255332Scy/* ------------------------------------------------------------------------ */ 1558255332Scystatic int 1559255332Scyipf_nat_ruleaddrinit(softc, softn, n) 1560255332Scy ipf_main_softc_t *softc; 1561255332Scy ipf_nat_softc_t *softn; 1562255332Scy ipnat_t *n; 1563255332Scy{ 1564255332Scy int idx, error; 1565255332Scy 1566255332Scy if ((n->in_ndst.na_atype == FRI_LOOKUP) && 1567255332Scy (n->in_ndst.na_type != IPLT_DSTLIST)) { 1568255332Scy IPFERROR(60071); 1569255332Scy return EINVAL; 1570255332Scy } 1571255332Scy if ((n->in_nsrc.na_atype == FRI_LOOKUP) && 1572255332Scy (n->in_nsrc.na_type != IPLT_DSTLIST)) { 1573255332Scy IPFERROR(60069); 1574255332Scy return EINVAL; 1575255332Scy } 1576255332Scy 1577255332Scy if (n->in_redir == NAT_BIMAP) { 1578255332Scy n->in_ndstaddr = n->in_osrcaddr; 1579255332Scy n->in_ndstmsk = n->in_osrcmsk; 1580255332Scy n->in_odstaddr = n->in_nsrcaddr; 1581255332Scy n->in_odstmsk = n->in_nsrcmsk; 1582255332Scy 1583255332Scy } 1584255332Scy 1585255332Scy if (n->in_redir & NAT_REDIRECT) 1586255332Scy idx = 1; 1587255332Scy else 1588255332Scy idx = 0; 1589255332Scy /* 1590255332Scy * Initialise all of the address fields. 1591255332Scy */ 1592255332Scy error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, 1, 1593255332Scy n->in_ifps[idx]); 1594255332Scy if (error != 0) 1595255332Scy return error; 1596255332Scy 1597255332Scy error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, 1, 1598255332Scy n->in_ifps[idx]); 1599255332Scy if (error != 0) 1600255332Scy return error; 1601255332Scy 1602255332Scy error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, 1, 1603255332Scy n->in_ifps[idx]); 1604255332Scy if (error != 0) 1605255332Scy return error; 1606255332Scy 1607255332Scy error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, 1, 1608255332Scy n->in_ifps[idx]); 1609255332Scy if (error != 0) 1610255332Scy return error; 1611255332Scy 1612255332Scy if (n->in_redir & NAT_DIVERTUDP) 1613255332Scy ipf_nat_builddivertmp(softn, n); 1614255332Scy 1615255332Scy return 0; 1616255332Scy} 1617255332Scy 1618255332Scy 1619255332Scy/* ------------------------------------------------------------------------ */ 1620255332Scy/* Function: ipf_nat_resolvrule */ 1621145522Sdarrenr/* Returns: Nil */ 1622255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1623255332Scy/* n(I) - pointer to NAT rule */ 1624145522Sdarrenr/* */ 1625145522Sdarrenr/* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 1626145522Sdarrenr/* from information passed to the kernel, then add it to the appropriate */ 1627145522Sdarrenr/* NAT rule table(s). */ 1628145522Sdarrenr/* ------------------------------------------------------------------------ */ 1629255332Scystatic int 1630255332Scyipf_nat_resolverule(softc, n) 1631255332Scy ipf_main_softc_t *softc; 1632255332Scy ipnat_t *n; 1633145522Sdarrenr{ 1634255332Scy char *base; 1635145522Sdarrenr 1636255332Scy base = n->in_names; 1637255332Scy 1638255332Scy n->in_ifps[0] = ipf_resolvenic(softc, base + n->in_ifnames[0], 1639255332Scy n->in_v[0]); 1640255332Scy 1641255332Scy if (n->in_ifnames[1] == -1) { 1642255332Scy n->in_ifnames[1] = n->in_ifnames[0]; 1643145522Sdarrenr n->in_ifps[1] = n->in_ifps[0]; 1644145522Sdarrenr } else { 1645255332Scy n->in_ifps[1] = ipf_resolvenic(softc, base + n->in_ifnames[1], 1646255332Scy n->in_v[1]); 1647145522Sdarrenr } 1648145522Sdarrenr 1649255332Scy if (n->in_plabel != -1) { 1650255332Scy if (n->in_redir & NAT_REDIRECT) 1651255332Scy n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft, 1652255332Scy n->in_pr[0], 1653255332Scy base + n->in_plabel); 1654255332Scy else 1655255332Scy n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft, 1656255332Scy n->in_pr[1], 1657255332Scy base + n->in_plabel); 1658161356Sguido if (n->in_apr == NULL) 1659161356Sguido return -1; 1660145522Sdarrenr } 1661161356Sguido return 0; 1662145522Sdarrenr} 1663145522Sdarrenr 1664145522Sdarrenr 1665145522Sdarrenr/* ------------------------------------------------------------------------ */ 1666255332Scy/* Function: ipf_nat_siocdelnat */ 1667145522Sdarrenr/* Returns: int - 0 == success, != 0 == failure */ 1668255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1669255332Scy/* softn(I) - pointer to NAT context structure */ 1670255332Scy/* n(I) - pointer to new NAT rule */ 1671255332Scy/* getlock(I) - flag indicating if lock on is held */ 1672255332Scy/* Mutex Locks: ipf_nat_io */ 1673145522Sdarrenr/* */ 1674145522Sdarrenr/* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 1675145522Sdarrenr/* from information passed to the kernel, then add it to the appropriate */ 1676145522Sdarrenr/* NAT rule table(s). */ 1677145522Sdarrenr/* ------------------------------------------------------------------------ */ 1678255332Scystatic void 1679255332Scyipf_nat_siocdelnat(softc, softn, n, getlock) 1680255332Scy ipf_main_softc_t *softc; 1681255332Scy ipf_nat_softc_t *softn; 1682255332Scy ipnat_t *n; 1683255332Scy int getlock; 1684145522Sdarrenr{ 1685255332Scy#ifdef IPF_NAT6 1686255332Scy int i; 1687255332Scy#endif 1688255332Scy 1689145522Sdarrenr if (getlock) { 1690255332Scy WRITE_ENTER(&softc->ipf_nat); 1691145522Sdarrenr } 1692145522Sdarrenr 1693255332Scy ipf_nat_delrule(softc, softn, n, 1); 1694145522Sdarrenr 1695145522Sdarrenr if (getlock) { 1696255332Scy RWLOCK_EXIT(&softc->ipf_nat); /* READ/WRITE */ 1697145522Sdarrenr } 1698145522Sdarrenr} 1699145522Sdarrenr 1700145522Sdarrenr 1701145522Sdarrenr/* ------------------------------------------------------------------------ */ 1702255332Scy/* Function: ipf_nat_getsz */ 1703145522Sdarrenr/* Returns: int - 0 == success, != 0 is the error value. */ 1704255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1705255332Scy/* data(I) - pointer to natget structure with kernel */ 1706255332Scy/* pointer get the size of. */ 1707255332Scy/* getlock(I) - flag indicating whether or not the caller */ 1708255332Scy/* holds a lock on ipf_nat */ 1709145522Sdarrenr/* */ 1710145522Sdarrenr/* Handle SIOCSTGSZ. */ 1711145522Sdarrenr/* Return the size of the nat list entry to be copied back to user space. */ 1712145522Sdarrenr/* The size of the entry is stored in the ng_sz field and the enture natget */ 1713145522Sdarrenr/* structure is copied back to the user. */ 1714145522Sdarrenr/* ------------------------------------------------------------------------ */ 1715255332Scystatic int 1716255332Scyipf_nat_getsz(softc, data, getlock) 1717255332Scy ipf_main_softc_t *softc; 1718255332Scy caddr_t data; 1719255332Scy int getlock; 172060852Sdarrenr{ 1721255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 172260852Sdarrenr ap_session_t *aps; 172360852Sdarrenr nat_t *nat, *n; 172460852Sdarrenr natget_t ng; 1725255332Scy int error; 172660852Sdarrenr 1727255332Scy error = BCOPYIN(data, &ng, sizeof(ng)); 1728255332Scy if (error != 0) { 1729255332Scy IPFERROR(60024); 1730170268Sdarrenr return EFAULT; 1731255332Scy } 173260852Sdarrenr 1733173181Sdarrenr if (getlock) { 1734255332Scy READ_ENTER(&softc->ipf_nat); 1735173181Sdarrenr } 1736173181Sdarrenr 173760852Sdarrenr nat = ng.ng_ptr; 173860852Sdarrenr if (!nat) { 1739255332Scy nat = softn->ipf_nat_instances; 174060852Sdarrenr ng.ng_sz = 0; 1741145522Sdarrenr /* 1742145522Sdarrenr * Empty list so the size returned is 0. Simple. 1743145522Sdarrenr */ 174460852Sdarrenr if (nat == NULL) { 1745173181Sdarrenr if (getlock) { 1746255332Scy RWLOCK_EXIT(&softc->ipf_nat); 1747173181Sdarrenr } 1748255332Scy error = BCOPYOUT(&ng, data, sizeof(ng)); 1749255332Scy if (error != 0) { 1750255332Scy IPFERROR(60025); 1751170268Sdarrenr return EFAULT; 1752255332Scy } 1753145522Sdarrenr return 0; 175460852Sdarrenr } 175560852Sdarrenr } else { 175660852Sdarrenr /* 175760852Sdarrenr * Make sure the pointer we're copying from exists in the 175860852Sdarrenr * current list of entries. Security precaution to prevent 175960852Sdarrenr * copying of random kernel data. 176060852Sdarrenr */ 1761255332Scy for (n = softn->ipf_nat_instances; n; n = n->nat_next) 176260852Sdarrenr if (n == nat) 176360852Sdarrenr break; 1764173181Sdarrenr if (n == NULL) { 1765173181Sdarrenr if (getlock) { 1766255332Scy RWLOCK_EXIT(&softc->ipf_nat); 1767173181Sdarrenr } 1768255332Scy IPFERROR(60026); 176960852Sdarrenr return ESRCH; 1770173181Sdarrenr } 177160852Sdarrenr } 177260852Sdarrenr 1773145522Sdarrenr /* 1774145522Sdarrenr * Incluse any space required for proxy data structures. 1775145522Sdarrenr */ 177660852Sdarrenr ng.ng_sz = sizeof(nat_save_t); 177760852Sdarrenr aps = nat->nat_aps; 1778145522Sdarrenr if (aps != NULL) { 1779145522Sdarrenr ng.ng_sz += sizeof(ap_session_t) - 4; 1780145522Sdarrenr if (aps->aps_data != 0) 1781145522Sdarrenr ng.ng_sz += aps->aps_psiz; 178260852Sdarrenr } 1783173181Sdarrenr if (getlock) { 1784255332Scy RWLOCK_EXIT(&softc->ipf_nat); 1785173181Sdarrenr } 178660852Sdarrenr 1787255332Scy error = BCOPYOUT(&ng, data, sizeof(ng)); 1788255332Scy if (error != 0) { 1789255332Scy IPFERROR(60027); 1790170268Sdarrenr return EFAULT; 1791255332Scy } 1792145522Sdarrenr return 0; 179360852Sdarrenr} 179460852Sdarrenr 179560852Sdarrenr 1796145522Sdarrenr/* ------------------------------------------------------------------------ */ 1797255332Scy/* Function: ipf_nat_getent */ 1798145522Sdarrenr/* Returns: int - 0 == success, != 0 is the error value. */ 1799255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1800255332Scy/* data(I) - pointer to natget structure with kernel pointer*/ 1801255332Scy/* to NAT structure to copy out. */ 1802255332Scy/* getlock(I) - flag indicating whether or not the caller */ 1803255332Scy/* holds a lock on ipf_nat */ 1804145522Sdarrenr/* */ 1805145522Sdarrenr/* Handle SIOCSTGET. */ 1806145522Sdarrenr/* Copies out NAT entry to user space. Any additional data held for a */ 1807145522Sdarrenr/* proxy is also copied, as to is the NAT rule which was responsible for it */ 1808145522Sdarrenr/* ------------------------------------------------------------------------ */ 1809255332Scystatic int 1810255332Scyipf_nat_getent(softc, data, getlock) 1811255332Scy ipf_main_softc_t *softc; 1812255332Scy caddr_t data; 1813255332Scy int getlock; 181460852Sdarrenr{ 1815255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 1816145522Sdarrenr int error, outsize; 181760852Sdarrenr ap_session_t *aps; 1818145522Sdarrenr nat_save_t *ipn, ipns; 1819145522Sdarrenr nat_t *n, *nat; 182060852Sdarrenr 1821255332Scy error = ipf_inobj(softc, data, NULL, &ipns, IPFOBJ_NATSAVE); 1822145522Sdarrenr if (error != 0) 1823145522Sdarrenr return error; 182460852Sdarrenr 1825255332Scy if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) { 1826255332Scy IPFERROR(60028); 1827145522Sdarrenr return EINVAL; 1828255332Scy } 1829145522Sdarrenr 1830145522Sdarrenr KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize); 1831255332Scy if (ipn == NULL) { 1832255332Scy IPFERROR(60029); 1833145522Sdarrenr return ENOMEM; 1834255332Scy } 1835145522Sdarrenr 1836173181Sdarrenr if (getlock) { 1837255332Scy READ_ENTER(&softc->ipf_nat); 1838173181Sdarrenr } 1839173181Sdarrenr 1840145522Sdarrenr ipn->ipn_dsize = ipns.ipn_dsize; 1841145522Sdarrenr nat = ipns.ipn_next; 1842145522Sdarrenr if (nat == NULL) { 1843255332Scy nat = softn->ipf_nat_instances; 184460852Sdarrenr if (nat == NULL) { 1845255332Scy if (softn->ipf_nat_instances == NULL) { 1846255332Scy IPFERROR(60030); 1847145522Sdarrenr error = ENOENT; 1848255332Scy } 1849145522Sdarrenr goto finished; 185060852Sdarrenr } 185160852Sdarrenr } else { 185260852Sdarrenr /* 185360852Sdarrenr * Make sure the pointer we're copying from exists in the 185460852Sdarrenr * current list of entries. Security precaution to prevent 185560852Sdarrenr * copying of random kernel data. 185660852Sdarrenr */ 1857255332Scy for (n = softn->ipf_nat_instances; n; n = n->nat_next) 185860852Sdarrenr if (n == nat) 185960852Sdarrenr break; 1860145522Sdarrenr if (n == NULL) { 1861255332Scy IPFERROR(60031); 1862145522Sdarrenr error = ESRCH; 1863145522Sdarrenr goto finished; 1864145522Sdarrenr } 186560852Sdarrenr } 1866145522Sdarrenr ipn->ipn_next = nat->nat_next; 186760852Sdarrenr 1868145522Sdarrenr /* 1869145522Sdarrenr * Copy the NAT structure. 1870145522Sdarrenr */ 1871145522Sdarrenr bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat)); 187260852Sdarrenr 1873145522Sdarrenr /* 1874145522Sdarrenr * If we have a pointer to the NAT rule it belongs to, save that too. 1875145522Sdarrenr */ 1876145522Sdarrenr if (nat->nat_ptr != NULL) 1877145522Sdarrenr bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat, 1878255332Scy ipn->ipn_ipnat.in_size); 187960852Sdarrenr 1880145522Sdarrenr /* 1881145522Sdarrenr * If we also know the NAT entry has an associated filter rule, 1882145522Sdarrenr * save that too. 1883145522Sdarrenr */ 1884145522Sdarrenr if (nat->nat_fr != NULL) 1885145522Sdarrenr bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr, 1886145522Sdarrenr sizeof(ipn->ipn_fr)); 188760852Sdarrenr 1888145522Sdarrenr /* 1889145522Sdarrenr * Last but not least, if there is an application proxy session set 1890145522Sdarrenr * up for this NAT entry, then copy that out too, including any 1891145522Sdarrenr * private data saved along side it by the proxy. 1892145522Sdarrenr */ 1893145522Sdarrenr aps = nat->nat_aps; 1894145522Sdarrenr outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data); 1895145522Sdarrenr if (aps != NULL) { 1896145522Sdarrenr char *s; 189760852Sdarrenr 1898145522Sdarrenr if (outsize < sizeof(*aps)) { 1899255332Scy IPFERROR(60032); 1900145522Sdarrenr error = ENOBUFS; 1901145522Sdarrenr goto finished; 190260852Sdarrenr } 1903145522Sdarrenr 1904145522Sdarrenr s = ipn->ipn_data; 1905145522Sdarrenr bcopy((char *)aps, s, sizeof(*aps)); 1906145522Sdarrenr s += sizeof(*aps); 1907145522Sdarrenr outsize -= sizeof(*aps); 1908145522Sdarrenr if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz)) 1909145522Sdarrenr bcopy(aps->aps_data, s, aps->aps_psiz); 1910255332Scy else { 1911255332Scy IPFERROR(60033); 1912145522Sdarrenr error = ENOBUFS; 1913255332Scy } 191460852Sdarrenr } 1915145522Sdarrenr if (error == 0) { 1916173181Sdarrenr if (getlock) { 1917255332Scy READ_ENTER(&softc->ipf_nat); 1918173181Sdarrenr getlock = 0; 1919173181Sdarrenr } 1920255332Scy error = ipf_outobjsz(softc, data, ipn, IPFOBJ_NATSAVE, 1921255332Scy ipns.ipn_dsize); 1922145522Sdarrenr } 1923145522Sdarrenr 1924145522Sdarrenrfinished: 1925173181Sdarrenr if (getlock) { 1926255332Scy READ_ENTER(&softc->ipf_nat); 1927173181Sdarrenr } 1928145522Sdarrenr if (ipn != NULL) { 1929145522Sdarrenr KFREES(ipn, ipns.ipn_dsize); 1930145522Sdarrenr } 193164580Sdarrenr return error; 193260852Sdarrenr} 193360852Sdarrenr 193460852Sdarrenr 1935145522Sdarrenr/* ------------------------------------------------------------------------ */ 1936255332Scy/* Function: ipf_nat_putent */ 1937145522Sdarrenr/* Returns: int - 0 == success, != 0 is the error value. */ 1938255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1939255332Scy/* data(I) - pointer to natget structure with NAT */ 1940255332Scy/* structure information to load into the kernel */ 1941145522Sdarrenr/* getlock(I) - flag indicating whether or not a write lock */ 1942255332Scy/* on is already held. */ 1943145522Sdarrenr/* */ 1944145522Sdarrenr/* Handle SIOCSTPUT. */ 1945145522Sdarrenr/* Loads a NAT table entry from user space, including a NAT rule, proxy and */ 1946145522Sdarrenr/* firewall rule data structures, if pointers to them indicate so. */ 1947145522Sdarrenr/* ------------------------------------------------------------------------ */ 1948255332Scystatic int 1949255332Scyipf_nat_putent(softc, data, getlock) 1950255332Scy ipf_main_softc_t *softc; 1951255332Scy caddr_t data; 1952255332Scy int getlock; 195360852Sdarrenr{ 1954255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 1955145522Sdarrenr nat_save_t ipn, *ipnn; 195660852Sdarrenr ap_session_t *aps; 1957145522Sdarrenr nat_t *n, *nat; 195860852Sdarrenr frentry_t *fr; 1959145522Sdarrenr fr_info_t fin; 196060852Sdarrenr ipnat_t *in; 196160852Sdarrenr int error; 196260852Sdarrenr 1963255332Scy error = ipf_inobj(softc, data, NULL, &ipn, IPFOBJ_NATSAVE); 1964145522Sdarrenr if (error != 0) 1965145522Sdarrenr return error; 1966145522Sdarrenr 1967145522Sdarrenr /* 1968145522Sdarrenr * Initialise early because of code at junkput label. 1969145522Sdarrenr */ 1970255332Scy n = NULL; 1971145522Sdarrenr in = NULL; 1972145522Sdarrenr aps = NULL; 197364580Sdarrenr nat = NULL; 1974145522Sdarrenr ipnn = NULL; 1975170268Sdarrenr fr = NULL; 1976145522Sdarrenr 1977145522Sdarrenr /* 1978145522Sdarrenr * New entry, copy in the rest of the NAT entry if it's size is more 1979145522Sdarrenr * than just the nat_t structure. 1980145522Sdarrenr */ 1981145522Sdarrenr if (ipn.ipn_dsize > sizeof(ipn)) { 1982145522Sdarrenr if (ipn.ipn_dsize > 81920) { 1983255332Scy IPFERROR(60034); 1984145522Sdarrenr error = ENOMEM; 1985145522Sdarrenr goto junkput; 1986145522Sdarrenr } 1987145522Sdarrenr 1988145522Sdarrenr KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize); 1989255332Scy if (ipnn == NULL) { 1990255332Scy IPFERROR(60035); 199160852Sdarrenr return ENOMEM; 1992255332Scy } 1993145522Sdarrenr 1994255332Scy bzero(ipnn, ipn.ipn_dsize); 1995255332Scy error = ipf_inobjsz(softc, data, ipnn, IPFOBJ_NATSAVE, 1996255332Scy ipn.ipn_dsize); 1997145522Sdarrenr if (error != 0) { 199864580Sdarrenr goto junkput; 199964580Sdarrenr } 200060852Sdarrenr } else 2001145522Sdarrenr ipnn = &ipn; 200260852Sdarrenr 200360852Sdarrenr KMALLOC(nat, nat_t *); 200464580Sdarrenr if (nat == NULL) { 2005255332Scy IPFERROR(60037); 2006145522Sdarrenr error = ENOMEM; 200764580Sdarrenr goto junkput; 200864580Sdarrenr } 200960852Sdarrenr 2010145522Sdarrenr bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat)); 2011255332Scy 2012255332Scy switch (nat->nat_v[0]) 2013255332Scy { 2014255332Scy case 4: 2015255332Scy#ifdef USE_INET6 2016255332Scy case 6 : 2017255332Scy#endif 2018255332Scy break; 2019255332Scy default : 2020255332Scy IPFERROR(60061); 2021255332Scy error = EPROTONOSUPPORT; 2022255332Scy goto junkput; 2023255332Scy /*NOTREACHED*/ 2024255332Scy } 2025255332Scy 202660852Sdarrenr /* 2027255332Scy * Initialize all these so that ipf_nat_delete() doesn't cause a crash. 202860852Sdarrenr */ 2029145522Sdarrenr bzero((char *)nat, offsetof(struct nat, nat_tqe)); 2030145522Sdarrenr nat->nat_tqe.tqe_pnext = NULL; 2031145522Sdarrenr nat->nat_tqe.tqe_next = NULL; 2032145522Sdarrenr nat->nat_tqe.tqe_ifq = NULL; 2033145522Sdarrenr nat->nat_tqe.tqe_parent = nat; 203460852Sdarrenr 203560852Sdarrenr /* 203660852Sdarrenr * Restore the rule associated with this nat session 203760852Sdarrenr */ 2038145522Sdarrenr in = ipnn->ipn_nat.nat_ptr; 2039145522Sdarrenr if (in != NULL) { 2040255332Scy KMALLOCS(in, ipnat_t *, ipnn->ipn_ipnat.in_size); 2041145522Sdarrenr nat->nat_ptr = in; 204260852Sdarrenr if (in == NULL) { 2043255332Scy IPFERROR(60038); 204460852Sdarrenr error = ENOMEM; 204560852Sdarrenr goto junkput; 204660852Sdarrenr } 2047255332Scy bcopy((char *)&ipnn->ipn_ipnat, (char *)in, 2048255332Scy ipnn->ipn_ipnat.in_size); 204960852Sdarrenr in->in_use = 1; 205060852Sdarrenr in->in_flags |= IPN_DELETE; 2051145522Sdarrenr 2052255332Scy ATOMIC_INC32(softn->ipf_nat_stats.ns_rules); 2053145522Sdarrenr 2054255332Scy if (ipf_nat_resolverule(softc, in) != 0) { 2055255332Scy IPFERROR(60039); 2056161356Sguido error = ESRCH; 2057161356Sguido goto junkput; 2058161356Sguido } 2059145522Sdarrenr } 2060145522Sdarrenr 2061145522Sdarrenr /* 2062145522Sdarrenr * Check that the NAT entry doesn't already exist in the kernel. 2063161356Sguido * 2064161356Sguido * For NAT_OUTBOUND, we're lookup for a duplicate MAP entry. To do 2065161356Sguido * this, we check to see if the inbound combination of addresses and 2066161356Sguido * ports is already known. Similar logic is applied for NAT_INBOUND. 2067255332Scy * 2068145522Sdarrenr */ 2069145522Sdarrenr bzero((char *)&fin, sizeof(fin)); 2070255332Scy fin.fin_v = nat->nat_v[0]; 2071255332Scy fin.fin_p = nat->nat_pr[0]; 2072255332Scy fin.fin_rev = nat->nat_rev; 2073255332Scy fin.fin_ifp = nat->nat_ifps[0]; 2074255332Scy fin.fin_data[0] = ntohs(nat->nat_ndport); 2075255332Scy fin.fin_data[1] = ntohs(nat->nat_nsport); 2076255332Scy 2077255332Scy switch (nat->nat_dir) 2078255332Scy { 2079255332Scy case NAT_OUTBOUND : 2080255332Scy case NAT_DIVERTOUT : 2081153876Sguido if (getlock) { 2082255332Scy READ_ENTER(&softc->ipf_nat); 2083153876Sguido } 2084255332Scy 2085255332Scy fin.fin_v = nat->nat_v[1]; 2086255332Scy if (nat->nat_v[1] == 4) { 2087255332Scy n = ipf_nat_inlookup(&fin, nat->nat_flags, fin.fin_p, 2088255332Scy nat->nat_ndstip, nat->nat_nsrcip); 2089255332Scy#ifdef USE_INET6 2090255332Scy } else if (nat->nat_v[1] == 6) { 2091255332Scy n = ipf_nat6_inlookup(&fin, nat->nat_flags, fin.fin_p, 2092255332Scy &nat->nat_ndst6.in6, 2093255332Scy &nat->nat_nsrc6.in6); 2094255332Scy#endif 2095255332Scy } 2096255332Scy 2097153876Sguido if (getlock) { 2098255332Scy RWLOCK_EXIT(&softc->ipf_nat); 2099153876Sguido } 2100153876Sguido if (n != NULL) { 2101255332Scy IPFERROR(60040); 2102145522Sdarrenr error = EEXIST; 2103145522Sdarrenr goto junkput; 210460852Sdarrenr } 2105255332Scy break; 2106255332Scy 2107255332Scy case NAT_INBOUND : 2108255332Scy case NAT_DIVERTIN : 2109153876Sguido if (getlock) { 2110255332Scy READ_ENTER(&softc->ipf_nat); 2111153876Sguido } 2112255332Scy 2113255332Scy if (fin.fin_v == 4) { 2114255332Scy n = ipf_nat_outlookup(&fin, nat->nat_flags, fin.fin_p, 2115255332Scy nat->nat_ndstip, 2116255332Scy nat->nat_nsrcip); 2117255332Scy#ifdef USE_INET6 2118255332Scy } else if (fin.fin_v == 6) { 2119255332Scy n = ipf_nat6_outlookup(&fin, nat->nat_flags, fin.fin_p, 2120255332Scy &nat->nat_ndst6.in6, 2121255332Scy &nat->nat_nsrc6.in6); 2122255332Scy#endif 2123255332Scy } 2124255332Scy 2125153876Sguido if (getlock) { 2126255332Scy RWLOCK_EXIT(&softc->ipf_nat); 2127153876Sguido } 2128153876Sguido if (n != NULL) { 2129255332Scy IPFERROR(60041); 2130145522Sdarrenr error = EEXIST; 2131145522Sdarrenr goto junkput; 2132145522Sdarrenr } 2133255332Scy break; 2134255332Scy 2135255332Scy default : 2136255332Scy IPFERROR(60042); 2137145522Sdarrenr error = EINVAL; 2138145522Sdarrenr goto junkput; 213960852Sdarrenr } 214060852Sdarrenr 214160852Sdarrenr /* 214260852Sdarrenr * Restore ap_session_t structure. Include the private data allocated 214360852Sdarrenr * if it was there. 214460852Sdarrenr */ 2145145522Sdarrenr aps = nat->nat_aps; 2146145522Sdarrenr if (aps != NULL) { 214760852Sdarrenr KMALLOC(aps, ap_session_t *); 2148145522Sdarrenr nat->nat_aps = aps; 214960852Sdarrenr if (aps == NULL) { 2150255332Scy IPFERROR(60043); 215160852Sdarrenr error = ENOMEM; 215260852Sdarrenr goto junkput; 215360852Sdarrenr } 215460852Sdarrenr bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps)); 2155145522Sdarrenr if (in != NULL) 215660852Sdarrenr aps->aps_apr = in->in_apr; 2157145522Sdarrenr else 2158145522Sdarrenr aps->aps_apr = NULL; 2159145522Sdarrenr if (aps->aps_psiz != 0) { 2160145522Sdarrenr if (aps->aps_psiz > 81920) { 2161255332Scy IPFERROR(60044); 2162145522Sdarrenr error = ENOMEM; 2163145522Sdarrenr goto junkput; 2164145522Sdarrenr } 216560852Sdarrenr KMALLOCS(aps->aps_data, void *, aps->aps_psiz); 216660852Sdarrenr if (aps->aps_data == NULL) { 2167255332Scy IPFERROR(60045); 216860852Sdarrenr error = ENOMEM; 216960852Sdarrenr goto junkput; 217060852Sdarrenr } 217160852Sdarrenr bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data, 217260852Sdarrenr aps->aps_psiz); 217360852Sdarrenr } else { 217460852Sdarrenr aps->aps_psiz = 0; 217560852Sdarrenr aps->aps_data = NULL; 217660852Sdarrenr } 217760852Sdarrenr } 217860852Sdarrenr 217960852Sdarrenr /* 218060852Sdarrenr * If there was a filtering rule associated with this entry then 218160852Sdarrenr * build up a new one. 218260852Sdarrenr */ 2183145522Sdarrenr fr = nat->nat_fr; 218460852Sdarrenr if (fr != NULL) { 2185145522Sdarrenr if ((nat->nat_flags & SI_NEWFR) != 0) { 218660852Sdarrenr KMALLOC(fr, frentry_t *); 218760852Sdarrenr nat->nat_fr = fr; 218860852Sdarrenr if (fr == NULL) { 2189255332Scy IPFERROR(60046); 219060852Sdarrenr error = ENOMEM; 219160852Sdarrenr goto junkput; 219260852Sdarrenr } 2193145522Sdarrenr ipnn->ipn_nat.nat_fr = fr; 2194145522Sdarrenr fr->fr_ref = 1; 2195255332Scy (void) ipf_outobj(softc, data, ipnn, IPFOBJ_NATSAVE); 2196145522Sdarrenr bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr)); 2197161356Sguido 2198161356Sguido fr->fr_ref = 1; 2199161356Sguido fr->fr_dsize = 0; 2200161356Sguido fr->fr_data = NULL; 2201161356Sguido fr->fr_type = FR_T_NONE; 2202161356Sguido 2203145522Sdarrenr MUTEX_NUKE(&fr->fr_lock); 2204145522Sdarrenr MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock"); 220560852Sdarrenr } else { 2206153876Sguido if (getlock) { 2207255332Scy READ_ENTER(&softc->ipf_nat); 2208153876Sguido } 2209255332Scy for (n = softn->ipf_nat_instances; n; n = n->nat_next) 221060852Sdarrenr if (n->nat_fr == fr) 221160852Sdarrenr break; 2212145522Sdarrenr 2213145522Sdarrenr if (n != NULL) { 2214145522Sdarrenr MUTEX_ENTER(&fr->fr_lock); 2215145522Sdarrenr fr->fr_ref++; 2216145522Sdarrenr MUTEX_EXIT(&fr->fr_lock); 2217145522Sdarrenr } 2218153876Sguido if (getlock) { 2219255332Scy RWLOCK_EXIT(&softc->ipf_nat); 2220153876Sguido } 2221145522Sdarrenr 2222255332Scy if (n == NULL) { 2223255332Scy IPFERROR(60047); 222460852Sdarrenr error = ESRCH; 222560852Sdarrenr goto junkput; 222660852Sdarrenr } 222760852Sdarrenr } 222860852Sdarrenr } 222960852Sdarrenr 2230145522Sdarrenr if (ipnn != &ipn) { 2231145522Sdarrenr KFREES(ipnn, ipn.ipn_dsize); 2232145522Sdarrenr ipnn = NULL; 2233145522Sdarrenr } 2234145522Sdarrenr 2235145522Sdarrenr if (getlock) { 2236255332Scy WRITE_ENTER(&softc->ipf_nat); 2237145522Sdarrenr } 2238255332Scy 2239255332Scy if (fin.fin_v == 4) 2240255332Scy error = ipf_nat_finalise(&fin, nat); 2241255332Scy#ifdef USE_INET6 2242255332Scy else 2243255332Scy error = ipf_nat6_finalise(&fin, nat); 2244255332Scy#endif 2245255332Scy 2246145522Sdarrenr if (getlock) { 2247255332Scy RWLOCK_EXIT(&softc->ipf_nat); 2248145522Sdarrenr } 2249145522Sdarrenr 2250145522Sdarrenr if (error == 0) 2251145522Sdarrenr return 0; 2252145522Sdarrenr 2253255332Scy IPFERROR(60048); 2254145522Sdarrenr error = ENOMEM; 2255145522Sdarrenr 225660852Sdarrenrjunkput: 2257255332Scy if (fr != NULL) { 2258255332Scy (void) ipf_derefrule(softc, &fr); 2259255332Scy } 2260145522Sdarrenr 2261145522Sdarrenr if ((ipnn != NULL) && (ipnn != &ipn)) { 2262145522Sdarrenr KFREES(ipnn, ipn.ipn_dsize); 2263145522Sdarrenr } 2264145522Sdarrenr if (nat != NULL) { 2265145522Sdarrenr if (aps != NULL) { 2266145522Sdarrenr if (aps->aps_data != NULL) { 2267145522Sdarrenr KFREES(aps->aps_data, aps->aps_psiz); 2268145522Sdarrenr } 2269145522Sdarrenr KFREE(aps); 2270145522Sdarrenr } 2271145522Sdarrenr if (in != NULL) { 2272145522Sdarrenr if (in->in_apr) 2273255332Scy ipf_proxy_deref(in->in_apr); 2274255332Scy KFREES(in, in->in_size); 2275145522Sdarrenr } 2276145522Sdarrenr KFREE(nat); 2277145522Sdarrenr } 227860852Sdarrenr return error; 227960852Sdarrenr} 228060852Sdarrenr 228160852Sdarrenr 2282145522Sdarrenr/* ------------------------------------------------------------------------ */ 2283255332Scy/* Function: ipf_nat_delete */ 2284145522Sdarrenr/* Returns: Nil */ 2285255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 2286255332Scy/* nat(I) - pointer to NAT structure to delete */ 2287145522Sdarrenr/* logtype(I) - type of LOG record to create before deleting */ 2288145522Sdarrenr/* Write Lock: ipf_nat */ 2289145522Sdarrenr/* */ 2290145522Sdarrenr/* Delete a nat entry from the various lists and table. If NAT logging is */ 2291145522Sdarrenr/* enabled then generate a NAT log record for this event. */ 2292145522Sdarrenr/* ------------------------------------------------------------------------ */ 2293255332Scyvoid 2294255332Scyipf_nat_delete(softc, nat, logtype) 2295255332Scy ipf_main_softc_t *softc; 2296255332Scy struct nat *nat; 2297255332Scy int logtype; 229853642Sguido{ 2299255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 2300255332Scy int madeorphan = 0, bkt, removed = 0; 2301255332Scy nat_stat_side_t *nss; 230253642Sguido struct ipnat *ipn; 230353642Sguido 2304255332Scy if (logtype != 0 && softn->ipf_nat_logging != 0) 2305255332Scy ipf_nat_log(softc, softn, nat, logtype); 230653642Sguido 2307145522Sdarrenr /* 2308145522Sdarrenr * Take it as a general indication that all the pointers are set if 2309145522Sdarrenr * nat_pnext is set. 2310145522Sdarrenr */ 2311145522Sdarrenr if (nat->nat_pnext != NULL) { 2312172776Sdarrenr removed = 1; 2313172776Sdarrenr 2314255332Scy bkt = nat->nat_hv[0] % softn->ipf_nat_table_sz; 2315255332Scy nss = &softn->ipf_nat_stats.ns_side[0]; 2316255332Scy nss->ns_bucketlen[bkt]--; 2317255332Scy if (nss->ns_bucketlen[bkt] == 0) { 2318255332Scy nss->ns_inuse--; 2319255332Scy } 2320145522Sdarrenr 2321255332Scy bkt = nat->nat_hv[1] % softn->ipf_nat_table_sz; 2322255332Scy nss = &softn->ipf_nat_stats.ns_side[1]; 2323255332Scy nss->ns_bucketlen[bkt]--; 2324255332Scy if (nss->ns_bucketlen[bkt] == 0) { 2325255332Scy nss->ns_inuse--; 2326255332Scy } 2327255332Scy 2328145522Sdarrenr *nat->nat_pnext = nat->nat_next; 2329145522Sdarrenr if (nat->nat_next != NULL) { 2330145522Sdarrenr nat->nat_next->nat_pnext = nat->nat_pnext; 2331145522Sdarrenr nat->nat_next = NULL; 2332145522Sdarrenr } 2333145522Sdarrenr nat->nat_pnext = NULL; 2334145522Sdarrenr 2335145522Sdarrenr *nat->nat_phnext[0] = nat->nat_hnext[0]; 2336145522Sdarrenr if (nat->nat_hnext[0] != NULL) { 2337145522Sdarrenr nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 2338145522Sdarrenr nat->nat_hnext[0] = NULL; 2339145522Sdarrenr } 2340145522Sdarrenr nat->nat_phnext[0] = NULL; 2341145522Sdarrenr 2342145522Sdarrenr *nat->nat_phnext[1] = nat->nat_hnext[1]; 2343145522Sdarrenr if (nat->nat_hnext[1] != NULL) { 2344145522Sdarrenr nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 2345145522Sdarrenr nat->nat_hnext[1] = NULL; 2346145522Sdarrenr } 2347145522Sdarrenr nat->nat_phnext[1] = NULL; 2348145522Sdarrenr 2349255332Scy if ((nat->nat_flags & SI_WILDP) != 0) { 2350255332Scy ATOMIC_DEC32(softn->ipf_nat_stats.ns_wilds); 2351255332Scy } 2352255332Scy madeorphan = 1; 235353642Sguido } 235460852Sdarrenr 2355145522Sdarrenr if (nat->nat_me != NULL) { 2356145522Sdarrenr *nat->nat_me = NULL; 2357145522Sdarrenr nat->nat_me = NULL; 2358255332Scy nat->nat_ref--; 2359255332Scy ASSERT(nat->nat_ref >= 0); 2360145522Sdarrenr } 236160852Sdarrenr 2362255332Scy if (nat->nat_tqe.tqe_ifq != NULL) { 2363255332Scy /* 2364255332Scy * No call to ipf_freetimeoutqueue() is made here, they are 2365255332Scy * garbage collected in ipf_nat_expire(). 2366255332Scy */ 2367255332Scy (void) ipf_deletequeueentry(&nat->nat_tqe); 2368255332Scy } 2369145522Sdarrenr 2370255332Scy if (nat->nat_sync) { 2371255332Scy ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); 2372255332Scy nat->nat_sync = NULL; 2373255332Scy } 2374255332Scy 2375170268Sdarrenr if (logtype == NL_EXPIRE) 2376255332Scy softn->ipf_nat_stats.ns_expire++; 2377170268Sdarrenr 2378172776Sdarrenr MUTEX_ENTER(&nat->nat_lock); 2379172776Sdarrenr /* 2380172776Sdarrenr * NL_DESTROY should only be passed in when we've got nat_ref >= 2. 2381172776Sdarrenr * This happens when a nat'd packet is blocked and we want to throw 2382172776Sdarrenr * away the NAT session. 2383172776Sdarrenr */ 2384172776Sdarrenr if (logtype == NL_DESTROY) { 2385172776Sdarrenr if (nat->nat_ref > 2) { 2386172776Sdarrenr nat->nat_ref -= 2; 2387172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 2388172776Sdarrenr if (removed) 2389255332Scy softn->ipf_nat_stats.ns_orphans++; 2390172776Sdarrenr return; 2391172776Sdarrenr } 2392172776Sdarrenr } else if (nat->nat_ref > 1) { 2393172776Sdarrenr nat->nat_ref--; 2394172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 2395255332Scy if (madeorphan == 1) 2396255332Scy softn->ipf_nat_stats.ns_orphans++; 2397145522Sdarrenr return; 2398145522Sdarrenr } 2399255332Scy ASSERT(nat->nat_ref >= 0); 2400172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 2401170268Sdarrenr 2402255332Scy nat->nat_ref = 0; 2403255332Scy 2404255332Scy if (madeorphan == 0) 2405255332Scy softn->ipf_nat_stats.ns_orphans--; 2406255332Scy 2407161356Sguido /* 2408255332Scy * At this point, nat_ref can be either 0 or -1 2409161356Sguido */ 2410255332Scy softn->ipf_nat_stats.ns_proto[nat->nat_pr[0]]--; 2411145522Sdarrenr 2412255332Scy if (nat->nat_fr != NULL) { 2413255332Scy (void) ipf_derefrule(softc, &nat->nat_fr); 2414255332Scy } 2415145522Sdarrenr 2416255332Scy if (nat->nat_hm != NULL) { 2417255332Scy ipf_nat_hostmapdel(softc, &nat->nat_hm); 2418255332Scy } 2419145522Sdarrenr 242053642Sguido /* 242153642Sguido * If there is an active reference from the nat entry to its parent 242253642Sguido * rule, decrement the rule's reference count and free it too if no 242353642Sguido * longer being used. 242453642Sguido */ 2425145522Sdarrenr ipn = nat->nat_ptr; 2426255332Scy nat->nat_ptr = NULL; 2427255332Scy 242853642Sguido if (ipn != NULL) { 2429255332Scy ipn->in_space++; 2430255332Scy ipf_nat_rule_deref(softc, &ipn); 243153642Sguido } 243253642Sguido 2433255332Scy if (nat->nat_aps != NULL) { 2434255332Scy ipf_proxy_free(softc, nat->nat_aps); 2435255332Scy nat->nat_aps = NULL; 2436255332Scy } 2437255332Scy 2438145522Sdarrenr MUTEX_DESTROY(&nat->nat_lock); 2439145522Sdarrenr 2440255332Scy softn->ipf_nat_stats.ns_active--; 2441145522Sdarrenr 244253642Sguido /* 244353642Sguido * If there's a fragment table entry too for this nat entry, then 2444145522Sdarrenr * dereference that as well. This is after nat_lock is released 2445145522Sdarrenr * because of Tru64. 244653642Sguido */ 2447255332Scy ipf_frag_natforget(softc, (void *)nat); 2448145522Sdarrenr 2449145522Sdarrenr KFREE(nat); 245053642Sguido} 245153642Sguido 245253642Sguido 2453145522Sdarrenr/* ------------------------------------------------------------------------ */ 2454255332Scy/* Function: ipf_nat_flushtable */ 2455145522Sdarrenr/* Returns: int - number of NAT rules deleted */ 2456255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 2457255332Scy/* softn(I) - pointer to NAT context structure */ 2458255332Scy/* Write Lock: ipf_nat */ 2459145522Sdarrenr/* */ 2460145522Sdarrenr/* Deletes all currently active NAT sessions. In deleting each NAT entry a */ 2461255332Scy/* log record should be emitted in ipf_nat_delete() if NAT logging is */ 2462255332Scy/* enabled. */ 2463145522Sdarrenr/* ------------------------------------------------------------------------ */ 246453642Sguido/* 246553642Sguido * nat_flushtable - clear the NAT table of all mapping entries. 246653642Sguido */ 2467255332Scystatic int 2468255332Scyipf_nat_flushtable(softc, softn) 2469255332Scy ipf_main_softc_t *softc; 2470255332Scy ipf_nat_softc_t *softn; 247153642Sguido{ 2472145522Sdarrenr nat_t *nat; 2473145522Sdarrenr int j = 0; 247467614Sdarrenr 247553642Sguido /* 247653642Sguido * ALL NAT mappings deleted, so lets just make the deletions 247753642Sguido * quicker. 247853642Sguido */ 2479255332Scy if (softn->ipf_nat_table[0] != NULL) 2480255332Scy bzero((char *)softn->ipf_nat_table[0], 2481255332Scy sizeof(softn->ipf_nat_table[0]) * 2482255332Scy softn->ipf_nat_table_sz); 2483255332Scy if (softn->ipf_nat_table[1] != NULL) 2484255332Scy bzero((char *)softn->ipf_nat_table[1], 2485255332Scy sizeof(softn->ipf_nat_table[1]) * 2486255332Scy softn->ipf_nat_table_sz); 248753642Sguido 2488255332Scy while ((nat = softn->ipf_nat_instances) != NULL) { 2489255332Scy ipf_nat_delete(softc, nat, NL_FLUSH); 249053642Sguido j++; 249153642Sguido } 2492145522Sdarrenr 249353642Sguido return j; 249453642Sguido} 249553642Sguido 249653642Sguido 2497145522Sdarrenr/* ------------------------------------------------------------------------ */ 2498255332Scy/* Function: ipf_nat_clearlist */ 2499145522Sdarrenr/* Returns: int - number of NAT/RDR rules deleted */ 2500255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 2501255332Scy/* softn(I) - pointer to NAT context structure */ 2502145522Sdarrenr/* */ 2503145522Sdarrenr/* Delete all rules in the current list of rules. There is nothing elegant */ 2504145522Sdarrenr/* about this cleanup: simply free all entries on the list of rules and */ 2505145522Sdarrenr/* clear out the tables used for hashed NAT rule lookups. */ 2506145522Sdarrenr/* ------------------------------------------------------------------------ */ 2507255332Scystatic int 2508255332Scyipf_nat_clearlist(softc, softn) 2509255332Scy ipf_main_softc_t *softc; 2510255332Scy ipf_nat_softc_t *softn; 251153642Sguido{ 2512255332Scy ipnat_t *n; 251353642Sguido int i = 0; 251453642Sguido 2515255332Scy if (softn->ipf_nat_map_rules != NULL) { 2516255332Scy bzero((char *)softn->ipf_nat_map_rules, 2517255332Scy sizeof(*softn->ipf_nat_map_rules) * 2518255332Scy softn->ipf_nat_maprules_sz); 2519255332Scy } 2520255332Scy if (softn->ipf_nat_rdr_rules != NULL) { 2521255332Scy bzero((char *)softn->ipf_nat_rdr_rules, 2522255332Scy sizeof(*softn->ipf_nat_rdr_rules) * 2523255332Scy softn->ipf_nat_rdrrules_sz); 2524255332Scy } 252553642Sguido 2526255332Scy while ((n = softn->ipf_nat_list) != NULL) { 2527255332Scy ipf_nat_delrule(softc, softn, n, 0); 252853642Sguido i++; 252953642Sguido } 2530255332Scy#if SOLARIS && !defined(INSTANCES) 2531145522Sdarrenr pfil_delayed_copy = 1; 2532145522Sdarrenr#endif 253353642Sguido return i; 253453642Sguido} 253553642Sguido 253653642Sguido 2537145522Sdarrenr/* ------------------------------------------------------------------------ */ 2538255332Scy/* Function: ipf_nat_delrule */ 2539255332Scy/* Returns: Nil */ 2540255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 2541255332Scy/* softn(I) - pointer to NAT context structure */ 2542255332Scy/* np(I) - pointer to NAT rule to delete */ 2543255332Scy/* purge(I) - 1 == allow purge, 0 == prevent purge */ 2544255332Scy/* Locks: WRITE(ipf_nat) */ 2545255332Scy/* */ 2546255332Scy/* Preventing "purge" from occuring is allowed because when all of the NAT */ 2547255332Scy/* rules are being removed, allowing the "purge" to walk through the list */ 2548255332Scy/* of NAT sessions, possibly multiple times, would be a large performance */ 2549255332Scy/* hit, on the order of O(N^2). */ 2550255332Scy/* ------------------------------------------------------------------------ */ 2551255332Scystatic void 2552255332Scyipf_nat_delrule(softc, softn, np, purge) 2553255332Scy ipf_main_softc_t *softc; 2554255332Scy ipf_nat_softc_t *softn; 2555255332Scy ipnat_t *np; 2556255332Scy int purge; 2557255332Scy{ 2558255332Scy 2559255332Scy if (np->in_pnext != NULL) { 2560255332Scy *np->in_pnext = np->in_next; 2561255332Scy if (np->in_next != NULL) 2562255332Scy np->in_next->in_pnext = np->in_pnext; 2563255332Scy if (softn->ipf_nat_list_tail == &np->in_next) 2564255332Scy softn->ipf_nat_list_tail = np->in_pnext; 2565255332Scy } 2566255332Scy 2567255332Scy if ((purge == 1) && ((np->in_flags & IPN_PURGE) != 0)) { 2568255332Scy nat_t *next; 2569255332Scy nat_t *nat; 2570255332Scy 2571255332Scy for (next = softn->ipf_nat_instances; (nat = next) != NULL;) { 2572255332Scy next = nat->nat_next; 2573255332Scy if (nat->nat_ptr == np) 2574255332Scy ipf_nat_delete(softc, nat, NL_PURGE); 2575255332Scy } 2576255332Scy } 2577255332Scy 2578255332Scy if ((np->in_flags & IPN_DELETE) == 0) { 2579255332Scy if (np->in_redir & NAT_REDIRECT) { 2580255332Scy switch (np->in_v[0]) 2581255332Scy { 2582255332Scy case 4 : 2583255332Scy ipf_nat_delrdr(softn, np); 2584255332Scy break; 2585255332Scy#ifdef USE_INET6 2586255332Scy case 6 : 2587255332Scy ipf_nat6_delrdr(softn, np); 2588255332Scy break; 2589255332Scy#endif 2590255332Scy } 2591255332Scy } 2592255332Scy if (np->in_redir & (NAT_MAPBLK|NAT_MAP)) { 2593255332Scy switch (np->in_v[0]) 2594255332Scy { 2595255332Scy case 4 : 2596255332Scy ipf_nat_delmap(softn, np); 2597255332Scy break; 2598255332Scy#ifdef USE_INET6 2599255332Scy case 6 : 2600255332Scy ipf_nat6_delmap(softn, np); 2601255332Scy break; 2602255332Scy#endif 2603255332Scy } 2604255332Scy } 2605255332Scy } 2606255332Scy 2607255332Scy np->in_flags |= IPN_DELETE; 2608255332Scy ipf_nat_rule_deref(softc, &np); 2609255332Scy} 2610255332Scy 2611255332Scy 2612255332Scy/* ------------------------------------------------------------------------ */ 2613255332Scy/* Function: ipf_nat_newmap */ 2614145522Sdarrenr/* Returns: int - -1 == error, 0 == success */ 2615145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2616145522Sdarrenr/* nat(I) - pointer to NAT entry */ 2617145522Sdarrenr/* ni(I) - pointer to structure with misc. information needed */ 2618145522Sdarrenr/* to create new NAT entry. */ 2619145522Sdarrenr/* */ 2620145522Sdarrenr/* Given an empty NAT structure, populate it with new information about a */ 2621145522Sdarrenr/* new NAT session, as defined by the matching NAT rule. */ 2622145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 2623145522Sdarrenr/* to the new IP address for the translation. */ 2624145522Sdarrenr/* ------------------------------------------------------------------------ */ 2625255332Scystatic int 2626255332Scyipf_nat_newmap(fin, nat, ni) 2627255332Scy fr_info_t *fin; 2628255332Scy nat_t *nat; 2629255332Scy natinfo_t *ni; 2630145522Sdarrenr{ 2631255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 2632255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 2633145522Sdarrenr u_short st_port, dport, sport, port, sp, dp; 2634145522Sdarrenr struct in_addr in, inb; 2635145522Sdarrenr hostmap_t *hm; 2636145522Sdarrenr u_32_t flags; 2637145522Sdarrenr u_32_t st_ip; 2638145522Sdarrenr ipnat_t *np; 2639145522Sdarrenr nat_t *natl; 2640145522Sdarrenr int l; 2641145522Sdarrenr 2642145522Sdarrenr /* 2643145522Sdarrenr * If it's an outbound packet which doesn't match any existing 2644145522Sdarrenr * record, then create a new port 2645145522Sdarrenr */ 2646145522Sdarrenr l = 0; 2647145522Sdarrenr hm = NULL; 2648145522Sdarrenr np = ni->nai_np; 2649255332Scy st_ip = np->in_snip; 2650255332Scy st_port = np->in_spnext; 2651255332Scy flags = nat->nat_flags; 2652145522Sdarrenr 2653255332Scy if (flags & IPN_ICMPQUERY) { 2654255332Scy sport = fin->fin_data[1]; 2655255332Scy dport = 0; 2656255332Scy } else { 2657255332Scy sport = htons(fin->fin_data[0]); 2658255332Scy dport = htons(fin->fin_data[1]); 2659255332Scy } 2660255332Scy 2661145522Sdarrenr /* 2662145522Sdarrenr * Do a loop until we either run out of entries to try or we find 2663145522Sdarrenr * a NAT mapping that isn't currently being used. This is done 2664145522Sdarrenr * because the change to the source is not (usually) being fixed. 2665145522Sdarrenr */ 2666145522Sdarrenr do { 2667145522Sdarrenr port = 0; 2668255332Scy in.s_addr = htonl(np->in_snip); 2669145522Sdarrenr if (l == 0) { 2670145522Sdarrenr /* 2671145522Sdarrenr * Check to see if there is an existing NAT 2672145522Sdarrenr * setup for this IP address pair. 2673145522Sdarrenr */ 2674255332Scy hm = ipf_nat_hostmap(softn, np, fin->fin_src, 2675255332Scy fin->fin_dst, in, 0); 2676145522Sdarrenr if (hm != NULL) 2677255332Scy in.s_addr = hm->hm_nsrcip.s_addr; 2678145522Sdarrenr } else if ((l == 1) && (hm != NULL)) { 2679255332Scy ipf_nat_hostmapdel(softc, &hm); 2680145522Sdarrenr } 2681145522Sdarrenr in.s_addr = ntohl(in.s_addr); 2682145522Sdarrenr 2683145522Sdarrenr nat->nat_hm = hm; 2684145522Sdarrenr 2685255332Scy if ((np->in_nsrcmsk == 0xffffffff) && (np->in_spnext == 0)) { 2686255332Scy if (l > 0) { 2687255332Scy NBUMPSIDEX(1, ns_exhausted, ns_exhausted_1); 2688145522Sdarrenr return -1; 2689255332Scy } 2690145522Sdarrenr } 2691145522Sdarrenr 2692145522Sdarrenr if (np->in_redir == NAT_BIMAP && 2693255332Scy np->in_osrcmsk == np->in_nsrcmsk) { 2694145522Sdarrenr /* 2695145522Sdarrenr * map the address block in a 1:1 fashion 2696145522Sdarrenr */ 2697255332Scy in.s_addr = np->in_nsrcaddr; 2698255332Scy in.s_addr |= fin->fin_saddr & ~np->in_osrcmsk; 2699145522Sdarrenr in.s_addr = ntohl(in.s_addr); 2700145522Sdarrenr 2701145522Sdarrenr } else if (np->in_redir & NAT_MAPBLK) { 2702145522Sdarrenr if ((l >= np->in_ppip) || ((l > 0) && 2703255332Scy !(flags & IPN_TCPUDP))) { 2704255332Scy NBUMPSIDEX(1, ns_exhausted, ns_exhausted_2); 2705145522Sdarrenr return -1; 2706255332Scy } 2707145522Sdarrenr /* 2708145522Sdarrenr * map-block - Calculate destination address. 2709145522Sdarrenr */ 2710145522Sdarrenr in.s_addr = ntohl(fin->fin_saddr); 2711255332Scy in.s_addr &= ntohl(~np->in_osrcmsk); 2712145522Sdarrenr inb.s_addr = in.s_addr; 2713145522Sdarrenr in.s_addr /= np->in_ippip; 2714255332Scy in.s_addr &= ntohl(~np->in_nsrcmsk); 2715255332Scy in.s_addr += ntohl(np->in_nsrcaddr); 2716145522Sdarrenr /* 2717145522Sdarrenr * Calculate destination port. 2718145522Sdarrenr */ 2719145522Sdarrenr if ((flags & IPN_TCPUDP) && 2720145522Sdarrenr (np->in_ppip != 0)) { 2721145522Sdarrenr port = ntohs(sport) + l; 2722145522Sdarrenr port %= np->in_ppip; 2723145522Sdarrenr port += np->in_ppip * 2724145522Sdarrenr (inb.s_addr % np->in_ippip); 2725145522Sdarrenr port += MAPBLK_MINPORT; 2726145522Sdarrenr port = htons(port); 2727145522Sdarrenr } 2728145522Sdarrenr 2729255332Scy } else if ((np->in_nsrcaddr == 0) && 2730255332Scy (np->in_nsrcmsk == 0xffffffff)) { 2731255332Scy i6addr_t in6; 2732255332Scy 2733145522Sdarrenr /* 2734145522Sdarrenr * 0/32 - use the interface's IP address. 2735145522Sdarrenr */ 2736145522Sdarrenr if ((l > 0) || 2737255332Scy ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp, 2738255332Scy &in6, NULL) == -1) { 2739255332Scy NBUMPSIDEX(1, ns_new_ifpaddr, ns_new_ifpaddr_1); 2740145522Sdarrenr return -1; 2741255332Scy } 2742255332Scy in.s_addr = ntohl(in6.in4.s_addr); 2743145522Sdarrenr 2744255332Scy } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) { 2745145522Sdarrenr /* 2746145522Sdarrenr * 0/0 - use the original source address/port. 2747145522Sdarrenr */ 2748255332Scy if (l > 0) { 2749255332Scy NBUMPSIDEX(1, ns_exhausted, ns_exhausted_3); 2750145522Sdarrenr return -1; 2751255332Scy } 2752145522Sdarrenr in.s_addr = ntohl(fin->fin_saddr); 2753145522Sdarrenr 2754255332Scy } else if ((np->in_nsrcmsk != 0xffffffff) && 2755255332Scy (np->in_spnext == 0) && ((l > 0) || (hm == NULL))) 2756255332Scy np->in_snip++; 2757145522Sdarrenr 2758145522Sdarrenr natl = NULL; 2759145522Sdarrenr 2760145522Sdarrenr if ((flags & IPN_TCPUDP) && 2761145522Sdarrenr ((np->in_redir & NAT_MAPBLK) == 0) && 2762145522Sdarrenr (np->in_flags & IPN_AUTOPORTMAP)) { 2763145522Sdarrenr /* 2764145522Sdarrenr * "ports auto" (without map-block) 2765145522Sdarrenr */ 2766145522Sdarrenr if ((l > 0) && (l % np->in_ppip == 0)) { 2767255332Scy if ((l > np->in_ppip) && 2768255332Scy np->in_nsrcmsk != 0xffffffff) 2769255332Scy np->in_snip++; 2770145522Sdarrenr } 2771145522Sdarrenr if (np->in_ppip != 0) { 2772145522Sdarrenr port = ntohs(sport); 2773145522Sdarrenr port += (l % np->in_ppip); 2774145522Sdarrenr port %= np->in_ppip; 2775145522Sdarrenr port += np->in_ppip * 2776145522Sdarrenr (ntohl(fin->fin_saddr) % 2777145522Sdarrenr np->in_ippip); 2778145522Sdarrenr port += MAPBLK_MINPORT; 2779145522Sdarrenr port = htons(port); 2780145522Sdarrenr } 2781145522Sdarrenr 2782145522Sdarrenr } else if (((np->in_redir & NAT_MAPBLK) == 0) && 2783255332Scy (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) { 2784145522Sdarrenr /* 2785145522Sdarrenr * Standard port translation. Select next port. 2786145522Sdarrenr */ 2787180778Sdarrenr if (np->in_flags & IPN_SEQUENTIAL) { 2788255332Scy port = np->in_spnext; 2789180778Sdarrenr } else { 2790255332Scy port = ipf_random() % (np->in_spmax - 2791255332Scy np->in_spmin + 1); 2792255332Scy port += np->in_spmin; 2793180778Sdarrenr } 2794180832Sdarrenr port = htons(port); 2795255332Scy np->in_spnext++; 2796145522Sdarrenr 2797255332Scy if (np->in_spnext > np->in_spmax) { 2798255332Scy np->in_spnext = np->in_spmin; 2799255332Scy if (np->in_nsrcmsk != 0xffffffff) 2800255332Scy np->in_snip++; 2801145522Sdarrenr } 2802145522Sdarrenr } 2803145522Sdarrenr 2804255332Scy if (np->in_flags & IPN_SIPRANGE) { 2805255332Scy if (np->in_snip > ntohl(np->in_nsrcmsk)) 2806255332Scy np->in_snip = ntohl(np->in_nsrcaddr); 2807145522Sdarrenr } else { 2808255332Scy if ((np->in_nsrcmsk != 0xffffffff) && 2809255332Scy ((np->in_snip + 1) & ntohl(np->in_nsrcmsk)) > 2810255332Scy ntohl(np->in_nsrcaddr)) 2811255332Scy np->in_snip = ntohl(np->in_nsrcaddr) + 1; 2812145522Sdarrenr } 2813145522Sdarrenr 2814145522Sdarrenr if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) 2815145522Sdarrenr port = sport; 2816145522Sdarrenr 2817145522Sdarrenr /* 2818145522Sdarrenr * Here we do a lookup of the connection as seen from 2819145522Sdarrenr * the outside. If an IP# pair already exists, try 2820145522Sdarrenr * again. So if you have A->B becomes C->B, you can 2821145522Sdarrenr * also have D->E become C->E but not D->B causing 2822145522Sdarrenr * another C->B. Also take protocol and ports into 2823145522Sdarrenr * account when determining whether a pre-existing 2824145522Sdarrenr * NAT setup will cause an external conflict where 2825145522Sdarrenr * this is appropriate. 2826145522Sdarrenr */ 2827145522Sdarrenr inb.s_addr = htonl(in.s_addr); 2828145522Sdarrenr sp = fin->fin_data[0]; 2829145522Sdarrenr dp = fin->fin_data[1]; 2830145522Sdarrenr fin->fin_data[0] = fin->fin_data[1]; 2831255332Scy fin->fin_data[1] = ntohs(port); 2832255332Scy natl = ipf_nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 2833255332Scy (u_int)fin->fin_p, fin->fin_dst, inb); 2834145522Sdarrenr fin->fin_data[0] = sp; 2835145522Sdarrenr fin->fin_data[1] = dp; 2836145522Sdarrenr 2837145522Sdarrenr /* 2838145522Sdarrenr * Has the search wrapped around and come back to the 2839145522Sdarrenr * start ? 2840145522Sdarrenr */ 2841145522Sdarrenr if ((natl != NULL) && 2842255332Scy (np->in_spnext != 0) && (st_port == np->in_spnext) && 2843255332Scy (np->in_snip != 0) && (st_ip == np->in_snip)) { 2844255332Scy NBUMPSIDED(1, ns_wrap); 2845145522Sdarrenr return -1; 2846255332Scy } 2847145522Sdarrenr l++; 2848145522Sdarrenr } while (natl != NULL); 2849145522Sdarrenr 2850145522Sdarrenr /* Setup the NAT table */ 2851255332Scy nat->nat_osrcip = fin->fin_src; 2852255332Scy nat->nat_nsrcaddr = htonl(in.s_addr); 2853255332Scy nat->nat_odstip = fin->fin_dst; 2854255332Scy nat->nat_ndstip = fin->fin_dst; 2855145522Sdarrenr if (nat->nat_hm == NULL) 2856255332Scy nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src, 2857255332Scy fin->fin_dst, nat->nat_nsrcip, 2858255332Scy 0); 2859145522Sdarrenr 2860145522Sdarrenr if (flags & IPN_TCPUDP) { 2861255332Scy nat->nat_osport = sport; 2862255332Scy nat->nat_nsport = port; /* sport */ 2863255332Scy nat->nat_odport = dport; 2864255332Scy nat->nat_ndport = dport; 2865145522Sdarrenr ((tcphdr_t *)fin->fin_dp)->th_sport = port; 2866145522Sdarrenr } else if (flags & IPN_ICMPQUERY) { 2867255332Scy nat->nat_oicmpid = fin->fin_data[1]; 2868145522Sdarrenr ((icmphdr_t *)fin->fin_dp)->icmp_id = port; 2869255332Scy nat->nat_nicmpid = port; 2870145522Sdarrenr } 2871145522Sdarrenr return 0; 2872145522Sdarrenr} 2873145522Sdarrenr 2874145522Sdarrenr 2875145522Sdarrenr/* ------------------------------------------------------------------------ */ 2876255332Scy/* Function: ipf_nat_newrdr */ 2877145522Sdarrenr/* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ 2878145522Sdarrenr/* allow rule to be moved if IPN_ROUNDR is set. */ 2879145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2880145522Sdarrenr/* nat(I) - pointer to NAT entry */ 2881145522Sdarrenr/* ni(I) - pointer to structure with misc. information needed */ 2882145522Sdarrenr/* to create new NAT entry. */ 2883145522Sdarrenr/* */ 2884145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 2885145522Sdarrenr/* to the new IP address for the translation. */ 2886145522Sdarrenr/* ------------------------------------------------------------------------ */ 2887255332Scystatic int 2888255332Scyipf_nat_newrdr(fin, nat, ni) 2889255332Scy fr_info_t *fin; 2890255332Scy nat_t *nat; 2891255332Scy natinfo_t *ni; 2892145522Sdarrenr{ 2893255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 2894255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 2895145522Sdarrenr u_short nport, dport, sport; 2896170268Sdarrenr struct in_addr in, inb; 2897170268Sdarrenr u_short sp, dp; 2898145522Sdarrenr hostmap_t *hm; 2899145522Sdarrenr u_32_t flags; 2900145522Sdarrenr ipnat_t *np; 2901170268Sdarrenr nat_t *natl; 2902145522Sdarrenr int move; 2903145522Sdarrenr 2904145522Sdarrenr move = 1; 2905145522Sdarrenr hm = NULL; 2906145522Sdarrenr in.s_addr = 0; 2907145522Sdarrenr np = ni->nai_np; 2908255332Scy flags = nat->nat_flags; 2909145522Sdarrenr 2910255332Scy if (flags & IPN_ICMPQUERY) { 2911255332Scy dport = fin->fin_data[1]; 2912255332Scy sport = 0; 2913255332Scy } else { 2914255332Scy sport = htons(fin->fin_data[0]); 2915255332Scy dport = htons(fin->fin_data[1]); 2916255332Scy } 2917255332Scy 2918255332Scy /* TRACE sport, dport */ 2919255332Scy 2920255332Scy 2921145522Sdarrenr /* 2922145522Sdarrenr * If the matching rule has IPN_STICKY set, then we want to have the 2923145522Sdarrenr * same rule kick in as before. Why would this happen? If you have 2924145522Sdarrenr * a collection of rdr rules with "round-robin sticky", the current 2925145522Sdarrenr * packet might match a different one to the previous connection but 2926145522Sdarrenr * we want the same destination to be used. 2927145522Sdarrenr */ 2928153876Sguido if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) && 2929153876Sguido ((np->in_flags & IPN_STICKY) != 0)) { 2930255332Scy hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, fin->fin_dst, 2931255332Scy in, (u_32_t)dport); 2932145522Sdarrenr if (hm != NULL) { 2933255332Scy in.s_addr = ntohl(hm->hm_ndstip.s_addr); 2934145522Sdarrenr np = hm->hm_ipnat; 2935145522Sdarrenr ni->nai_np = np; 2936145522Sdarrenr move = 0; 2937255332Scy ipf_nat_hostmapdel(softc, &hm); 2938145522Sdarrenr } 2939145522Sdarrenr } 2940145522Sdarrenr 2941145522Sdarrenr /* 2942145522Sdarrenr * Otherwise, it's an inbound packet. Most likely, we don't 2943145522Sdarrenr * want to rewrite source ports and source addresses. Instead, 2944145522Sdarrenr * we want to rewrite to a fixed internal address and fixed 2945145522Sdarrenr * internal port. 2946145522Sdarrenr */ 2947145522Sdarrenr if (np->in_flags & IPN_SPLIT) { 2948255332Scy in.s_addr = np->in_dnip; 2949272998Scy inb.s_addr = htonl(in.s_addr); 2950145522Sdarrenr 2951145522Sdarrenr if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { 2952255332Scy hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, 2953272998Scy fin->fin_dst, inb, (u_32_t)dport); 2954145522Sdarrenr if (hm != NULL) { 2955255332Scy in.s_addr = hm->hm_ndstip.s_addr; 2956145522Sdarrenr move = 0; 2957145522Sdarrenr } 2958145522Sdarrenr } 2959145522Sdarrenr 2960145522Sdarrenr if (hm == NULL || hm->hm_ref == 1) { 2961255332Scy if (np->in_ndstaddr == htonl(in.s_addr)) { 2962255332Scy np->in_dnip = ntohl(np->in_ndstmsk); 2963145522Sdarrenr move = 0; 2964145522Sdarrenr } else { 2965255332Scy np->in_dnip = ntohl(np->in_ndstaddr); 2966145522Sdarrenr } 2967145522Sdarrenr } 2968255332Scy if (hm != NULL) 2969255332Scy ipf_nat_hostmapdel(softc, &hm); 2970145522Sdarrenr 2971255332Scy } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) { 2972255332Scy i6addr_t in6; 2973255332Scy 2974145522Sdarrenr /* 2975145522Sdarrenr * 0/32 - use the interface's IP address. 2976145522Sdarrenr */ 2977255332Scy if (ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp, 2978255332Scy &in6, NULL) == -1) { 2979255332Scy NBUMPSIDEX(0, ns_new_ifpaddr, ns_new_ifpaddr_2); 2980145522Sdarrenr return -1; 2981255332Scy } 2982255332Scy in.s_addr = ntohl(in6.in4.s_addr); 2983145522Sdarrenr 2984255332Scy } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk== 0)) { 2985145522Sdarrenr /* 2986145522Sdarrenr * 0/0 - use the original destination address/port. 2987145522Sdarrenr */ 2988145522Sdarrenr in.s_addr = ntohl(fin->fin_daddr); 2989145522Sdarrenr 2990145522Sdarrenr } else if (np->in_redir == NAT_BIMAP && 2991255332Scy np->in_ndstmsk == np->in_odstmsk) { 2992145522Sdarrenr /* 2993145522Sdarrenr * map the address block in a 1:1 fashion 2994145522Sdarrenr */ 2995255332Scy in.s_addr = np->in_ndstaddr; 2996255332Scy in.s_addr |= fin->fin_daddr & ~np->in_ndstmsk; 2997145522Sdarrenr in.s_addr = ntohl(in.s_addr); 2998145522Sdarrenr } else { 2999255332Scy in.s_addr = ntohl(np->in_ndstaddr); 3000145522Sdarrenr } 3001145522Sdarrenr 3002255332Scy if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) 3003145522Sdarrenr nport = dport; 3004145522Sdarrenr else { 3005145522Sdarrenr /* 3006145522Sdarrenr * Whilst not optimized for the case where 3007145522Sdarrenr * pmin == pmax, the gain is not significant. 3008145522Sdarrenr */ 3009145522Sdarrenr if (((np->in_flags & IPN_FIXEDDPORT) == 0) && 3010255332Scy (np->in_odport != np->in_dtop)) { 3011255332Scy nport = ntohs(dport) - np->in_odport + np->in_dpmax; 3012145522Sdarrenr nport = htons(nport); 3013255332Scy } else { 3014255332Scy nport = htons(np->in_dpnext); 3015255332Scy np->in_dpnext++; 3016255332Scy if (np->in_dpnext > np->in_dpmax) 3017255332Scy np->in_dpnext = np->in_dpmin; 3018255332Scy } 3019145522Sdarrenr } 3020145522Sdarrenr 3021145522Sdarrenr /* 3022145522Sdarrenr * When the redirect-to address is set to 0.0.0.0, just 3023145522Sdarrenr * assume a blank `forwarding' of the packet. We don't 3024145522Sdarrenr * setup any translation for this either. 3025145522Sdarrenr */ 3026145522Sdarrenr if (in.s_addr == 0) { 3027255332Scy if (nport == dport) { 3028255332Scy NBUMPSIDED(0, ns_xlate_null); 3029145522Sdarrenr return -1; 3030255332Scy } 3031145522Sdarrenr in.s_addr = ntohl(fin->fin_daddr); 3032145522Sdarrenr } 3033145522Sdarrenr 3034170268Sdarrenr /* 3035170268Sdarrenr * Check to see if this redirect mapping already exists and if 3036170268Sdarrenr * it does, return "failure" (allowing it to be created will just 3037170268Sdarrenr * cause one or both of these "connections" to stop working.) 3038170268Sdarrenr */ 3039170268Sdarrenr inb.s_addr = htonl(in.s_addr); 3040170268Sdarrenr sp = fin->fin_data[0]; 3041170268Sdarrenr dp = fin->fin_data[1]; 3042170268Sdarrenr fin->fin_data[1] = fin->fin_data[0]; 3043170268Sdarrenr fin->fin_data[0] = ntohs(nport); 3044255332Scy natl = ipf_nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 3045170268Sdarrenr (u_int)fin->fin_p, inb, fin->fin_src); 3046170268Sdarrenr fin->fin_data[0] = sp; 3047170268Sdarrenr fin->fin_data[1] = dp; 3048255332Scy if (natl != NULL) { 3049255332Scy DT2(ns_new_xlate_exists, fr_info_t *, fin, nat_t *, natl); 3050255332Scy NBUMPSIDE(0, ns_xlate_exists); 3051170268Sdarrenr return -1; 3052255332Scy } 3053170268Sdarrenr 3054272998Scy inb.s_addr = htonl(in.s_addr); 3055255332Scy nat->nat_ndstaddr = htonl(in.s_addr); 3056255332Scy nat->nat_odstip = fin->fin_dst; 3057255332Scy nat->nat_nsrcip = fin->fin_src; 3058255332Scy nat->nat_osrcip = fin->fin_src; 3059153876Sguido if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0)) 3060255332Scy nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src, 3061272998Scy fin->fin_dst, inb, (u_32_t)dport); 3062145522Sdarrenr 3063145522Sdarrenr if (flags & IPN_TCPUDP) { 3064255332Scy nat->nat_odport = dport; 3065255332Scy nat->nat_ndport = nport; 3066255332Scy nat->nat_osport = sport; 3067255332Scy nat->nat_nsport = sport; 3068145522Sdarrenr ((tcphdr_t *)fin->fin_dp)->th_dport = nport; 3069145522Sdarrenr } else if (flags & IPN_ICMPQUERY) { 3070255332Scy nat->nat_oicmpid = fin->fin_data[1]; 3071145522Sdarrenr ((icmphdr_t *)fin->fin_dp)->icmp_id = nport; 3072255332Scy nat->nat_nicmpid = nport; 3073145522Sdarrenr } 3074145522Sdarrenr 3075145522Sdarrenr return move; 3076145522Sdarrenr} 3077145522Sdarrenr 3078145522Sdarrenr/* ------------------------------------------------------------------------ */ 3079255332Scy/* Function: ipf_nat_add */ 3080145522Sdarrenr/* Returns: nat_t* - NULL == failure to create new NAT structure, */ 3081145522Sdarrenr/* else pointer to new NAT structure */ 3082145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3083145522Sdarrenr/* np(I) - pointer to NAT rule */ 3084145522Sdarrenr/* natsave(I) - pointer to where to store NAT struct pointer */ 3085145522Sdarrenr/* flags(I) - flags describing the current packet */ 3086145522Sdarrenr/* direction(I) - direction of packet (in/out) */ 3087145522Sdarrenr/* Write Lock: ipf_nat */ 3088145522Sdarrenr/* */ 3089145522Sdarrenr/* Attempts to create a new NAT entry. Does not actually change the packet */ 3090145522Sdarrenr/* in any way. */ 3091145522Sdarrenr/* */ 3092145522Sdarrenr/* This fucntion is in three main parts: (1) deal with creating a new NAT */ 3093145522Sdarrenr/* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */ 3094145522Sdarrenr/* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */ 3095145522Sdarrenr/* and (3) building that structure and putting it into the NAT table(s). */ 3096161356Sguido/* */ 3097161356Sguido/* NOTE: natsave should NOT be used top point back to an ipstate_t struct */ 3098161356Sguido/* as it can result in memory being corrupted. */ 3099145522Sdarrenr/* ------------------------------------------------------------------------ */ 3100255332Scynat_t * 3101255332Scyipf_nat_add(fin, np, natsave, flags, direction) 3102255332Scy fr_info_t *fin; 3103255332Scy ipnat_t *np; 3104255332Scy nat_t **natsave; 3105255332Scy u_int flags; 3106255332Scy int direction; 310753642Sguido{ 3108255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3109255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 311060852Sdarrenr hostmap_t *hm = NULL; 311160852Sdarrenr nat_t *nat, *natl; 3112255332Scy natstat_t *nsp; 3113145522Sdarrenr u_int nflags; 3114145522Sdarrenr natinfo_t ni; 3115145522Sdarrenr int move; 311653642Sguido 3117255332Scy nsp = &softn->ipf_nat_stats; 3118255332Scy 3119255332Scy if ((nsp->ns_active * 100 / softn->ipf_nat_table_max) > 3120255332Scy softn->ipf_nat_table_wm_high) { 3121255332Scy softn->ipf_nat_doflush = 1; 3122255332Scy } 3123255332Scy 3124255332Scy if (nsp->ns_active >= softn->ipf_nat_table_max) { 3125255332Scy NBUMPSIDED(fin->fin_out, ns_table_max); 3126130886Sdarrenr return NULL; 3127130886Sdarrenr } 3128130886Sdarrenr 3129145522Sdarrenr move = 1; 3130145522Sdarrenr nflags = np->in_flags & flags; 3131145522Sdarrenr nflags &= NAT_FROMRULE; 313253642Sguido 3133145522Sdarrenr ni.nai_np = np; 3134170268Sdarrenr ni.nai_dport = 0; 3135170268Sdarrenr ni.nai_sport = 0; 3136145522Sdarrenr 313753642Sguido /* Give me a new nat */ 313853642Sguido KMALLOC(nat, nat_t *); 313960852Sdarrenr if (nat == NULL) { 3140255332Scy NBUMPSIDED(fin->fin_out, ns_memfail); 3141130886Sdarrenr /* 3142130886Sdarrenr * Try to automatically tune the max # of entries in the 3143130886Sdarrenr * table allowed to be less than what will cause kmem_alloc() 3144130886Sdarrenr * to fail and try to eliminate panics due to out of memory 3145130886Sdarrenr * conditions arising. 3146130886Sdarrenr */ 3147255332Scy if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) && 3148255332Scy (nsp->ns_active > 100)) { 3149255332Scy softn->ipf_nat_table_max = nsp->ns_active - 100; 3150255332Scy printf("table_max reduced to %d\n", 3151255332Scy softn->ipf_nat_table_max); 3152130886Sdarrenr } 315353642Sguido return NULL; 315460852Sdarrenr } 315553642Sguido 3156255332Scy if (flags & IPN_ICMPQUERY) { 3157145522Sdarrenr /* 3158145522Sdarrenr * In the ICMP query NAT code, we translate the ICMP id fields 3159145522Sdarrenr * to make them unique. This is indepedent of the ICMP type 3160145522Sdarrenr * (e.g. in the unlikely event that a host sends an echo and 3161145522Sdarrenr * an tstamp request with the same id, both packets will have 3162145522Sdarrenr * their ip address/id field changed in the same way). 3163145522Sdarrenr */ 3164145522Sdarrenr /* The icmp_id field is used by the sender to identify the 3165145522Sdarrenr * process making the icmp request. (the receiver justs 3166145522Sdarrenr * copies it back in its response). So, it closely matches 3167145522Sdarrenr * the concept of source port. We overlay sport, so we can 3168145522Sdarrenr * maximally reuse the existing code. 3169145522Sdarrenr */ 3170255332Scy ni.nai_sport = fin->fin_data[1]; 3171255332Scy ni.nai_dport = 0; 3172145522Sdarrenr } 3173145522Sdarrenr 317453642Sguido bzero((char *)nat, sizeof(*nat)); 317553642Sguido nat->nat_flags = flags; 3176170268Sdarrenr nat->nat_redir = np->in_redir; 3177255332Scy nat->nat_dir = direction; 3178255332Scy nat->nat_pr[0] = fin->fin_p; 3179255332Scy nat->nat_pr[1] = fin->fin_p; 3180145522Sdarrenr 318153642Sguido /* 3182255332Scy * Search the current table for a match and create a new mapping 3183255332Scy * if there is none found. 318453642Sguido */ 3185255332Scy if (np->in_redir & NAT_DIVERTUDP) { 3186255332Scy move = ipf_nat_newdivert(fin, nat, &ni); 3187255332Scy 3188255332Scy } else if (np->in_redir & NAT_REWRITE) { 3189255332Scy move = ipf_nat_newrewrite(fin, nat, &ni); 3190255332Scy 3191255332Scy } else if (direction == NAT_OUTBOUND) { 319253642Sguido /* 3193145522Sdarrenr * We can now arrange to call this for the same connection 3194145522Sdarrenr * because ipf_nat_new doesn't protect the code path into 3195145522Sdarrenr * this function. 319653642Sguido */ 3197255332Scy natl = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p, 3198145522Sdarrenr fin->fin_src, fin->fin_dst); 3199145522Sdarrenr if (natl != NULL) { 3200161356Sguido KFREE(nat); 3201145522Sdarrenr nat = natl; 3202145522Sdarrenr goto done; 3203145522Sdarrenr } 320453642Sguido 3205255332Scy move = ipf_nat_newmap(fin, nat, &ni); 320653642Sguido } else { 320753642Sguido /* 3208255332Scy * NAT_INBOUND is used for redirects rules 320953642Sguido */ 3210255332Scy natl = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p, 3211255332Scy fin->fin_src, fin->fin_dst); 3212145522Sdarrenr if (natl != NULL) { 3213161356Sguido KFREE(nat); 3214145522Sdarrenr nat = natl; 3215145522Sdarrenr goto done; 321660852Sdarrenr } 321753642Sguido 3218255332Scy move = ipf_nat_newrdr(fin, nat, &ni); 3219145522Sdarrenr } 3220255332Scy if (move == -1) 3221255332Scy goto badnat; 322253642Sguido 3223255332Scy np = ni.nai_np; 3224255332Scy 3225255332Scy nat->nat_mssclamp = np->in_mssclamp; 3226255332Scy nat->nat_me = natsave; 3227255332Scy nat->nat_fr = fin->fin_fr; 3228255332Scy nat->nat_rev = fin->fin_rev; 3229255332Scy nat->nat_ptr = np; 3230255332Scy nat->nat_dlocal = np->in_dlocal; 3231255332Scy 3232255332Scy if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) { 3233255332Scy if (ipf_proxy_new(fin, nat) == -1) { 3234255332Scy NBUMPSIDED(fin->fin_out, ns_appr_fail); 3235255332Scy goto badnat; 323653642Sguido } 323753642Sguido } 323853642Sguido 3239255332Scy nat->nat_ifps[0] = np->in_ifps[0]; 3240255332Scy if (np->in_ifps[0] != NULL) { 3241255332Scy COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]); 3242145522Sdarrenr } 3243145522Sdarrenr 3244255332Scy nat->nat_ifps[1] = np->in_ifps[1]; 3245255332Scy if (np->in_ifps[1] != NULL) { 3246255332Scy COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]); 3247255332Scy } 324853642Sguido 3249255332Scy if (ipf_nat_finalise(fin, nat) == -1) { 3250255332Scy goto badnat; 3251255332Scy } 325253642Sguido 3253255332Scy np->in_use++; 325453642Sguido 3255255332Scy if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { 3256255332Scy if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) { 3257255332Scy ipf_nat_delrdr(softn, np); 3258255332Scy ipf_nat_addrdr(softn, np); 3259255332Scy } else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) { 3260255332Scy ipf_nat_delmap(softn, np); 3261255332Scy ipf_nat_addmap(softn, np); 3262145522Sdarrenr } 3263145522Sdarrenr } 326453642Sguido 3265145522Sdarrenr if (flags & SI_WILDP) 3266255332Scy nsp->ns_wilds++; 3267255332Scy nsp->ns_proto[nat->nat_pr[0]]++; 3268255332Scy 3269145522Sdarrenr goto done; 3270145522Sdarrenrbadnat: 3271255332Scy DT2(ns_badnatnew, fr_info_t *, fin, nat_t *, nat); 3272255332Scy NBUMPSIDE(fin->fin_out, ns_badnatnew); 3273145522Sdarrenr if ((hm = nat->nat_hm) != NULL) 3274255332Scy ipf_nat_hostmapdel(softc, &hm); 3275145522Sdarrenr KFREE(nat); 3276145522Sdarrenr nat = NULL; 3277145522Sdarrenrdone: 3278255332Scy if (nat != NULL && np != NULL) 3279255332Scy np->in_hits++; 3280255332Scy if (natsave != NULL) 3281255332Scy *natsave = nat; 3282145522Sdarrenr return nat; 3283145522Sdarrenr} 328460852Sdarrenr 328560852Sdarrenr 3286145522Sdarrenr/* ------------------------------------------------------------------------ */ 3287255332Scy/* Function: ipf_nat_finalise */ 3288145522Sdarrenr/* Returns: int - 0 == sucess, -1 == failure */ 3289145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3290145522Sdarrenr/* nat(I) - pointer to NAT entry */ 3291145522Sdarrenr/* Write Lock: ipf_nat */ 3292145522Sdarrenr/* */ 3293145522Sdarrenr/* This is the tail end of constructing a new NAT entry and is the same */ 3294145522Sdarrenr/* for both IPv4 and IPv6. */ 3295145522Sdarrenr/* ------------------------------------------------------------------------ */ 3296145522Sdarrenr/*ARGSUSED*/ 3297255332Scystatic int 3298255332Scyipf_nat_finalise(fin, nat) 3299255332Scy fr_info_t *fin; 3300255332Scy nat_t *nat; 3301145522Sdarrenr{ 3302255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3303255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 3304255332Scy u_32_t sum1, sum2, sumd; 3305145522Sdarrenr frentry_t *fr; 3306255332Scy u_32_t flags; 3307255332Scy#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC) 3308255332Scy qpktinfo_t *qpi = fin->fin_qpi; 3309255332Scy#endif 3310145522Sdarrenr 3311255332Scy flags = nat->nat_flags; 3312145522Sdarrenr 3313255332Scy switch (nat->nat_pr[0]) 3314255332Scy { 3315255332Scy case IPPROTO_ICMP : 3316255332Scy sum1 = LONG_SUM(ntohs(nat->nat_oicmpid)); 3317255332Scy sum2 = LONG_SUM(ntohs(nat->nat_nicmpid)); 3318255332Scy CALC_SUMD(sum1, sum2, sumd); 3319255332Scy nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 3320255332Scy 3321255332Scy break; 3322255332Scy 3323255332Scy default : 3324255332Scy sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr) + \ 3325255332Scy ntohs(nat->nat_osport)); 3326255332Scy sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr) + \ 3327255332Scy ntohs(nat->nat_nsport)); 3328255332Scy CALC_SUMD(sum1, sum2, sumd); 3329255332Scy nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 3330255332Scy 3331255332Scy sum1 = LONG_SUM(ntohl(nat->nat_odstaddr) + \ 3332255332Scy ntohs(nat->nat_odport)); 3333255332Scy sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr) + \ 3334255332Scy ntohs(nat->nat_ndport)); 3335255332Scy CALC_SUMD(sum1, sum2, sumd); 3336255332Scy nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); 3337255332Scy break; 3338161356Sguido } 3339255332Scy 3340255332Scy /* 3341255332Scy * Compute the partial checksum, just in case. 3342255332Scy * This is only ever placed into outbound packets so care needs 3343255332Scy * to be taken over which pair of addresses are used. 3344255332Scy */ 3345255332Scy if (nat->nat_dir == NAT_OUTBOUND) { 3346255332Scy sum1 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); 3347255332Scy sum1 += LONG_SUM(ntohl(nat->nat_ndstaddr)); 3348255332Scy } else { 3349255332Scy sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr)); 3350255332Scy sum1 += LONG_SUM(ntohl(nat->nat_odstaddr)); 3351161356Sguido } 3352255332Scy sum1 += nat->nat_pr[1]; 3353255332Scy nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16); 3354145522Sdarrenr 3355255332Scy sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr)); 3356255332Scy sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); 3357255332Scy CALC_SUMD(sum1, sum2, sumd); 3358255332Scy nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); 335992685Sdarrenr 3360255332Scy sum1 = LONG_SUM(ntohl(nat->nat_odstaddr)); 3361255332Scy sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr)); 3362255332Scy CALC_SUMD(sum1, sum2, sumd); 3363255332Scy nat->nat_ipsumd += (sumd & 0xffff) + (sumd >> 16); 336492685Sdarrenr 3365255332Scy nat->nat_v[0] = 4; 3366255332Scy nat->nat_v[1] = 4; 3367255332Scy 3368255332Scy if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { 3369255332Scy nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); 3370255332Scy } 3371255332Scy 3372255332Scy if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { 3373255332Scy nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); 3374255332Scy } 3375255332Scy 3376255332Scy if ((nat->nat_flags & SI_CLONE) == 0) 3377255332Scy nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat); 3378255332Scy 3379255332Scy if (ipf_nat_insert(softc, softn, nat) == 0) { 3380255332Scy if (softn->ipf_nat_logging) 3381255332Scy ipf_nat_log(softc, softn, nat, NL_NEW); 3382255332Scy fr = nat->nat_fr; 3383145522Sdarrenr if (fr != NULL) { 3384145522Sdarrenr MUTEX_ENTER(&fr->fr_lock); 3385145522Sdarrenr fr->fr_ref++; 3386145522Sdarrenr MUTEX_EXIT(&fr->fr_lock); 3387145522Sdarrenr } 3388145522Sdarrenr return 0; 3389145522Sdarrenr } 339092685Sdarrenr 3391255332Scy NBUMPSIDED(fin->fin_out, ns_unfinalised); 3392145522Sdarrenr /* 3393145522Sdarrenr * nat_insert failed, so cleanup time... 3394145522Sdarrenr */ 3395255332Scy if (nat->nat_sync != NULL) 3396255332Scy ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); 3397145522Sdarrenr return -1; 339853642Sguido} 339953642Sguido 340053642Sguido 3401145522Sdarrenr/* ------------------------------------------------------------------------ */ 3402255332Scy/* Function: ipf_nat_insert */ 3403255332Scy/* Returns: int - 0 == sucess, -1 == failure */ 3404255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 3405255332Scy/* softn(I) - pointer to NAT context structure */ 3406255332Scy/* nat(I) - pointer to NAT structure */ 3407255332Scy/* Write Lock: ipf_nat */ 3408145522Sdarrenr/* */ 3409145522Sdarrenr/* Insert a NAT entry into the hash tables for searching and add it to the */ 3410145522Sdarrenr/* list of active NAT entries. Adjust global counters when complete. */ 3411145522Sdarrenr/* ------------------------------------------------------------------------ */ 3412255332Scyint 3413255332Scyipf_nat_insert(softc, softn, nat) 3414255332Scy ipf_main_softc_t *softc; 3415255332Scy ipf_nat_softc_t *softn; 3416255332Scy nat_t *nat; 341760852Sdarrenr{ 3418255332Scy u_int hv0, hv1; 3419255332Scy u_int sp, dp; 3420255332Scy ipnat_t *in; 342160852Sdarrenr 3422145522Sdarrenr /* 3423145522Sdarrenr * Try and return an error as early as possible, so calculate the hash 3424145522Sdarrenr * entry numbers first and then proceed. 3425145522Sdarrenr */ 3426145522Sdarrenr if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { 3427255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 3428255332Scy sp = nat->nat_osport; 3429255332Scy dp = nat->nat_odport; 3430255332Scy } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { 3431255332Scy sp = 0; 3432255332Scy dp = nat->nat_oicmpid; 3433255332Scy } else { 3434255332Scy sp = 0; 3435255332Scy dp = 0; 3436255332Scy } 3437255332Scy hv0 = NAT_HASH_FN(nat->nat_osrcaddr, sp, 0xffffffff); 3438255332Scy hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0 + dp, 0xffffffff); 3439255332Scy /* 3440255332Scy * TRACE nat_osrcaddr, nat_osport, nat_odstaddr, 3441255332Scy * nat_odport, hv0 3442255332Scy */ 3443255332Scy 3444255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 3445255332Scy sp = nat->nat_nsport; 3446255332Scy dp = nat->nat_ndport; 3447255332Scy } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { 3448255332Scy sp = 0; 3449255332Scy dp = nat->nat_nicmpid; 3450255332Scy } else { 3451255332Scy sp = 0; 3452255332Scy dp = 0; 3453255332Scy } 3454255332Scy hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, sp, 0xffffffff); 3455255332Scy hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1 + dp, 0xffffffff); 3456255332Scy /* 3457255332Scy * TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, 3458255332Scy * nat_ndport, hv1 3459255332Scy */ 346080482Sdarrenr } else { 3461255332Scy hv0 = NAT_HASH_FN(nat->nat_osrcaddr, 0, 0xffffffff); 3462255332Scy hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0, 0xffffffff); 3463255332Scy /* TRACE nat_osrcaddr, nat_odstaddr, hv0 */ 346480482Sdarrenr 3465255332Scy hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, 0, 0xffffffff); 3466255332Scy hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1, 0xffffffff); 3467255332Scy /* TRACE nat_nsrcaddr, nat_ndstaddr, hv1 */ 3468145522Sdarrenr } 3469145522Sdarrenr 3470255332Scy nat->nat_hv[0] = hv0; 3471255332Scy nat->nat_hv[1] = hv1; 3472145522Sdarrenr 3473145522Sdarrenr MUTEX_INIT(&nat->nat_lock, "nat entry lock"); 3474145522Sdarrenr 3475255332Scy in = nat->nat_ptr; 3476255332Scy nat->nat_ref = nat->nat_me ? 2 : 1; 3477145522Sdarrenr 3478145522Sdarrenr nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; 3479255332Scy nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0], 4); 3480145522Sdarrenr 3481161356Sguido if (nat->nat_ifnames[1][0] != '\0') { 3482145522Sdarrenr nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 3483255332Scy nat->nat_ifps[1] = ipf_resolvenic(softc, 3484255332Scy nat->nat_ifnames[1], 4); 3485255332Scy } else if (in->in_ifnames[1] != -1) { 3486255332Scy char *name; 3487255332Scy 3488255332Scy name = in->in_names + in->in_ifnames[1]; 3489255332Scy if (name[1] != '\0' && name[0] != '-' && name[0] != '*') { 3490255332Scy (void) strncpy(nat->nat_ifnames[1], 3491255332Scy nat->nat_ifnames[0], LIFNAMSIZ); 3492255332Scy nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 3493255332Scy nat->nat_ifps[1] = nat->nat_ifps[0]; 3494255332Scy } 3495145522Sdarrenr } 3496255332Scy if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { 3497255332Scy nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); 3498255332Scy } 3499255332Scy if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { 3500255332Scy nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); 3501255332Scy } 3502145522Sdarrenr 3503255332Scy return ipf_nat_hashtab_add(softc, softn, nat); 3504255332Scy} 3505145522Sdarrenr 3506255332Scy 3507255332Scy/* ------------------------------------------------------------------------ */ 3508255332Scy/* Function: ipf_nat_hashtab_add */ 3509255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 3510255332Scy/* softn(I) - pointer to NAT context structure */ 3511255332Scy/* nat(I) - pointer to NAT structure */ 3512255332Scy/* */ 3513255332Scy/* Handle the insertion of a NAT entry into the table/list. */ 3514255332Scy/* ------------------------------------------------------------------------ */ 3515255332Scyint 3516255332Scyipf_nat_hashtab_add(softc, softn, nat) 3517255332Scy ipf_main_softc_t *softc; 3518255332Scy ipf_nat_softc_t *softn; 3519255332Scy nat_t *nat; 3520255332Scy{ 3521255332Scy nat_t **natp; 3522255332Scy u_int hv0; 3523255332Scy u_int hv1; 3524255332Scy 3525255332Scy hv0 = nat->nat_hv[0] % softn->ipf_nat_table_sz; 3526255332Scy hv1 = nat->nat_hv[1] % softn->ipf_nat_table_sz; 3527255332Scy 3528255332Scy if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) { 3529255332Scy u_int swap; 3530255332Scy 3531255332Scy swap = hv0; 3532255332Scy hv0 = hv1; 3533255332Scy hv1 = swap; 3534255332Scy } 3535255332Scy 3536255332Scy if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0] >= 3537255332Scy softn->ipf_nat_maxbucket) { 3538255332Scy DT1(ns_bucket_max_0, int, 3539255332Scy softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]); 3540255332Scy NBUMPSIDE(0, ns_bucket_max); 3541255332Scy return -1; 3542255332Scy } 3543255332Scy 3544255332Scy if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1] >= 3545255332Scy softn->ipf_nat_maxbucket) { 3546255332Scy DT1(ns_bucket_max_1, int, 3547255332Scy softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]); 3548255332Scy NBUMPSIDE(1, ns_bucket_max); 3549255332Scy return -1; 3550255332Scy } 3551255332Scy 3552255332Scy /* 3553255332Scy * The ordering of operations in the list and hash table insertion 3554255332Scy * is very important. The last operation for each task should be 3555255332Scy * to update the top of the list, after all the "nexts" have been 3556255332Scy * done so that walking the list while it is being done does not 3557255332Scy * find strange pointers. 3558255332Scy * 3559255332Scy * Global list of NAT instances 3560255332Scy */ 3561255332Scy nat->nat_next = softn->ipf_nat_instances; 3562255332Scy nat->nat_pnext = &softn->ipf_nat_instances; 3563255332Scy if (softn->ipf_nat_instances) 3564255332Scy softn->ipf_nat_instances->nat_pnext = &nat->nat_next; 3565255332Scy softn->ipf_nat_instances = nat; 3566255332Scy 3567255332Scy /* 3568255332Scy * Inbound hash table. 3569255332Scy */ 3570255332Scy natp = &softn->ipf_nat_table[0][hv0]; 357167614Sdarrenr nat->nat_phnext[0] = natp; 357260852Sdarrenr nat->nat_hnext[0] = *natp; 3573255332Scy if (*natp) { 3574255332Scy (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 3575255332Scy } else { 3576255332Scy NBUMPSIDE(0, ns_inuse); 3577255332Scy } 357860852Sdarrenr *natp = nat; 3579255332Scy NBUMPSIDE(0, ns_bucketlen[hv0]); 358067614Sdarrenr 3581255332Scy /* 3582255332Scy * Outbound hash table. 3583255332Scy */ 3584255332Scy natp = &softn->ipf_nat_table[1][hv1]; 3585255332Scy nat->nat_phnext[1] = natp; 3586255332Scy nat->nat_hnext[1] = *natp; 358767614Sdarrenr if (*natp) 358867614Sdarrenr (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 3589255332Scy else { 3590255332Scy NBUMPSIDE(1, ns_inuse); 3591255332Scy } 359260852Sdarrenr *natp = nat; 3593255332Scy NBUMPSIDE(1, ns_bucketlen[hv1]); 359460852Sdarrenr 3595255332Scy ipf_nat_setqueue(softc, softn, nat); 3596145522Sdarrenr 3597255332Scy if (nat->nat_dir & NAT_OUTBOUND) { 3598255332Scy NBUMPSIDE(1, ns_added); 3599255332Scy } else { 3600255332Scy NBUMPSIDE(0, ns_added); 3601255332Scy } 3602255332Scy softn->ipf_nat_stats.ns_active++; 3603145522Sdarrenr return 0; 360460852Sdarrenr} 360560852Sdarrenr 360660852Sdarrenr 3607145522Sdarrenr/* ------------------------------------------------------------------------ */ 3608255332Scy/* Function: ipf_nat_icmperrorlookup */ 3609145522Sdarrenr/* Returns: nat_t* - point to matching NAT structure */ 3610145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3611145522Sdarrenr/* dir(I) - direction of packet (in/out) */ 3612145522Sdarrenr/* */ 3613145522Sdarrenr/* Check if the ICMP error message is related to an existing TCP, UDP or */ 3614145522Sdarrenr/* ICMP query nat entry. It is assumed that the packet is already of the */ 3615145522Sdarrenr/* the required length. */ 3616145522Sdarrenr/* ------------------------------------------------------------------------ */ 3617255332Scynat_t * 3618255332Scyipf_nat_icmperrorlookup(fin, dir) 3619255332Scy fr_info_t *fin; 3620255332Scy int dir; 362153642Sguido{ 3622255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3623255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 3624145522Sdarrenr int flags = 0, type, minlen; 3625145522Sdarrenr icmphdr_t *icmp, *orgicmp; 3626255332Scy nat_stat_side_t *nside; 362753642Sguido tcphdr_t *tcp = NULL; 3628145522Sdarrenr u_short data[2]; 3629145522Sdarrenr nat_t *nat; 363053642Sguido ip_t *oip; 3631145522Sdarrenr u_int p; 363253642Sguido 3633145522Sdarrenr icmp = fin->fin_dp; 3634145522Sdarrenr type = icmp->icmp_type; 3635255332Scy nside = &softn->ipf_nat_stats.ns_side[fin->fin_out]; 363653642Sguido /* 363753642Sguido * Does it at least have the return (basic) IP header ? 363853642Sguido * Only a basic IP header (no options) should be with an ICMP error 3639145522Sdarrenr * header. Also, if it's not an error type, then return. 364053642Sguido */ 3641255332Scy if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) { 3642255332Scy ATOMIC_INCL(nside->ns_icmp_basic); 364353642Sguido return NULL; 3644255332Scy } 3645145522Sdarrenr 364653642Sguido /* 3647145522Sdarrenr * Check packet size 364853642Sguido */ 364953642Sguido oip = (ip_t *)((char *)fin->fin_dp + 8); 3650145522Sdarrenr minlen = IP_HL(oip) << 2; 3651145522Sdarrenr if ((minlen < sizeof(ip_t)) || 3652255332Scy (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) { 3653255332Scy ATOMIC_INCL(nside->ns_icmp_size); 365453642Sguido return NULL; 3655255332Scy } 3656255332Scy 365764580Sdarrenr /* 365864580Sdarrenr * Is the buffer big enough for all of it ? It's the size of the IP 365964580Sdarrenr * header claimed in the encapsulated part which is of concern. It 366064580Sdarrenr * may be too big to be in this buffer but not so big that it's 366164580Sdarrenr * outside the ICMP packet, leading to TCP deref's causing problems. 366264580Sdarrenr * This is possible because we don't know how big oip_hl is when we 3663255332Scy * do the pullup early in ipf_check() and thus can't gaurantee it is 366464580Sdarrenr * all here now. 366564580Sdarrenr */ 3666255332Scy#ifdef ipf_nat_KERNEL 366764580Sdarrenr { 366864580Sdarrenr mb_t *m; 366964580Sdarrenr 3670145522Sdarrenr m = fin->fin_m; 3671145522Sdarrenr# if defined(MENTAT) 3672255332Scy if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > 3673255332Scy (char *)m->b_wptr) { 3674255332Scy ATOMIC_INCL(nside->ns_icmp_mbuf); 367564580Sdarrenr return NULL; 3676255332Scy } 367764580Sdarrenr# else 367864580Sdarrenr if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > 3679255332Scy (char *)fin->fin_ip + M_LEN(m)) { 3680255332Scy ATOMIC_INCL(nside->ns_icmp_mbuf); 368164580Sdarrenr return NULL; 3682255332Scy } 368364580Sdarrenr# endif 368464580Sdarrenr } 368564580Sdarrenr#endif 368664580Sdarrenr 3687255332Scy if (fin->fin_daddr != oip->ip_src.s_addr) { 3688255332Scy ATOMIC_INCL(nside->ns_icmp_address); 3689145522Sdarrenr return NULL; 3690255332Scy } 3691145522Sdarrenr 3692145522Sdarrenr p = oip->ip_p; 3693145522Sdarrenr if (p == IPPROTO_TCP) 369453642Sguido flags = IPN_TCP; 3695145522Sdarrenr else if (p == IPPROTO_UDP) 369653642Sguido flags = IPN_UDP; 3697145522Sdarrenr else if (p == IPPROTO_ICMP) { 3698145522Sdarrenr orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 3699145522Sdarrenr 3700145522Sdarrenr /* see if this is related to an ICMP query */ 3701255332Scy if (ipf_nat_icmpquerytype(orgicmp->icmp_type)) { 3702145522Sdarrenr data[0] = fin->fin_data[0]; 3703145522Sdarrenr data[1] = fin->fin_data[1]; 3704145522Sdarrenr fin->fin_data[0] = 0; 3705145522Sdarrenr fin->fin_data[1] = orgicmp->icmp_id; 3706145522Sdarrenr 3707145522Sdarrenr flags = IPN_ICMPERR|IPN_ICMPQUERY; 3708145522Sdarrenr /* 3709145522Sdarrenr * NOTE : dir refers to the direction of the original 3710145522Sdarrenr * ip packet. By definition the icmp error 3711145522Sdarrenr * message flows in the opposite direction. 3712145522Sdarrenr */ 3713145522Sdarrenr if (dir == NAT_INBOUND) 3714255332Scy nat = ipf_nat_inlookup(fin, flags, p, 3715255332Scy oip->ip_dst, 3716255332Scy oip->ip_src); 3717145522Sdarrenr else 3718255332Scy nat = ipf_nat_outlookup(fin, flags, p, 3719255332Scy oip->ip_dst, 3720255332Scy oip->ip_src); 3721145522Sdarrenr fin->fin_data[0] = data[0]; 3722145522Sdarrenr fin->fin_data[1] = data[1]; 3723145522Sdarrenr return nat; 3724145522Sdarrenr } 3725145522Sdarrenr } 3726255332Scy 372753642Sguido if (flags & IPN_TCPUDP) { 372864580Sdarrenr minlen += 8; /* + 64bits of data to get ports */ 3729255332Scy /* TRACE (fin,minlen) */ 3730255332Scy if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) { 3731255332Scy ATOMIC_INCL(nside->ns_icmp_short); 373264580Sdarrenr return NULL; 3733255332Scy } 373492685Sdarrenr 373592685Sdarrenr data[0] = fin->fin_data[0]; 373692685Sdarrenr data[1] = fin->fin_data[1]; 3737145522Sdarrenr tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 373892685Sdarrenr fin->fin_data[0] = ntohs(tcp->th_dport); 373992685Sdarrenr fin->fin_data[1] = ntohs(tcp->th_sport); 374092685Sdarrenr 374192685Sdarrenr if (dir == NAT_INBOUND) { 3742255332Scy nat = ipf_nat_inlookup(fin, flags, p, oip->ip_dst, 3743255332Scy oip->ip_src); 374492685Sdarrenr } else { 3745255332Scy nat = ipf_nat_outlookup(fin, flags, p, oip->ip_dst, 3746145522Sdarrenr oip->ip_src); 374792685Sdarrenr } 374892685Sdarrenr fin->fin_data[0] = data[0]; 374992685Sdarrenr fin->fin_data[1] = data[1]; 375092685Sdarrenr return nat; 375153642Sguido } 375260852Sdarrenr if (dir == NAT_INBOUND) 3753255332Scy nat = ipf_nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src); 375460852Sdarrenr else 3755255332Scy nat = ipf_nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src); 3756255332Scy 3757255332Scy return nat; 375853642Sguido} 375953642Sguido 376053642Sguido 3761145522Sdarrenr/* ------------------------------------------------------------------------ */ 3762255332Scy/* Function: ipf_nat_icmperror */ 3763145522Sdarrenr/* Returns: nat_t* - point to matching NAT structure */ 3764145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3765145522Sdarrenr/* nflags(I) - NAT flags for this packet */ 3766145522Sdarrenr/* dir(I) - direction of packet (in/out) */ 3767145522Sdarrenr/* */ 3768145522Sdarrenr/* Fix up an ICMP packet which is an error message for an existing NAT */ 3769145522Sdarrenr/* session. This will correct both packet header data and checksums. */ 3770145522Sdarrenr/* */ 3771145522Sdarrenr/* This should *ONLY* be used for incoming ICMP error packets to make sure */ 3772145522Sdarrenr/* a NAT'd ICMP packet gets correctly recognised. */ 3773145522Sdarrenr/* ------------------------------------------------------------------------ */ 3774255332Scynat_t * 3775255332Scyipf_nat_icmperror(fin, nflags, dir) 3776255332Scy fr_info_t *fin; 3777255332Scy u_int *nflags; 3778255332Scy int dir; 377953642Sguido{ 3780255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3781255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 3782145522Sdarrenr u_32_t sum1, sum2, sumd, sumd2; 3783255332Scy struct in_addr a1, a2, a3, a4; 3784170268Sdarrenr int flags, dlen, odst; 3785145522Sdarrenr icmphdr_t *icmp; 3786145522Sdarrenr u_short *csump; 378795418Sdarrenr tcphdr_t *tcp; 378853642Sguido nat_t *nat; 378953642Sguido ip_t *oip; 3790145522Sdarrenr void *dp; 379153642Sguido 3792255332Scy if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { 3793255332Scy NBUMPSIDED(fin->fin_out, ns_icmp_short); 379463523Sdarrenr return NULL; 3795255332Scy } 3796255332Scy 379767614Sdarrenr /* 3798255332Scy * ipf_nat_icmperrorlookup() will return NULL for `defective' packets. 379967614Sdarrenr */ 3800255332Scy if ((fin->fin_v != 4) || !(nat = ipf_nat_icmperrorlookup(fin, dir))) { 3801255332Scy NBUMPSIDED(fin->fin_out, ns_icmp_notfound); 380253642Sguido return NULL; 3803255332Scy } 380492685Sdarrenr 3805145522Sdarrenr tcp = NULL; 3806145522Sdarrenr csump = NULL; 380792685Sdarrenr flags = 0; 3808130886Sdarrenr sumd2 = 0; 380953642Sguido *nflags = IPN_ICMPERR; 3810145522Sdarrenr icmp = fin->fin_dp; 381153642Sguido oip = (ip_t *)&icmp->icmp_ip; 3812145522Sdarrenr dp = (((char *)oip) + (IP_HL(oip) << 2)); 3813145522Sdarrenr if (oip->ip_p == IPPROTO_TCP) { 3814145522Sdarrenr tcp = (tcphdr_t *)dp; 3815145522Sdarrenr csump = (u_short *)&tcp->th_sum; 381653642Sguido flags = IPN_TCP; 3817145522Sdarrenr } else if (oip->ip_p == IPPROTO_UDP) { 3818145522Sdarrenr udphdr_t *udp; 3819145522Sdarrenr 3820145522Sdarrenr udp = (udphdr_t *)dp; 3821145522Sdarrenr tcp = (tcphdr_t *)dp; 3822145522Sdarrenr csump = (u_short *)&udp->uh_sum; 382353642Sguido flags = IPN_UDP; 3824145522Sdarrenr } else if (oip->ip_p == IPPROTO_ICMP) 3825145522Sdarrenr flags = IPN_ICMPQUERY; 3826145522Sdarrenr dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip); 382795418Sdarrenr 382895418Sdarrenr /* 382953642Sguido * Need to adjust ICMP header to include the real IP#'s and 383053642Sguido * port #'s. Only apply a checksum change relative to the 3831255332Scy * IP address change as it will be modified again in ipf_nat_checkout 383253642Sguido * for both address and port. Two checksum changes are 383353642Sguido * necessary for the two header address changes. Be careful 383453642Sguido * to only modify the checksum once for the port # and twice 383553642Sguido * for the IP#. 383653642Sguido */ 383760852Sdarrenr 383867614Sdarrenr /* 383967614Sdarrenr * Step 1 384067614Sdarrenr * Fix the IP addresses in the offending IP packet. You also need 3841170268Sdarrenr * to adjust the IP header checksum of that offending IP packet. 384267614Sdarrenr * 3843145522Sdarrenr * Normally, you would expect that the ICMP checksum of the 3844130886Sdarrenr * ICMP error message needs to be adjusted as well for the 3845130886Sdarrenr * IP address change in oip. 3846145522Sdarrenr * However, this is a NOP, because the ICMP checksum is 3847130886Sdarrenr * calculated over the complete ICMP packet, which includes the 3848145522Sdarrenr * changed oip IP addresses and oip->ip_sum. However, these 3849130886Sdarrenr * two changes cancel each other out (if the delta for 3850145522Sdarrenr * the IP address is x, then the delta for ip_sum is minus x), 3851130886Sdarrenr * so no change in the icmp_cksum is necessary. 3852130886Sdarrenr * 3853170268Sdarrenr * Inbound ICMP 3854170268Sdarrenr * ------------ 3855170268Sdarrenr * MAP rule, SRC=a,DST=b -> SRC=c,DST=b 3856170268Sdarrenr * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b) 3857255332Scy * - OIP_SRC(c)=nat_newsrcip, OIP_DST(b)=nat_newdstip 3858255332Scy *=> OIP_SRC(c)=nat_oldsrcip, OIP_DST(b)=nat_olddstip 3859170268Sdarrenr * 3860170268Sdarrenr * RDR rule, SRC=a,DST=b -> SRC=a,DST=c 3861170268Sdarrenr * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a) 3862255332Scy * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip 3863255332Scy *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip 3864170268Sdarrenr * 3865255332Scy * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d 3866255332Scy * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d) 3867255332Scy * - OIP_SRC(c)=nat_newsrcip, OIP_DST(d)=nat_newdstip 3868255332Scy *=> OIP_SRC(c)=nat_oldsrcip, OIP_DST(d)=nat_olddstip 3869255332Scy * 3870255332Scy * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d 3871255332Scy * - response to outgoing packet (d,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) 3872255332Scy * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip 3873255332Scy *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip 3874255332Scy * 3875170268Sdarrenr * Outbound ICMP 3876170268Sdarrenr * ------------- 3877170268Sdarrenr * MAP rule, SRC=a,DST=b -> SRC=c,DST=b 3878170268Sdarrenr * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) 3879255332Scy * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip 3880255332Scy *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip 3881170268Sdarrenr * 3882170268Sdarrenr * RDR rule, SRC=a,DST=b -> SRC=a,DST=c 3883170268Sdarrenr * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c) 3884255332Scy * - OIP_SRC(a)=nat_newsrcip, OIP_DST(c)=nat_newdstip 3885255332Scy *=> OIP_SRC(a)=nat_oldsrcip, OIP_DST(c)=nat_olddstip 3886170268Sdarrenr * 3887255332Scy * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d 3888255332Scy * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d) 3889255332Scy * - OIP_SRC(c)=nat_olddstip, OIP_DST(d)=nat_oldsrcip 3890255332Scy *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip 3891255332Scy * 3892255332Scy * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d 3893255332Scy * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a) 3894255332Scy * - OIP_SRC(b)=nat_newsrcip, OIP_DST(a)=nat_newdstip 3895255332Scy *=> OIP_SRC(a)=nat_oldsrcip, OIP_DST(c)=nat_olddstip 3896130886Sdarrenr */ 3897255332Scy 3898255332Scy if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) || 3899255332Scy ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) { 3900255332Scy a1.s_addr = ntohl(nat->nat_osrcaddr); 3901255332Scy a4.s_addr = ntohl(oip->ip_src.s_addr); 3902255332Scy a3.s_addr = ntohl(nat->nat_odstaddr); 3903255332Scy a2.s_addr = ntohl(oip->ip_dst.s_addr); 3904170268Sdarrenr oip->ip_src.s_addr = htonl(a1.s_addr); 3905255332Scy oip->ip_dst.s_addr = htonl(a3.s_addr); 3906255332Scy odst = 1; 3907170268Sdarrenr } else { 3908255332Scy a1.s_addr = ntohl(nat->nat_ndstaddr); 3909170268Sdarrenr a2.s_addr = ntohl(oip->ip_dst.s_addr); 3910255332Scy a3.s_addr = ntohl(nat->nat_nsrcaddr); 3911255332Scy a4.s_addr = ntohl(oip->ip_src.s_addr); 3912255332Scy oip->ip_dst.s_addr = htonl(a3.s_addr); 3913255332Scy oip->ip_src.s_addr = htonl(a1.s_addr); 3914255332Scy odst = 0; 3915170268Sdarrenr } 3916255332Scy sum1 = 0; 3917255332Scy sum2 = 0; 3918255332Scy sumd = 0; 3919255332Scy CALC_SUMD(a2.s_addr, a3.s_addr, sum1); 3920255332Scy CALC_SUMD(a4.s_addr, a1.s_addr, sum2); 3921255332Scy sumd = sum2 + sum1; 3922255332Scy if (sumd != 0) 3923255332Scy ipf_fix_datacksum(&oip->ip_sum, sumd); 3924130886Sdarrenr 3925170268Sdarrenr sumd2 = sumd; 3926170268Sdarrenr sum1 = 0; 3927170268Sdarrenr sum2 = 0; 3928170268Sdarrenr 3929130886Sdarrenr /* 3930170268Sdarrenr * Fix UDP pseudo header checksum to compensate for the 3931170268Sdarrenr * IP address change. 3932130886Sdarrenr */ 3933130886Sdarrenr if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) { 3934255332Scy u_32_t sum3, sum4, sumt; 3935255332Scy 393664580Sdarrenr /* 393767614Sdarrenr * Step 2 : 393867614Sdarrenr * For offending TCP/UDP IP packets, translate the ports as 393967614Sdarrenr * well, based on the NAT specification. Of course such 3940170268Sdarrenr * a change may be reflected in the ICMP checksum as well. 394167614Sdarrenr * 394267614Sdarrenr * Since the port fields are part of the TCP/UDP checksum 394367614Sdarrenr * of the offending IP packet, you need to adjust that checksum 3944255332Scy * as well... except that the change in the port numbers should 3945170268Sdarrenr * be offset by the checksum change. However, the TCP/UDP 3946170268Sdarrenr * checksum will also need to change if there has been an 3947170268Sdarrenr * IP address change. 394867614Sdarrenr */ 3949170268Sdarrenr if (odst == 1) { 3950255332Scy sum1 = ntohs(nat->nat_osport); 3951255332Scy sum4 = ntohs(tcp->th_sport); 3952255332Scy sum3 = ntohs(nat->nat_odport); 3953255332Scy sum2 = ntohs(tcp->th_dport); 3954145522Sdarrenr 3955170268Sdarrenr tcp->th_sport = htons(sum1); 3956255332Scy tcp->th_dport = htons(sum3); 3957170268Sdarrenr } else { 3958255332Scy sum1 = ntohs(nat->nat_ndport); 3959145522Sdarrenr sum2 = ntohs(tcp->th_dport); 3960255332Scy sum3 = ntohs(nat->nat_nsport); 3961255332Scy sum4 = ntohs(tcp->th_sport); 3962170268Sdarrenr 3963255332Scy tcp->th_dport = htons(sum3); 3964255332Scy tcp->th_sport = htons(sum1); 3965145522Sdarrenr } 3966255332Scy CALC_SUMD(sum4, sum1, sumt); 3967255332Scy sumd += sumt; 3968255332Scy CALC_SUMD(sum2, sum3, sumt); 3969255332Scy sumd += sumt; 397067614Sdarrenr 3971170268Sdarrenr if (sumd != 0 || sumd2 != 0) { 3972145522Sdarrenr /* 3973170268Sdarrenr * At this point, sumd is the delta to apply to the 3974170268Sdarrenr * TCP/UDP header, given the changes in both the IP 3975170268Sdarrenr * address and the ports and sumd2 is the delta to 3976170268Sdarrenr * apply to the ICMP header, given the IP address 3977170268Sdarrenr * change delta that may need to be applied to the 3978170268Sdarrenr * TCP/UDP checksum instead. 3979145522Sdarrenr * 3980170268Sdarrenr * If we will both the IP and TCP/UDP checksums 3981170268Sdarrenr * then the ICMP checksum changes by the address 3982170268Sdarrenr * delta applied to the TCP/UDP checksum. If we 3983170268Sdarrenr * do not change the TCP/UDP checksum them we 3984170268Sdarrenr * apply the delta in ports to the ICMP checksum. 3985145522Sdarrenr */ 3986161356Sguido if (oip->ip_p == IPPROTO_UDP) { 3987161356Sguido if ((dlen >= 8) && (*csump != 0)) { 3988255332Scy ipf_fix_datacksum(csump, sumd); 3989161356Sguido } else { 3990255332Scy CALC_SUMD(sum1, sum4, sumd2); 3991255332Scy CALC_SUMD(sum3, sum2, sumt); 3992255332Scy sumd2 += sumt; 3993161356Sguido } 3994170268Sdarrenr } else if (oip->ip_p == IPPROTO_TCP) { 3995145522Sdarrenr if (dlen >= 18) { 3996255332Scy ipf_fix_datacksum(csump, sumd); 3997145522Sdarrenr } else { 3998255332Scy CALC_SUMD(sum1, sum4, sumd2); 3999255332Scy CALC_SUMD(sum3, sum2, sumt); 4000255332Scy sumd2 += sumt; 400167614Sdarrenr } 400253642Sguido } 4003170268Sdarrenr if (sumd2 != 0) { 4004170268Sdarrenr sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 4005170268Sdarrenr sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 4006170268Sdarrenr sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 4007255332Scy ipf_fix_incksum(0, &icmp->icmp_cksum, sumd2, 0); 4008130886Sdarrenr } 400953642Sguido } 4010145522Sdarrenr } else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) { 4011145522Sdarrenr icmphdr_t *orgicmp; 4012145522Sdarrenr 4013145522Sdarrenr /* 4014145522Sdarrenr * XXX - what if this is bogus hl and we go off the end ? 4015255332Scy * In this case, ipf_nat_icmperrorlookup() will have 4016255332Scy * returned NULL. 4017145522Sdarrenr */ 4018145522Sdarrenr orgicmp = (icmphdr_t *)dp; 4019145522Sdarrenr 4020170268Sdarrenr if (odst == 1) { 4021255332Scy if (orgicmp->icmp_id != nat->nat_osport) { 4022145522Sdarrenr 4023145522Sdarrenr /* 4024145522Sdarrenr * Fix ICMP checksum (of the offening ICMP 4025145522Sdarrenr * query packet) to compensate the change 4026145522Sdarrenr * in the ICMP id of the offending ICMP 4027145522Sdarrenr * packet. 4028145522Sdarrenr * 4029145522Sdarrenr * Since you modify orgicmp->icmp_id with 4030145522Sdarrenr * a delta (say x) and you compensate that 4031145522Sdarrenr * in origicmp->icmp_cksum with a delta 4032145522Sdarrenr * minus x, you don't have to adjust the 4033145522Sdarrenr * overall icmp->icmp_cksum 4034145522Sdarrenr */ 4035145522Sdarrenr sum1 = ntohs(orgicmp->icmp_id); 4036255332Scy sum2 = ntohs(nat->nat_oicmpid); 4037145522Sdarrenr CALC_SUMD(sum1, sum2, sumd); 4038255332Scy orgicmp->icmp_id = nat->nat_oicmpid; 4039255332Scy ipf_fix_datacksum(&orgicmp->icmp_cksum, sumd); 4040145522Sdarrenr } 4041145522Sdarrenr } /* nat_dir == NAT_INBOUND is impossible for icmp queries */ 404253642Sguido } 404353642Sguido return nat; 404453642Sguido} 404553642Sguido 404653642Sguido 404753642Sguido/* 4048255332Scy * MAP-IN MAP-OUT RDR-IN RDR-OUT 4049255332Scy * osrc X == src == src X 4050255332Scy * odst X == dst == dst X 4051255332Scy * nsrc == dst X X == dst 4052255332Scy * ndst == src X X == src 4053255332Scy * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND 4054255332Scy */ 4055255332Scy/* 4056145522Sdarrenr * NB: these lookups don't lock access to the list, it assumed that it has 4057145522Sdarrenr * already been done! 405853642Sguido */ 4059145522Sdarrenr/* ------------------------------------------------------------------------ */ 4060255332Scy/* Function: ipf_nat_inlookup */ 4061145522Sdarrenr/* Returns: nat_t* - NULL == no match, */ 4062145522Sdarrenr/* else pointer to matching NAT entry */ 4063145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4064145522Sdarrenr/* flags(I) - NAT flags for this packet */ 4065145522Sdarrenr/* p(I) - protocol for this packet */ 4066145522Sdarrenr/* src(I) - source IP address */ 4067145522Sdarrenr/* mapdst(I) - destination IP address */ 4068145522Sdarrenr/* */ 4069145522Sdarrenr/* Lookup a nat entry based on the mapped destination ip address/port and */ 4070145522Sdarrenr/* real source address/port. We use this lookup when receiving a packet, */ 4071145522Sdarrenr/* we're looking for a table entry, based on the destination address. */ 4072145522Sdarrenr/* */ 4073145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 4074145522Sdarrenr/* */ 4075255332Scy/* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ 4076145522Sdarrenr/* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 4077145522Sdarrenr/* */ 4078145522Sdarrenr/* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 4079145522Sdarrenr/* the packet is of said protocol */ 4080145522Sdarrenr/* ------------------------------------------------------------------------ */ 4081255332Scynat_t * 4082255332Scyipf_nat_inlookup(fin, flags, p, src, mapdst) 4083255332Scy fr_info_t *fin; 4084255332Scy u_int flags, p; 4085255332Scy struct in_addr src , mapdst; 408653642Sguido{ 4087255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 4088255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 4089145522Sdarrenr u_short sport, dport; 4090145522Sdarrenr grehdr_t *gre; 409192685Sdarrenr ipnat_t *ipn; 4092145522Sdarrenr u_int sflags; 4093145522Sdarrenr nat_t *nat; 4094145522Sdarrenr int nflags; 4095145522Sdarrenr u_32_t dst; 409692685Sdarrenr void *ifp; 4097255332Scy u_int hv, rhv; 409853642Sguido 4099161356Sguido ifp = fin->fin_ifp; 4100145522Sdarrenr gre = NULL; 410167614Sdarrenr dst = mapdst.s_addr; 4102145522Sdarrenr sflags = flags & NAT_TCPUDPICMP; 4103145522Sdarrenr 4104145522Sdarrenr switch (p) 4105145522Sdarrenr { 4106145522Sdarrenr case IPPROTO_TCP : 4107145522Sdarrenr case IPPROTO_UDP : 410892685Sdarrenr sport = htons(fin->fin_data[0]); 410992685Sdarrenr dport = htons(fin->fin_data[1]); 4110145522Sdarrenr break; 4111145522Sdarrenr case IPPROTO_ICMP : 4112255332Scy if (flags & IPN_ICMPERR) { 4113145522Sdarrenr sport = fin->fin_data[1]; 4114255332Scy dport = 0; 4115255332Scy } else { 4116145522Sdarrenr dport = fin->fin_data[1]; 4117255332Scy sport = 0; 4118255332Scy } 4119145522Sdarrenr break; 4120145522Sdarrenr default : 4121255332Scy sport = 0; 4122255332Scy dport = 0; 4123145522Sdarrenr break; 412492685Sdarrenr } 412553642Sguido 4126145522Sdarrenr 4127145522Sdarrenr if ((flags & SI_WILDP) != 0) 4128145522Sdarrenr goto find_in_wild_ports; 4129145522Sdarrenr 4130255332Scy rhv = NAT_HASH_FN(dst, dport, 0xffffffff); 4131255332Scy rhv = NAT_HASH_FN(src.s_addr, rhv + sport, 0xffffffff); 4132255332Scy hv = rhv % softn->ipf_nat_table_sz; 4133255332Scy nat = softn->ipf_nat_table[1][hv]; 4134255332Scy /* TRACE dst, dport, src, sport, hv, nat */ 4135255332Scy 413653642Sguido for (; nat; nat = nat->nat_hnext[1]) { 4137161356Sguido if (nat->nat_ifps[0] != NULL) { 4138161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 4139161356Sguido continue; 4140255332Scy } 4141161356Sguido 4142255332Scy if (nat->nat_pr[0] != p) 4143255332Scy continue; 4144145522Sdarrenr 4145255332Scy switch (nat->nat_dir) 4146255332Scy { 4147255332Scy case NAT_INBOUND : 4148255332Scy case NAT_DIVERTIN : 4149255332Scy if (nat->nat_v[0] != 4) 4150255332Scy continue; 4151255332Scy if (nat->nat_osrcaddr != src.s_addr || 4152255332Scy nat->nat_odstaddr != dst) 4153255332Scy continue; 4154255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 4155255332Scy if (nat->nat_osport != sport) 4156145522Sdarrenr continue; 4157255332Scy if (nat->nat_odport != dport) 4158255332Scy continue; 4159255332Scy 4160255332Scy } else if (p == IPPROTO_ICMP) { 4161255332Scy if (nat->nat_osport != dport) { 4162255332Scy continue; 4163145522Sdarrenr } 4164255332Scy } 4165255332Scy break; 4166255332Scy case NAT_DIVERTOUT : 4167255332Scy if (nat->nat_dlocal) 4168255332Scy continue; 4169255332Scy case NAT_OUTBOUND : 4170255332Scy if (nat->nat_v[1] != 4) 4171255332Scy continue; 4172255332Scy if (nat->nat_dlocal) 4173255332Scy continue; 4174255332Scy if (nat->nat_dlocal) 4175255332Scy continue; 4176255332Scy if (nat->nat_ndstaddr != src.s_addr || 4177255332Scy nat->nat_nsrcaddr != dst) 4178255332Scy continue; 4179255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 4180255332Scy if (nat->nat_ndport != sport) 418192685Sdarrenr continue; 4182255332Scy if (nat->nat_nsport != dport) 418392685Sdarrenr continue; 4184255332Scy 4185255332Scy } else if (p == IPPROTO_ICMP) { 4186255332Scy if (nat->nat_osport != dport) { 4187255332Scy continue; 4188255332Scy } 418992685Sdarrenr } 4190255332Scy break; 4191255332Scy } 419292685Sdarrenr 4193255332Scy 4194255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 419592685Sdarrenr ipn = nat->nat_ptr; 419692685Sdarrenr if ((ipn != NULL) && (nat->nat_aps != NULL)) 4197255332Scy if (ipf_proxy_match(fin, nat) != 0) 419892685Sdarrenr continue; 419992685Sdarrenr } 4200255332Scy if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { 4201255332Scy nat->nat_ifps[0] = ifp; 4202255332Scy nat->nat_mtu[0] = GETIFMTU_4(ifp); 4203255332Scy } 4204255332Scy return nat; 420553642Sguido } 4206145522Sdarrenr 4207145522Sdarrenr /* 4208145522Sdarrenr * So if we didn't find it but there are wildcard members in the hash 4209145522Sdarrenr * table, go back and look for them. We do this search and update here 4210145522Sdarrenr * because it is modifying the NAT table and we want to do this only 4211145522Sdarrenr * for the first packet that matches. The exception, of course, is 4212145522Sdarrenr * for "dummy" (FI_IGNORE) lookups. 4213145522Sdarrenr */ 4214145522Sdarrenrfind_in_wild_ports: 4215255332Scy if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { 4216255332Scy NBUMPSIDEX(0, ns_lookup_miss, ns_lookup_miss_0); 421767614Sdarrenr return NULL; 4218255332Scy } 4219255332Scy if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { 4220255332Scy NBUMPSIDEX(0, ns_lookup_nowild, ns_lookup_nowild_0); 4221145522Sdarrenr return NULL; 4222255332Scy } 4223145522Sdarrenr 4224255332Scy RWLOCK_EXIT(&softc->ipf_nat); 4225145522Sdarrenr 422680482Sdarrenr hv = NAT_HASH_FN(dst, 0, 0xffffffff); 4227255332Scy hv = NAT_HASH_FN(src.s_addr, hv, softn->ipf_nat_table_sz); 4228255332Scy WRITE_ENTER(&softc->ipf_nat); 4229145522Sdarrenr 4230255332Scy nat = softn->ipf_nat_table[1][hv]; 4231255332Scy /* TRACE dst, src, hv, nat */ 423267614Sdarrenr for (; nat; nat = nat->nat_hnext[1]) { 4233161356Sguido if (nat->nat_ifps[0] != NULL) { 4234161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 4235161356Sguido continue; 4236255332Scy } 4237145522Sdarrenr 4238255332Scy if (nat->nat_pr[0] != fin->fin_p) 423967614Sdarrenr continue; 4240145522Sdarrenr 4241255332Scy switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)) 4242255332Scy { 4243255332Scy case NAT_INBOUND : 4244255332Scy if (nat->nat_v[0] != 4) 4245255332Scy continue; 4246255332Scy if (nat->nat_osrcaddr != src.s_addr || 4247255332Scy nat->nat_odstaddr != dst) 4248255332Scy continue; 4249255332Scy break; 4250255332Scy case NAT_OUTBOUND : 4251255332Scy if (nat->nat_v[1] != 4) 4252255332Scy continue; 4253255332Scy if (nat->nat_ndstaddr != src.s_addr || 4254255332Scy nat->nat_nsrcaddr != dst) 4255255332Scy continue; 4256255332Scy break; 4257255332Scy } 4258255332Scy 4259145522Sdarrenr nflags = nat->nat_flags; 4260145522Sdarrenr if (!(nflags & (NAT_TCPUDP|SI_WILDP))) 4261145522Sdarrenr continue; 4262145522Sdarrenr 4263255332Scy if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags, 4264255332Scy NAT_INBOUND) == 1) { 4265145522Sdarrenr if ((fin->fin_flx & FI_IGNORE) != 0) 4266145522Sdarrenr break; 4267145522Sdarrenr if ((nflags & SI_CLONE) != 0) { 4268255332Scy nat = ipf_nat_clone(fin, nat); 4269145522Sdarrenr if (nat == NULL) 4270145522Sdarrenr break; 4271145522Sdarrenr } else { 4272255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 4273255332Scy softn->ipf_nat_stats.ns_wilds--; 4274255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 4275145522Sdarrenr } 4276255332Scy 4277255332Scy if (nat->nat_dir == NAT_INBOUND) { 4278255332Scy if (nat->nat_osport == 0) { 4279255332Scy nat->nat_osport = sport; 4280255332Scy nat->nat_nsport = sport; 4281255332Scy } 4282255332Scy if (nat->nat_odport == 0) { 4283255332Scy nat->nat_odport = dport; 4284255332Scy nat->nat_ndport = dport; 4285255332Scy } 4286255332Scy } else if (nat->nat_dir == NAT_OUTBOUND) { 4287255332Scy if (nat->nat_osport == 0) { 4288255332Scy nat->nat_osport = dport; 4289255332Scy nat->nat_nsport = dport; 4290255332Scy } 4291255332Scy if (nat->nat_odport == 0) { 4292255332Scy nat->nat_odport = sport; 4293255332Scy nat->nat_ndport = sport; 4294255332Scy } 4295255332Scy } 4296255332Scy if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { 4297255332Scy nat->nat_ifps[0] = ifp; 4298255332Scy nat->nat_mtu[0] = GETIFMTU_4(ifp); 4299255332Scy } 4300145522Sdarrenr nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 4301255332Scy ipf_nat_tabmove(softn, nat); 430267614Sdarrenr break; 430367614Sdarrenr } 430467614Sdarrenr } 4305145522Sdarrenr 4306255332Scy MUTEX_DOWNGRADE(&softc->ipf_nat); 4307145522Sdarrenr 4308255332Scy if (nat == NULL) { 4309255332Scy NBUMPSIDE(0, ns_lookup_miss); 4310255332Scy } 431167614Sdarrenr return nat; 431253642Sguido} 431353642Sguido 431453642Sguido 4315145522Sdarrenr/* ------------------------------------------------------------------------ */ 4316255332Scy/* Function: ipf_nat_tabmove */ 4317145522Sdarrenr/* Returns: Nil */ 4318255332Scy/* Parameters: softn(I) - pointer to NAT context structure */ 4319255332Scy/* nat(I) - pointer to NAT structure */ 4320145522Sdarrenr/* Write Lock: ipf_nat */ 4321145522Sdarrenr/* */ 4322145522Sdarrenr/* This function is only called for TCP/UDP NAT table entries where the */ 4323145522Sdarrenr/* original was placed in the table without hashing on the ports and we now */ 4324145522Sdarrenr/* want to include hashing on port numbers. */ 4325145522Sdarrenr/* ------------------------------------------------------------------------ */ 4326255332Scystatic void 4327255332Scyipf_nat_tabmove(softn, nat) 4328255332Scy ipf_nat_softc_t *softn; 4329255332Scy nat_t *nat; 433067614Sdarrenr{ 4331255332Scy u_int hv0, hv1, rhv0, rhv1; 4332255332Scy natstat_t *nsp; 433367614Sdarrenr nat_t **natp; 433467614Sdarrenr 4335145522Sdarrenr if (nat->nat_flags & SI_CLONE) 4336145522Sdarrenr return; 433772006Sdarrenr 4338255332Scy nsp = &softn->ipf_nat_stats; 433967614Sdarrenr /* 434067614Sdarrenr * Remove the NAT entry from the old location 434167614Sdarrenr */ 434267614Sdarrenr if (nat->nat_hnext[0]) 434367614Sdarrenr nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 434467614Sdarrenr *nat->nat_phnext[0] = nat->nat_hnext[0]; 4345255332Scy nsp->ns_side[0].ns_bucketlen[nat->nat_hv[0] % 4346255332Scy softn->ipf_nat_table_sz]--; 434767614Sdarrenr 434867614Sdarrenr if (nat->nat_hnext[1]) 434967853Sdarrenr nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 435067614Sdarrenr *nat->nat_phnext[1] = nat->nat_hnext[1]; 4351255332Scy nsp->ns_side[1].ns_bucketlen[nat->nat_hv[1] % 4352255332Scy softn->ipf_nat_table_sz]--; 435367614Sdarrenr 435467853Sdarrenr /* 435567853Sdarrenr * Add into the NAT table in the new position 435667853Sdarrenr */ 4357255332Scy rhv0 = NAT_HASH_FN(nat->nat_osrcaddr, nat->nat_osport, 0xffffffff); 4358255332Scy rhv0 = NAT_HASH_FN(nat->nat_odstaddr, rhv0 + nat->nat_odport, 4359255332Scy 0xffffffff); 4360255332Scy rhv1 = NAT_HASH_FN(nat->nat_nsrcaddr, nat->nat_nsport, 0xffffffff); 4361255332Scy rhv1 = NAT_HASH_FN(nat->nat_ndstaddr, rhv1 + nat->nat_ndport, 4362255332Scy 0xffffffff); 4363255332Scy 4364255332Scy hv0 = rhv0 % softn->ipf_nat_table_sz; 4365255332Scy hv1 = rhv1 % softn->ipf_nat_table_sz; 4366255332Scy 4367255332Scy if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) { 4368255332Scy u_int swap; 4369255332Scy 4370255332Scy swap = hv0; 4371255332Scy hv0 = hv1; 4372255332Scy hv1 = swap; 4373255332Scy } 4374255332Scy 4375255332Scy /* TRACE nat_osrcaddr, nat_osport, nat_odstaddr, nat_odport, hv0 */ 4376255332Scy /* TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, nat_ndport, hv1 */ 4377255332Scy 4378255332Scy nat->nat_hv[0] = rhv0; 4379255332Scy natp = &softn->ipf_nat_table[0][hv0]; 438067614Sdarrenr if (*natp) 438167614Sdarrenr (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 438267614Sdarrenr nat->nat_phnext[0] = natp; 438367614Sdarrenr nat->nat_hnext[0] = *natp; 438467614Sdarrenr *natp = nat; 4385255332Scy nsp->ns_side[0].ns_bucketlen[hv0]++; 438667614Sdarrenr 4387255332Scy nat->nat_hv[1] = rhv1; 4388255332Scy natp = &softn->ipf_nat_table[1][hv1]; 438967614Sdarrenr if (*natp) 439067614Sdarrenr (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 439167614Sdarrenr nat->nat_phnext[1] = natp; 439267614Sdarrenr nat->nat_hnext[1] = *natp; 439367614Sdarrenr *natp = nat; 4394255332Scy nsp->ns_side[1].ns_bucketlen[hv1]++; 439567614Sdarrenr} 439667614Sdarrenr 439767614Sdarrenr 4398145522Sdarrenr/* ------------------------------------------------------------------------ */ 4399255332Scy/* Function: ipf_nat_outlookup */ 4400145522Sdarrenr/* Returns: nat_t* - NULL == no match, */ 4401145522Sdarrenr/* else pointer to matching NAT entry */ 4402145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4403145522Sdarrenr/* flags(I) - NAT flags for this packet */ 4404145522Sdarrenr/* p(I) - protocol for this packet */ 4405145522Sdarrenr/* src(I) - source IP address */ 4406145522Sdarrenr/* dst(I) - destination IP address */ 4407255332Scy/* rw(I) - 1 == write lock on held, 0 == read lock. */ 4408145522Sdarrenr/* */ 4409145522Sdarrenr/* Lookup a nat entry based on the source 'real' ip address/port and */ 4410145522Sdarrenr/* destination address/port. We use this lookup when sending a packet out, */ 4411145522Sdarrenr/* we're looking for a table entry, based on the source address. */ 4412145522Sdarrenr/* */ 4413145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 4414145522Sdarrenr/* */ 4415255332Scy/* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ 4416145522Sdarrenr/* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 4417145522Sdarrenr/* */ 4418145522Sdarrenr/* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 4419145522Sdarrenr/* the packet is of said protocol */ 4420145522Sdarrenr/* ------------------------------------------------------------------------ */ 4421255332Scynat_t * 4422255332Scyipf_nat_outlookup(fin, flags, p, src, dst) 4423255332Scy fr_info_t *fin; 4424255332Scy u_int flags, p; 4425255332Scy struct in_addr src , dst; 442653642Sguido{ 4427255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 4428255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 4429145522Sdarrenr u_short sport, dport; 4430145522Sdarrenr u_int sflags; 443192685Sdarrenr ipnat_t *ipn; 4432145522Sdarrenr nat_t *nat; 443392685Sdarrenr void *ifp; 443453642Sguido u_int hv; 443553642Sguido 443692685Sdarrenr ifp = fin->fin_ifp; 4437145522Sdarrenr sflags = flags & IPN_TCPUDPICMP; 4438145522Sdarrenr sport = 0; 4439145522Sdarrenr dport = 0; 4440145522Sdarrenr 4441145522Sdarrenr switch (p) 4442145522Sdarrenr { 4443145522Sdarrenr case IPPROTO_TCP : 4444145522Sdarrenr case IPPROTO_UDP : 4445145522Sdarrenr sport = htons(fin->fin_data[0]); 4446145522Sdarrenr dport = htons(fin->fin_data[1]); 4447145522Sdarrenr break; 4448145522Sdarrenr case IPPROTO_ICMP : 4449145522Sdarrenr if (flags & IPN_ICMPERR) 4450145522Sdarrenr sport = fin->fin_data[1]; 4451145522Sdarrenr else 4452145522Sdarrenr dport = fin->fin_data[1]; 4453145522Sdarrenr break; 4454145522Sdarrenr default : 4455145522Sdarrenr break; 445692685Sdarrenr } 445753642Sguido 4458145522Sdarrenr if ((flags & SI_WILDP) != 0) 4459145522Sdarrenr goto find_out_wild_ports; 4460145522Sdarrenr 4461255332Scy hv = NAT_HASH_FN(src.s_addr, sport, 0xffffffff); 4462255332Scy hv = NAT_HASH_FN(dst.s_addr, hv + dport, softn->ipf_nat_table_sz); 4463255332Scy nat = softn->ipf_nat_table[0][hv]; 4464255332Scy 4465255332Scy /* TRACE src, sport, dst, dport, hv, nat */ 4466255332Scy 446753642Sguido for (; nat; nat = nat->nat_hnext[0]) { 4468161356Sguido if (nat->nat_ifps[1] != NULL) { 4469161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 4470161356Sguido continue; 4471255332Scy } 4472161356Sguido 4473255332Scy if (nat->nat_pr[1] != p) 4474255332Scy continue; 447553642Sguido 4476255332Scy switch (nat->nat_dir) 4477255332Scy { 4478255332Scy case NAT_INBOUND : 4479255332Scy case NAT_DIVERTIN : 4480255332Scy if (nat->nat_v[1] != 4) 4481255332Scy continue; 4482255332Scy if (nat->nat_ndstaddr != src.s_addr || 4483255332Scy nat->nat_nsrcaddr != dst.s_addr) 4484255332Scy continue; 4485255332Scy 4486255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 4487255332Scy if (nat->nat_ndport != sport) 4488145522Sdarrenr continue; 4489255332Scy if (nat->nat_nsport != dport) 449092685Sdarrenr continue; 4491255332Scy 4492255332Scy } else if (p == IPPROTO_ICMP) { 4493255332Scy if (nat->nat_osport != dport) { 449492685Sdarrenr continue; 4495255332Scy } 449692685Sdarrenr } 4497255332Scy break; 4498255332Scy case NAT_OUTBOUND : 4499255332Scy case NAT_DIVERTOUT : 4500255332Scy if (nat->nat_v[0] != 4) 4501255332Scy continue; 4502255332Scy if (nat->nat_osrcaddr != src.s_addr || 4503255332Scy nat->nat_odstaddr != dst.s_addr) 4504255332Scy continue; 450592685Sdarrenr 4506255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 4507255332Scy if (nat->nat_odport != dport) 450892685Sdarrenr continue; 4509255332Scy if (nat->nat_osport != sport) 4510255332Scy continue; 4511255332Scy 4512255332Scy } else if (p == IPPROTO_ICMP) { 4513255332Scy if (nat->nat_osport != dport) { 4514255332Scy continue; 4515255332Scy } 4516255332Scy } 4517255332Scy break; 451892685Sdarrenr } 4519255332Scy 4520255332Scy ipn = nat->nat_ptr; 4521255332Scy if ((ipn != NULL) && (nat->nat_aps != NULL)) 4522255332Scy if (ipf_proxy_match(fin, nat) != 0) 4523255332Scy continue; 4524255332Scy 4525255332Scy if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { 4526255332Scy nat->nat_ifps[1] = ifp; 4527255332Scy nat->nat_mtu[1] = GETIFMTU_4(ifp); 4528255332Scy } 4529255332Scy return nat; 453053642Sguido } 4531145522Sdarrenr 4532145522Sdarrenr /* 4533145522Sdarrenr * So if we didn't find it but there are wildcard members in the hash 4534145522Sdarrenr * table, go back and look for them. We do this search and update here 4535145522Sdarrenr * because it is modifying the NAT table and we want to do this only 4536145522Sdarrenr * for the first packet that matches. The exception, of course, is 4537145522Sdarrenr * for "dummy" (FI_IGNORE) lookups. 4538145522Sdarrenr */ 4539145522Sdarrenrfind_out_wild_ports: 4540255332Scy if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { 4541255332Scy NBUMPSIDEX(1, ns_lookup_miss, ns_lookup_miss_1); 454267614Sdarrenr return NULL; 4543255332Scy } 4544255332Scy if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { 4545255332Scy NBUMPSIDEX(1, ns_lookup_nowild, ns_lookup_nowild_1); 4546145522Sdarrenr return NULL; 4547255332Scy } 454892685Sdarrenr 4549255332Scy RWLOCK_EXIT(&softc->ipf_nat); 4550145522Sdarrenr 4551255332Scy hv = NAT_HASH_FN(src.s_addr, 0, 0xffffffff); 4552255332Scy hv = NAT_HASH_FN(dst.s_addr, hv, softn->ipf_nat_table_sz); 4553145522Sdarrenr 4554255332Scy WRITE_ENTER(&softc->ipf_nat); 4555145522Sdarrenr 4556255332Scy nat = softn->ipf_nat_table[0][hv]; 455767614Sdarrenr for (; nat; nat = nat->nat_hnext[0]) { 4558161356Sguido if (nat->nat_ifps[1] != NULL) { 4559161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 4560161356Sguido continue; 4561255332Scy } 4562145522Sdarrenr 4563255332Scy if (nat->nat_pr[1] != fin->fin_p) 456467614Sdarrenr continue; 4565145522Sdarrenr 4566255332Scy switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)) 4567255332Scy { 4568255332Scy case NAT_INBOUND : 4569255332Scy if (nat->nat_v[1] != 4) 4570255332Scy continue; 4571255332Scy if (nat->nat_ndstaddr != src.s_addr || 4572255332Scy nat->nat_nsrcaddr != dst.s_addr) 4573255332Scy continue; 4574255332Scy break; 4575255332Scy case NAT_OUTBOUND : 4576255332Scy if (nat->nat_v[0] != 4) 4577255332Scy continue; 4578255332Scy if (nat->nat_osrcaddr != src.s_addr || 4579255332Scy nat->nat_odstaddr != dst.s_addr) 4580255332Scy continue; 4581255332Scy break; 4582255332Scy } 4583255332Scy 4584255332Scy if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP))) 4585145522Sdarrenr continue; 4586145522Sdarrenr 4587255332Scy if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags, 4588255332Scy NAT_OUTBOUND) == 1) { 4589145522Sdarrenr if ((fin->fin_flx & FI_IGNORE) != 0) 4590145522Sdarrenr break; 4591255332Scy if ((nat->nat_flags & SI_CLONE) != 0) { 4592255332Scy nat = ipf_nat_clone(fin, nat); 4593145522Sdarrenr if (nat == NULL) 4594145522Sdarrenr break; 4595145522Sdarrenr } else { 4596255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 4597255332Scy softn->ipf_nat_stats.ns_wilds--; 4598255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 4599145522Sdarrenr } 4600255332Scy 4601255332Scy if (nat->nat_dir == NAT_OUTBOUND) { 4602255332Scy if (nat->nat_osport == 0) { 4603255332Scy nat->nat_osport = sport; 4604255332Scy nat->nat_nsport = sport; 4605255332Scy } 4606255332Scy if (nat->nat_odport == 0) { 4607255332Scy nat->nat_odport = dport; 4608255332Scy nat->nat_ndport = dport; 4609255332Scy } 4610255332Scy } else if (nat->nat_dir == NAT_INBOUND) { 4611255332Scy if (nat->nat_osport == 0) { 4612255332Scy nat->nat_osport = dport; 4613255332Scy nat->nat_nsport = dport; 4614255332Scy } 4615255332Scy if (nat->nat_odport == 0) { 4616255332Scy nat->nat_odport = sport; 4617255332Scy nat->nat_ndport = sport; 4618255332Scy } 4619255332Scy } 4620255332Scy if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { 4621255332Scy nat->nat_ifps[1] = ifp; 4622255332Scy nat->nat_mtu[1] = GETIFMTU_4(ifp); 4623255332Scy } 4624145522Sdarrenr nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 4625255332Scy ipf_nat_tabmove(softn, nat); 462667614Sdarrenr break; 462767614Sdarrenr } 462867614Sdarrenr } 4629145522Sdarrenr 4630255332Scy MUTEX_DOWNGRADE(&softc->ipf_nat); 4631145522Sdarrenr 4632255332Scy if (nat == NULL) { 4633255332Scy NBUMPSIDE(1, ns_lookup_miss); 4634255332Scy } 463567614Sdarrenr return nat; 463653642Sguido} 463753642Sguido 463853642Sguido 4639145522Sdarrenr/* ------------------------------------------------------------------------ */ 4640255332Scy/* Function: ipf_nat_lookupredir */ 4641145522Sdarrenr/* Returns: nat_t* - NULL == no match, */ 4642145522Sdarrenr/* else pointer to matching NAT entry */ 4643145522Sdarrenr/* Parameters: np(I) - pointer to description of packet to find NAT table */ 4644145522Sdarrenr/* entry for. */ 4645145522Sdarrenr/* */ 4646145522Sdarrenr/* Lookup the NAT tables to search for a matching redirect */ 4647161356Sguido/* The contents of natlookup_t should imitate those found in a packet that */ 4648161356Sguido/* would be translated - ie a packet coming in for RDR or going out for MAP.*/ 4649161356Sguido/* We can do the lookup in one of two ways, imitating an inbound or */ 4650161356Sguido/* outbound packet. By default we assume outbound, unless IPN_IN is set. */ 4651161356Sguido/* For IN, the fields are set as follows: */ 4652161356Sguido/* nl_real* = source information */ 4653161356Sguido/* nl_out* = destination information (translated) */ 4654161356Sguido/* For an out packet, the fields are set like this: */ 4655161356Sguido/* nl_in* = source information (untranslated) */ 4656161356Sguido/* nl_out* = destination information (translated) */ 4657145522Sdarrenr/* ------------------------------------------------------------------------ */ 4658255332Scynat_t * 4659255332Scyipf_nat_lookupredir(np) 4660255332Scy natlookup_t *np; 466153642Sguido{ 4662145522Sdarrenr fr_info_t fi; 466353642Sguido nat_t *nat; 466453642Sguido 466592685Sdarrenr bzero((char *)&fi, sizeof(fi)); 4666145522Sdarrenr if (np->nl_flags & IPN_IN) { 4667145522Sdarrenr fi.fin_data[0] = ntohs(np->nl_realport); 4668145522Sdarrenr fi.fin_data[1] = ntohs(np->nl_outport); 4669145522Sdarrenr } else { 4670145522Sdarrenr fi.fin_data[0] = ntohs(np->nl_inport); 4671145522Sdarrenr fi.fin_data[1] = ntohs(np->nl_outport); 4672145522Sdarrenr } 4673145522Sdarrenr if (np->nl_flags & IPN_TCP) 4674145522Sdarrenr fi.fin_p = IPPROTO_TCP; 4675145522Sdarrenr else if (np->nl_flags & IPN_UDP) 4676145522Sdarrenr fi.fin_p = IPPROTO_UDP; 4677145522Sdarrenr else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) 4678145522Sdarrenr fi.fin_p = IPPROTO_ICMP; 467992685Sdarrenr 468053642Sguido /* 4681145522Sdarrenr * We can do two sorts of lookups: 4682145522Sdarrenr * - IPN_IN: we have the `real' and `out' address, look for `in'. 4683145522Sdarrenr * - default: we have the `in' and `out' address, look for `real'. 468453642Sguido */ 4685145522Sdarrenr if (np->nl_flags & IPN_IN) { 4686255332Scy if ((nat = ipf_nat_inlookup(&fi, np->nl_flags, fi.fin_p, 4687255332Scy np->nl_realip, np->nl_outip))) { 4688255332Scy np->nl_inip = nat->nat_odstip; 4689255332Scy np->nl_inport = nat->nat_odport; 4690145522Sdarrenr } 4691145522Sdarrenr } else { 4692145522Sdarrenr /* 4693145522Sdarrenr * If nl_inip is non null, this is a lookup based on the real 4694145522Sdarrenr * ip address. Else, we use the fake. 4695145522Sdarrenr */ 4696255332Scy if ((nat = ipf_nat_outlookup(&fi, np->nl_flags, fi.fin_p, 4697145522Sdarrenr np->nl_inip, np->nl_outip))) { 4698145522Sdarrenr 4699145522Sdarrenr if ((np->nl_flags & IPN_FINDFORWARD) != 0) { 4700145522Sdarrenr fr_info_t fin; 4701145522Sdarrenr bzero((char *)&fin, sizeof(fin)); 4702255332Scy fin.fin_p = nat->nat_pr[0]; 4703255332Scy fin.fin_data[0] = ntohs(nat->nat_ndport); 4704255332Scy fin.fin_data[1] = ntohs(nat->nat_nsport); 4705255332Scy if (ipf_nat_inlookup(&fin, np->nl_flags, 4706255332Scy fin.fin_p, nat->nat_ndstip, 4707255332Scy nat->nat_nsrcip) != NULL) { 4708145522Sdarrenr np->nl_flags &= ~IPN_FINDFORWARD; 4709145522Sdarrenr } 4710145522Sdarrenr } 4711145522Sdarrenr 4712255332Scy np->nl_realip = nat->nat_ndstip; 4713255332Scy np->nl_realport = nat->nat_ndport; 4714145522Sdarrenr } 4715145522Sdarrenr } 4716145522Sdarrenr 471753642Sguido return nat; 471853642Sguido} 471953642Sguido 472053642Sguido 4721145522Sdarrenr/* ------------------------------------------------------------------------ */ 4722255332Scy/* Function: ipf_nat_match */ 4723145522Sdarrenr/* Returns: int - 0 == no match, 1 == match */ 4724145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4725145522Sdarrenr/* np(I) - pointer to NAT rule */ 4726145522Sdarrenr/* */ 4727145522Sdarrenr/* Pull the matching of a packet against a NAT rule out of that complex */ 4728255332Scy/* loop inside ipf_nat_checkin() and lay it out properly in its own function. */ 4729145522Sdarrenr/* ------------------------------------------------------------------------ */ 4730255332Scystatic int 4731255332Scyipf_nat_match(fin, np) 4732255332Scy fr_info_t *fin; 4733255332Scy ipnat_t *np; 473460852Sdarrenr{ 4735255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 473660852Sdarrenr frtuc_t *ft; 4737255332Scy int match; 473860852Sdarrenr 4739255332Scy match = 0; 4740255332Scy switch (np->in_osrcatype) 4741255332Scy { 4742255332Scy case FRI_NORMAL : 4743255332Scy match = ((fin->fin_saddr & np->in_osrcmsk) != np->in_osrcaddr); 4744255332Scy break; 4745255332Scy case FRI_LOOKUP : 4746255332Scy match = (*np->in_osrcfunc)(softc, np->in_osrcptr, 4747255332Scy 4, &fin->fin_saddr, fin->fin_plen); 4748255332Scy break; 4749255332Scy } 4750255332Scy match ^= ((np->in_flags & IPN_NOTSRC) != 0); 4751255332Scy if (match) 475260852Sdarrenr return 0; 475360852Sdarrenr 4754255332Scy match = 0; 4755255332Scy switch (np->in_odstatype) 4756255332Scy { 4757255332Scy case FRI_NORMAL : 4758255332Scy match = ((fin->fin_daddr & np->in_odstmsk) != np->in_odstaddr); 4759255332Scy break; 4760255332Scy case FRI_LOOKUP : 4761255332Scy match = (*np->in_odstfunc)(softc, np->in_odstptr, 4762255332Scy 4, &fin->fin_daddr, fin->fin_plen); 4763255332Scy break; 4764255332Scy } 4765255332Scy 4766255332Scy match ^= ((np->in_flags & IPN_NOTDST) != 0); 4767255332Scy if (match) 476860852Sdarrenr return 0; 4769145522Sdarrenr 477060852Sdarrenr ft = &np->in_tuc; 4771145522Sdarrenr if (!(fin->fin_flx & FI_TCPUDP) || 4772145522Sdarrenr (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { 477360852Sdarrenr if (ft->ftu_scmp || ft->ftu_dcmp) 477460852Sdarrenr return 0; 477560852Sdarrenr return 1; 477660852Sdarrenr } 477760852Sdarrenr 4778255332Scy return ipf_tcpudpchk(&fin->fin_fi, ft); 477960852Sdarrenr} 478060852Sdarrenr 478160852Sdarrenr 4782145522Sdarrenr/* ------------------------------------------------------------------------ */ 4783255332Scy/* Function: ipf_nat_update */ 4784145522Sdarrenr/* Returns: Nil */ 4785255332Scy/* Parameters: fin(I) - pointer to packet information */ 4786255332Scy/* nat(I) - pointer to NAT structure */ 4787145522Sdarrenr/* */ 4788145522Sdarrenr/* Updates the lifetime of a NAT table entry for non-TCP packets. Must be */ 4789255332Scy/* called with fin_rev updated - i.e. after calling ipf_nat_proto(). */ 4790255332Scy/* */ 4791255332Scy/* This *MUST* be called after ipf_nat_proto() as it expects fin_rev to */ 4792255332Scy/* already be set. */ 4793145522Sdarrenr/* ------------------------------------------------------------------------ */ 4794255332Scyvoid 4795255332Scyipf_nat_update(fin, nat) 4796255332Scy fr_info_t *fin; 4797255332Scy nat_t *nat; 479853642Sguido{ 4799255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 4800255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 4801145522Sdarrenr ipftq_t *ifq, *ifq2; 4802145522Sdarrenr ipftqent_t *tqe; 4803255332Scy ipnat_t *np = nat->nat_ptr; 4804145522Sdarrenr 4805145522Sdarrenr tqe = &nat->nat_tqe; 4806145522Sdarrenr ifq = tqe->tqe_ifq; 4807145522Sdarrenr 4808145522Sdarrenr /* 4809145522Sdarrenr * We allow over-riding of NAT timeouts from NAT rules, even for 4810145522Sdarrenr * TCP, however, if it is TCP and there is no rule timeout set, 4811145522Sdarrenr * then do not update the timeout here. 4812145522Sdarrenr */ 4813255332Scy if (np != NULL) { 4814255332Scy np->in_bytes[fin->fin_rev] += fin->fin_plen; 4815145522Sdarrenr ifq2 = np->in_tqehead[fin->fin_rev]; 4816255332Scy } else { 4817145522Sdarrenr ifq2 = NULL; 4818255332Scy } 4819145522Sdarrenr 4820255332Scy if (nat->nat_pr[0] == IPPROTO_TCP && ifq2 == NULL) { 4821255332Scy (void) ipf_tcp_age(&nat->nat_tqe, fin, softn->ipf_nat_tcptq, 4822255332Scy 0, 2); 4823145522Sdarrenr } else { 4824145522Sdarrenr if (ifq2 == NULL) { 4825255332Scy if (nat->nat_pr[0] == IPPROTO_UDP) 4826255332Scy ifq2 = fin->fin_rev ? &softn->ipf_nat_udpacktq : 4827255332Scy &softn->ipf_nat_udptq; 4828255332Scy else if (nat->nat_pr[0] == IPPROTO_ICMP || 4829255332Scy nat->nat_pr[0] == IPPROTO_ICMPV6) 4830255332Scy ifq2 = fin->fin_rev ? &softn->ipf_nat_icmpacktq: 4831255332Scy &softn->ipf_nat_icmptq; 4832145522Sdarrenr else 4833255332Scy ifq2 = &softn->ipf_nat_iptq; 4834145522Sdarrenr } 4835145522Sdarrenr 4836255332Scy ipf_movequeue(softc->ipf_ticks, tqe, ifq, ifq2); 4837145522Sdarrenr } 4838145522Sdarrenr} 4839145522Sdarrenr 4840145522Sdarrenr 4841145522Sdarrenr/* ------------------------------------------------------------------------ */ 4842255332Scy/* Function: ipf_nat_checkout */ 4843145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 4844145522Sdarrenr/* 0 == no packet translation occurred, */ 4845145522Sdarrenr/* 1 == packet was successfully translated. */ 4846145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4847145522Sdarrenr/* passp(I) - pointer to filtering result flags */ 4848145522Sdarrenr/* */ 4849145522Sdarrenr/* Check to see if an outcoming packet should be changed. ICMP packets are */ 4850145522Sdarrenr/* first checked to see if they match an existing entry (if an error), */ 4851145522Sdarrenr/* otherwise a search of the current NAT table is made. If neither results */ 4852145522Sdarrenr/* in a match then a search for a matching NAT rule is made. Create a new */ 4853145522Sdarrenr/* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 4854145522Sdarrenr/* packet header(s) as required. */ 4855145522Sdarrenr/* ------------------------------------------------------------------------ */ 4856255332Scyint 4857255332Scyipf_nat_checkout(fin, passp) 4858255332Scy fr_info_t *fin; 4859255332Scy u_32_t *passp; 4860145522Sdarrenr{ 4861255332Scy ipnat_t *np = NULL, *npnext; 4862145522Sdarrenr struct ifnet *ifp, *sifp; 4863255332Scy ipf_main_softc_t *softc; 4864255332Scy ipf_nat_softc_t *softn; 4865145522Sdarrenr icmphdr_t *icmp = NULL; 486653642Sguido tcphdr_t *tcp = NULL; 4867145522Sdarrenr int rval, natfailed; 4868145522Sdarrenr u_int nflags = 0; 4869145522Sdarrenr u_32_t ipa, iph; 4870145522Sdarrenr int natadd = 1; 487153642Sguido frentry_t *fr; 487253642Sguido nat_t *nat; 487353642Sguido 4874255332Scy if (fin->fin_v == 6) { 4875255332Scy#ifdef USE_INET6 4876255332Scy return ipf_nat6_checkout(fin, passp); 4877255332Scy#else 487853642Sguido return 0; 4879255332Scy#endif 4880255332Scy } 488153642Sguido 4882255332Scy softc = fin->fin_main_soft; 4883255332Scy softn = softc->ipf_nat_soft; 4884255332Scy 4885255332Scy if (softn->ipf_nat_lock != 0) 4886255332Scy return 0; 4887255332Scy if (softn->ipf_nat_stats.ns_rules == 0 && 4888255332Scy softn->ipf_nat_instances == NULL) 4889255332Scy return 0; 4890255332Scy 4891145522Sdarrenr natfailed = 0; 4892145522Sdarrenr fr = fin->fin_fr; 4893145522Sdarrenr sifp = fin->fin_ifp; 4894170268Sdarrenr if (fr != NULL) { 4895255332Scy ifp = fr->fr_tifs[fin->fin_rev].fd_ptr; 4896170268Sdarrenr if ((ifp != NULL) && (ifp != (void *)-1)) 4897170268Sdarrenr fin->fin_ifp = ifp; 4898170268Sdarrenr } 489992685Sdarrenr ifp = fin->fin_ifp; 490053642Sguido 4901145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 4902145522Sdarrenr switch (fin->fin_p) 4903145522Sdarrenr { 4904145522Sdarrenr case IPPROTO_TCP : 490553642Sguido nflags = IPN_TCP; 4906145522Sdarrenr break; 4907145522Sdarrenr case IPPROTO_UDP : 490853642Sguido nflags = IPN_UDP; 4909145522Sdarrenr break; 4910145522Sdarrenr case IPPROTO_ICMP : 4911145522Sdarrenr icmp = fin->fin_dp; 4912145522Sdarrenr 4913145522Sdarrenr /* 4914145522Sdarrenr * This is an incoming packet, so the destination is 4915145522Sdarrenr * the icmp_id and the source port equals 0 4916145522Sdarrenr */ 4917255332Scy if ((fin->fin_flx & FI_ICMPQUERY) != 0) 4918145522Sdarrenr nflags = IPN_ICMPQUERY; 4919145522Sdarrenr break; 4920145522Sdarrenr default : 4921145522Sdarrenr break; 492253642Sguido } 4923255332Scy 4924145522Sdarrenr if ((nflags & IPN_TCPUDP)) 4925145522Sdarrenr tcp = fin->fin_dp; 492653642Sguido } 492753642Sguido 492892685Sdarrenr ipa = fin->fin_saddr; 492953642Sguido 4930255332Scy READ_ENTER(&softc->ipf_nat); 493160852Sdarrenr 4932255332Scy if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && 4933255332Scy (nat = ipf_nat_icmperror(fin, &nflags, NAT_OUTBOUND))) 4934145522Sdarrenr /*EMPTY*/; 4935255332Scy else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) 493653642Sguido natadd = 0; 4937255332Scy else if ((nat = ipf_nat_outlookup(fin, nflags|NAT_SEARCH, 4938255332Scy (u_int)fin->fin_p, fin->fin_src, 4939255332Scy fin->fin_dst))) { 494053642Sguido nflags = nat->nat_flags; 4941255332Scy } else if (fin->fin_off == 0) { 4942255332Scy u_32_t hv, msk, nmsk = 0; 494392685Sdarrenr 494453642Sguido /* 494553642Sguido * If there is no current entry in the nat table for this IP#, 494653642Sguido * create one for it (if there is a matching rule). 494753642Sguido */ 494853642Sguidomaskloop: 4949255332Scy msk = softn->ipf_nat_map_active_masks[nmsk]; 4950255332Scy iph = ipa & msk; 4951255332Scy hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_maprules_sz); 4952255332Scyretry_roundrobin: 4953255332Scy for (np = softn->ipf_nat_map_rules[hv]; np; np = npnext) { 4954255332Scy npnext = np->in_mnext; 4955161356Sguido if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) 495660852Sdarrenr continue; 4957255332Scy if (np->in_v[0] != 4) 495860852Sdarrenr continue; 4959255332Scy if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p)) 4960145522Sdarrenr continue; 4961255332Scy if ((np->in_flags & IPN_RF) && 4962255332Scy !(np->in_flags & nflags)) 4963145522Sdarrenr continue; 496460852Sdarrenr if (np->in_flags & IPN_FILTER) { 4965255332Scy switch (ipf_nat_match(fin, np)) 4966255332Scy { 4967255332Scy case 0 : 496860852Sdarrenr continue; 4969255332Scy case -1 : 4970255332Scy rval = -1; 4971255332Scy goto outmatchfail; 4972255332Scy case 1 : 4973255332Scy default : 4974255332Scy break; 4975255332Scy } 4976255332Scy } else if ((ipa & np->in_osrcmsk) != np->in_osrcaddr) 497760852Sdarrenr continue; 4978145522Sdarrenr 4979145522Sdarrenr if ((fr != NULL) && 4980255332Scy !ipf_matchtag(&np->in_tag, &fr->fr_nattag)) 498192685Sdarrenr continue; 4982145522Sdarrenr 4983255332Scy if (np->in_plabel != -1) { 4984145522Sdarrenr if (((np->in_flags & IPN_FILTER) == 0) && 4985255332Scy (np->in_odport != fin->fin_data[1])) 4986145522Sdarrenr continue; 4987255332Scy if (ipf_proxy_ok(fin, tcp, np) == 0) 4988145522Sdarrenr continue; 4989145522Sdarrenr } 4990145522Sdarrenr 4991255332Scy if (np->in_flags & IPN_NO) { 499292685Sdarrenr np->in_hits++; 499392685Sdarrenr break; 4994145522Sdarrenr } 4995255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 4996255332Scy /* 4997255332Scy * If we've matched a round-robin rule but it has 4998255332Scy * moved in the list since we got it, start over as 4999255332Scy * this is now no longer correct. 5000255332Scy */ 5001255332Scy if (npnext != np->in_mnext) { 5002255332Scy if ((np->in_flags & IPN_ROUNDR) != 0) { 5003255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 5004255332Scy goto retry_roundrobin; 5005255332Scy } 5006255332Scy npnext = np->in_mnext; 5007145522Sdarrenr } 5008255332Scy 5009255332Scy nat = ipf_nat_add(fin, np, NULL, nflags, NAT_OUTBOUND); 5010255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 5011255332Scy if (nat != NULL) { 5012255332Scy natfailed = 0; 5013255332Scy break; 5014255332Scy } 5015255332Scy natfailed = -1; 501653642Sguido } 5017255332Scy if ((np == NULL) && (nmsk < softn->ipf_nat_map_max)) { 5018255332Scy nmsk++; 5019255332Scy goto maskloop; 5020255332Scy } 502153642Sguido } 502253642Sguido 5023145522Sdarrenr if (nat != NULL) { 5024255332Scy rval = ipf_nat_out(fin, nat, natadd, nflags); 5025145522Sdarrenr if (rval == 1) { 5026145522Sdarrenr MUTEX_ENTER(&nat->nat_lock); 5027255332Scy ipf_nat_update(fin, nat); 5028255332Scy nat->nat_bytes[1] += fin->fin_plen; 5029255332Scy nat->nat_pkts[1]++; 5030255332Scy fin->fin_pktnum = nat->nat_pkts[1]; 5031145522Sdarrenr MUTEX_EXIT(&nat->nat_lock); 5032145522Sdarrenr } 5033145522Sdarrenr } else 5034145522Sdarrenr rval = natfailed; 5035255332Scyoutmatchfail: 5036255332Scy RWLOCK_EXIT(&softc->ipf_nat); 5037145522Sdarrenr 5038255332Scy switch (rval) 5039255332Scy { 5040255332Scy case -1 : 5041255332Scy if (passp != NULL) { 5042255332Scy DT1(frb_natv4out, fr_info_t *, fin); 5043255332Scy NBUMPSIDED(1, ns_drop); 5044145522Sdarrenr *passp = FR_BLOCK; 5045255332Scy fin->fin_reason = FRB_NATV4; 5046255332Scy } 5047145522Sdarrenr fin->fin_flx |= FI_BADNAT; 5048255332Scy NBUMPSIDED(1, ns_badnat); 5049255332Scy break; 5050255332Scy case 0 : 5051255332Scy NBUMPSIDE(1, ns_ignored); 5052255332Scy break; 5053255332Scy case 1 : 5054255332Scy NBUMPSIDE(1, ns_translated); 5055255332Scy break; 5056145522Sdarrenr } 5057145522Sdarrenr fin->fin_ifp = sifp; 5058145522Sdarrenr return rval; 5059145522Sdarrenr} 5060145522Sdarrenr 5061145522Sdarrenr/* ------------------------------------------------------------------------ */ 5062255332Scy/* Function: ipf_nat_out */ 5063145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 5064145522Sdarrenr/* 1 == packet was successfully translated. */ 5065145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 5066145522Sdarrenr/* nat(I) - pointer to NAT structure */ 5067145522Sdarrenr/* natadd(I) - flag indicating if it is safe to add frag cache */ 5068145522Sdarrenr/* nflags(I) - NAT flags set for this packet */ 5069145522Sdarrenr/* */ 5070145522Sdarrenr/* Translate a packet coming "out" on an interface. */ 5071145522Sdarrenr/* ------------------------------------------------------------------------ */ 5072255332Scyint 5073255332Scyipf_nat_out(fin, nat, natadd, nflags) 5074255332Scy fr_info_t *fin; 5075255332Scy nat_t *nat; 5076255332Scy int natadd; 5077255332Scy u_32_t nflags; 5078145522Sdarrenr{ 5079255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 5080255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 5081145522Sdarrenr icmphdr_t *icmp; 5082145522Sdarrenr tcphdr_t *tcp; 5083145522Sdarrenr ipnat_t *np; 5084255332Scy int skip; 5085145522Sdarrenr int i; 5086145522Sdarrenr 5087145522Sdarrenr tcp = NULL; 5088145522Sdarrenr icmp = NULL; 5089145522Sdarrenr np = nat->nat_ptr; 5090145522Sdarrenr 5091145522Sdarrenr if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL)) 5092255332Scy (void) ipf_frag_natnew(softc, fin, 0, nat); 5093145522Sdarrenr 509472006Sdarrenr /* 5095145522Sdarrenr * Fix up checksums, not by recalculating them, but 5096145522Sdarrenr * simply computing adjustments. 5097145522Sdarrenr * This is only done for STREAMS based IP implementations where the 5098145522Sdarrenr * checksum has already been calculated by IP. In all other cases, 5099145522Sdarrenr * IPFilter is called before the checksum needs calculating so there 5100145522Sdarrenr * is no call to modify whatever is in the header now. 510172006Sdarrenr */ 5102255332Scy if (nflags == IPN_ICMPERR) { 5103255332Scy u_32_t s1, s2, sumd, msumd; 510463523Sdarrenr 5105255332Scy s1 = LONG_SUM(ntohl(fin->fin_saddr)); 5106255332Scy if (nat->nat_dir == NAT_OUTBOUND) { 5107255332Scy s2 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); 5108255332Scy } else { 5109255332Scy s2 = LONG_SUM(ntohl(nat->nat_odstaddr)); 511063523Sdarrenr } 5111255332Scy CALC_SUMD(s1, s2, sumd); 5112255332Scy msumd = sumd; 5113255332Scy 5114255332Scy s1 = LONG_SUM(ntohl(fin->fin_daddr)); 5115255332Scy if (nat->nat_dir == NAT_OUTBOUND) { 5116255332Scy s2 = LONG_SUM(ntohl(nat->nat_ndstaddr)); 5117255332Scy } else { 5118255332Scy s2 = LONG_SUM(ntohl(nat->nat_osrcaddr)); 5119255332Scy } 5120255332Scy CALC_SUMD(s1, s2, sumd); 5121255332Scy msumd += sumd; 5122255332Scy 5123255332Scy ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, msumd, 0); 5124255332Scy } 5125153876Sguido#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 5126292811Scy defined(linux) || defined(BRIDGE_IPF) || defined(__FreeBSD__) 5127255332Scy else { 5128255332Scy /* 5129255332Scy * Strictly speaking, this isn't necessary on BSD 5130255332Scy * kernels because they do checksum calculation after 5131255332Scy * this code has run BUT if ipfilter is being used 5132255332Scy * to do NAT as a bridge, that code doesn't exist. 5133255332Scy */ 5134255332Scy switch (nat->nat_dir) 5135255332Scy { 5136255332Scy case NAT_OUTBOUND : 5137255332Scy ipf_fix_outcksum(fin->fin_cksum & FI_CK_L4PART, 5138255332Scy &fin->fin_ip->ip_sum, 5139255332Scy nat->nat_ipsumd, 0); 5140255332Scy break; 5141255332Scy 5142255332Scy case NAT_INBOUND : 5143255332Scy ipf_fix_incksum(fin->fin_cksum & FI_CK_L4PART, 5144255332Scy &fin->fin_ip->ip_sum, 5145255332Scy nat->nat_ipsumd, 0); 5146255332Scy break; 5147255332Scy 5148255332Scy default : 5149255332Scy break; 515063523Sdarrenr } 5151255332Scy } 515253642Sguido#endif 5153255332Scy 5154255332Scy /* 5155255332Scy * Address assignment is after the checksum modification because 5156255332Scy * we are using the address in the packet for determining the 5157255332Scy * correct checksum offset (the ICMP error could be coming from 5158255332Scy * anyone...) 5159255332Scy */ 5160255332Scy switch (nat->nat_dir) 5161255332Scy { 5162255332Scy case NAT_OUTBOUND : 5163255332Scy fin->fin_ip->ip_src = nat->nat_nsrcip; 5164255332Scy fin->fin_saddr = nat->nat_nsrcaddr; 5165255332Scy fin->fin_ip->ip_dst = nat->nat_ndstip; 5166255332Scy fin->fin_daddr = nat->nat_ndstaddr; 5167255332Scy break; 5168255332Scy 5169255332Scy case NAT_INBOUND : 5170255332Scy fin->fin_ip->ip_src = nat->nat_odstip; 5171255332Scy fin->fin_saddr = nat->nat_ndstaddr; 5172255332Scy fin->fin_ip->ip_dst = nat->nat_osrcip; 5173255332Scy fin->fin_daddr = nat->nat_nsrcaddr; 5174255332Scy break; 5175255332Scy 5176255332Scy case NAT_DIVERTIN : 5177255332Scy { 5178255332Scy mb_t *m; 5179255332Scy 5180255332Scy skip = ipf_nat_decap(fin, nat); 5181255332Scy if (skip <= 0) { 5182255332Scy NBUMPSIDED(1, ns_decap_fail); 5183255332Scy return -1; 5184255332Scy } 5185255332Scy 5186255332Scy m = fin->fin_m; 5187255332Scy 5188255332Scy#if defined(MENTAT) && defined(_KERNEL) 5189255332Scy m->b_rptr += skip; 5190255332Scy#else 5191255332Scy m->m_data += skip; 5192255332Scy m->m_len -= skip; 5193255332Scy 5194255332Scy# ifdef M_PKTHDR 5195255332Scy if (m->m_flags & M_PKTHDR) 5196255332Scy m->m_pkthdr.len -= skip; 5197255332Scy# endif 5198255332Scy#endif 5199255332Scy 5200255332Scy MUTEX_ENTER(&nat->nat_lock); 5201255332Scy ipf_nat_update(fin, nat); 5202255332Scy MUTEX_EXIT(&nat->nat_lock); 5203255332Scy fin->fin_flx |= FI_NATED; 5204255332Scy if (np != NULL && np->in_tag.ipt_num[0] != 0) 5205255332Scy fin->fin_nattag = &np->in_tag; 5206255332Scy return 1; 5207255332Scy /* NOTREACHED */ 5208255332Scy } 5209255332Scy 5210255332Scy case NAT_DIVERTOUT : 5211255332Scy { 5212255332Scy u_32_t s1, s2, sumd; 5213255332Scy udphdr_t *uh; 5214255332Scy ip_t *ip; 5215255332Scy mb_t *m; 5216255332Scy 5217255332Scy m = M_DUP(np->in_divmp); 5218255332Scy if (m == NULL) { 5219255332Scy NBUMPSIDED(1, ns_divert_dup); 5220255332Scy return -1; 5221255332Scy } 5222255332Scy 5223255332Scy ip = MTOD(m, ip_t *); 5224255332Scy ip->ip_id = htons(ipf_nextipid(fin)); 5225255332Scy s2 = ntohs(ip->ip_id); 5226255332Scy 5227255332Scy s1 = ip->ip_len; 5228255332Scy ip->ip_len = ntohs(ip->ip_len); 5229255332Scy ip->ip_len += fin->fin_plen; 5230255332Scy ip->ip_len = htons(ip->ip_len); 5231255332Scy s2 += ntohs(ip->ip_len); 5232255332Scy CALC_SUMD(s1, s2, sumd); 5233255332Scy 5234255332Scy uh = (udphdr_t *)(ip + 1); 5235255332Scy uh->uh_ulen += fin->fin_plen; 5236255332Scy uh->uh_ulen = htons(uh->uh_ulen); 5237255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 5238292979Scy defined(linux) || defined(BRIDGE_IPF) || defined(__FreeBSD__) 5239255332Scy ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0); 5240255332Scy#endif 5241255332Scy 5242255332Scy PREP_MB_T(fin, m); 5243255332Scy 5244255332Scy fin->fin_src = ip->ip_src; 5245255332Scy fin->fin_dst = ip->ip_dst; 5246255332Scy fin->fin_ip = ip; 5247255332Scy fin->fin_plen += sizeof(ip_t) + 8; /* UDP + IPv4 hdr */ 5248255332Scy fin->fin_dlen += sizeof(ip_t) + 8; /* UDP + IPv4 hdr */ 5249255332Scy 5250255332Scy nflags &= ~IPN_TCPUDPICMP; 5251255332Scy 5252255332Scy break; 5253255332Scy } 5254255332Scy 5255255332Scy default : 5256255332Scy break; 5257145522Sdarrenr } 525853642Sguido 5259145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 5260255332Scy u_short *csump; 5261255332Scy 5262255332Scy if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) { 5263145522Sdarrenr tcp = fin->fin_dp; 526453642Sguido 5265255332Scy switch (nat->nat_dir) 5266255332Scy { 5267255332Scy case NAT_OUTBOUND : 5268255332Scy tcp->th_sport = nat->nat_nsport; 5269255332Scy fin->fin_data[0] = ntohs(nat->nat_nsport); 5270255332Scy tcp->th_dport = nat->nat_ndport; 5271255332Scy fin->fin_data[1] = ntohs(nat->nat_ndport); 5272255332Scy break; 5273255332Scy 5274255332Scy case NAT_INBOUND : 5275255332Scy tcp->th_sport = nat->nat_odport; 5276255332Scy fin->fin_data[0] = ntohs(nat->nat_odport); 5277255332Scy tcp->th_dport = nat->nat_osport; 5278255332Scy fin->fin_data[1] = ntohs(nat->nat_osport); 5279255332Scy break; 5280255332Scy } 5281145522Sdarrenr } 528253642Sguido 5283255332Scy if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) { 5284145522Sdarrenr icmp = fin->fin_dp; 5285255332Scy icmp->icmp_id = nat->nat_nicmpid; 5286145522Sdarrenr } 5287110916Sdarrenr 5288255332Scy csump = ipf_nat_proto(fin, nat, nflags); 5289255332Scy 5290255332Scy /* 5291255332Scy * The above comments do not hold for layer 4 (or higher) 5292255332Scy * checksums... 5293255332Scy */ 5294255332Scy if (csump != NULL) { 5295255332Scy if (nat->nat_dir == NAT_OUTBOUND) 5296255332Scy ipf_fix_outcksum(fin->fin_cksum, csump, 5297255332Scy nat->nat_sumd[0], 5298255332Scy nat->nat_sumd[1] + 5299255332Scy fin->fin_dlen); 5300255332Scy else 5301255332Scy ipf_fix_incksum(fin->fin_cksum, csump, 5302255332Scy nat->nat_sumd[0], 5303255332Scy nat->nat_sumd[1] + 5304255332Scy fin->fin_dlen); 5305255332Scy } 5306145522Sdarrenr } 5307110916Sdarrenr 5308255332Scy ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); 5309145522Sdarrenr /* ------------------------------------------------------------- */ 5310255332Scy /* A few quick notes: */ 5311255332Scy /* Following are test conditions prior to calling the */ 5312255332Scy /* ipf_proxy_check routine. */ 5313255332Scy /* */ 5314255332Scy /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 5315255332Scy /* with a redirect rule, we attempt to match the packet's */ 5316255332Scy /* source port against in_dport, otherwise we'd compare the */ 5317255332Scy /* packet's destination. */ 5318145522Sdarrenr /* ------------------------------------------------------------- */ 5319145522Sdarrenr if ((np != NULL) && (np->in_apr != NULL)) { 5320255332Scy i = ipf_proxy_check(fin, nat); 5321255332Scy if (i == 0) { 532260852Sdarrenr i = 1; 5323255332Scy } else if (i == -1) { 5324255332Scy NBUMPSIDED(1, ns_ipf_proxy_fail); 5325255332Scy } 5326255332Scy } else { 5327145522Sdarrenr i = 1; 5328255332Scy } 5329145522Sdarrenr fin->fin_flx |= FI_NATED; 5330145522Sdarrenr return i; 533153642Sguido} 533253642Sguido 533353642Sguido 5334145522Sdarrenr/* ------------------------------------------------------------------------ */ 5335255332Scy/* Function: ipf_nat_checkin */ 5336145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 5337145522Sdarrenr/* 0 == no packet translation occurred, */ 5338145522Sdarrenr/* 1 == packet was successfully translated. */ 5339145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 5340145522Sdarrenr/* passp(I) - pointer to filtering result flags */ 5341145522Sdarrenr/* */ 5342145522Sdarrenr/* Check to see if an incoming packet should be changed. ICMP packets are */ 5343145522Sdarrenr/* first checked to see if they match an existing entry (if an error), */ 5344145522Sdarrenr/* otherwise a search of the current NAT table is made. If neither results */ 5345145522Sdarrenr/* in a match then a search for a matching NAT rule is made. Create a new */ 5346145522Sdarrenr/* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 5347145522Sdarrenr/* packet header(s) as required. */ 5348145522Sdarrenr/* ------------------------------------------------------------------------ */ 5349255332Scyint 5350255332Scyipf_nat_checkin(fin, passp) 5351255332Scy fr_info_t *fin; 5352255332Scy u_32_t *passp; 535353642Sguido{ 5354255332Scy ipf_main_softc_t *softc; 5355255332Scy ipf_nat_softc_t *softn; 5356145522Sdarrenr u_int nflags, natadd; 5357255332Scy ipnat_t *np, *npnext; 5358145522Sdarrenr int rval, natfailed; 5359145522Sdarrenr struct ifnet *ifp; 5360145522Sdarrenr struct in_addr in; 5361145522Sdarrenr icmphdr_t *icmp; 5362145522Sdarrenr tcphdr_t *tcp; 5363145522Sdarrenr u_short dport; 536453642Sguido nat_t *nat; 536553642Sguido u_32_t iph; 536653642Sguido 5367255332Scy softc = fin->fin_main_soft; 5368255332Scy softn = softc->ipf_nat_soft; 5369255332Scy 5370255332Scy if (softn->ipf_nat_lock != 0) 537153642Sguido return 0; 5372255332Scy if (softn->ipf_nat_stats.ns_rules == 0 && 5373255332Scy softn->ipf_nat_instances == NULL) 5374255332Scy return 0; 537553642Sguido 5376145522Sdarrenr tcp = NULL; 5377145522Sdarrenr icmp = NULL; 5378145522Sdarrenr dport = 0; 5379145522Sdarrenr natadd = 1; 5380145522Sdarrenr nflags = 0; 5381145522Sdarrenr natfailed = 0; 5382145522Sdarrenr ifp = fin->fin_ifp; 5383145522Sdarrenr 5384145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 5385145522Sdarrenr switch (fin->fin_p) 5386145522Sdarrenr { 5387145522Sdarrenr case IPPROTO_TCP : 538853642Sguido nflags = IPN_TCP; 5389145522Sdarrenr break; 5390145522Sdarrenr case IPPROTO_UDP : 539153642Sguido nflags = IPN_UDP; 5392145522Sdarrenr break; 5393145522Sdarrenr case IPPROTO_ICMP : 5394145522Sdarrenr icmp = fin->fin_dp; 5395145522Sdarrenr 5396145522Sdarrenr /* 5397145522Sdarrenr * This is an incoming packet, so the destination is 5398145522Sdarrenr * the icmp_id and the source port equals 0 5399145522Sdarrenr */ 5400255332Scy if ((fin->fin_flx & FI_ICMPQUERY) != 0) { 5401145522Sdarrenr nflags = IPN_ICMPQUERY; 5402255332Scy dport = icmp->icmp_id; 5403145522Sdarrenr } break; 5404145522Sdarrenr default : 5405145522Sdarrenr break; 5406145522Sdarrenr } 5407255332Scy 540853642Sguido if ((nflags & IPN_TCPUDP)) { 5409145522Sdarrenr tcp = fin->fin_dp; 5410255332Scy dport = fin->fin_data[1]; 541153642Sguido } 541253642Sguido } 541353642Sguido 541492685Sdarrenr in = fin->fin_dst; 541553642Sguido 5416255332Scy READ_ENTER(&softc->ipf_nat); 541753642Sguido 5418255332Scy if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && 5419255332Scy (nat = ipf_nat_icmperror(fin, &nflags, NAT_INBOUND))) 5420145522Sdarrenr /*EMPTY*/; 5421255332Scy else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) 542253642Sguido natadd = 0; 5423255332Scy else if ((nat = ipf_nat_inlookup(fin, nflags|NAT_SEARCH, 5424255332Scy (u_int)fin->fin_p, 5425255332Scy fin->fin_src, in))) { 542653642Sguido nflags = nat->nat_flags; 5427255332Scy } else if (fin->fin_off == 0) { 5428255332Scy u_32_t hv, msk, rmsk = 0; 5429145522Sdarrenr 543053642Sguido /* 543153642Sguido * If there is no current entry in the nat table for this IP#, 543253642Sguido * create one for it (if there is a matching rule). 543353642Sguido */ 543453642Sguidomaskloop: 5435255332Scy msk = softn->ipf_nat_rdr_active_masks[rmsk]; 5436255332Scy iph = in.s_addr & msk; 5437255332Scy hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_rdrrules_sz); 5438255332Scyretry_roundrobin: 5439255332Scy /* TRACE (iph,msk,rmsk,hv,softn->ipf_nat_rdrrules_sz) */ 5440255332Scy for (np = softn->ipf_nat_rdr_rules[hv]; np; np = npnext) { 5441255332Scy npnext = np->in_rnext; 5442145522Sdarrenr if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) 544360852Sdarrenr continue; 5444255332Scy if (np->in_v[0] != 4) 5445138947Sdarrenr continue; 5446255332Scy if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p)) 5447145522Sdarrenr continue; 5448145522Sdarrenr if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) 5449145522Sdarrenr continue; 545060852Sdarrenr if (np->in_flags & IPN_FILTER) { 5451255332Scy switch (ipf_nat_match(fin, np)) 5452255332Scy { 5453255332Scy case 0 : 545460852Sdarrenr continue; 5455255332Scy case -1 : 5456255332Scy rval = -1; 5457255332Scy goto inmatchfail; 5458255332Scy case 1 : 5459255332Scy default : 5460255332Scy break; 5461255332Scy } 5462145522Sdarrenr } else { 5463255332Scy if ((in.s_addr & np->in_odstmsk) != 5464255332Scy np->in_odstaddr) 5465145522Sdarrenr continue; 5466255332Scy if (np->in_odport && 5467255332Scy ((np->in_dtop < dport) || 5468255332Scy (dport < np->in_odport))) 5469145522Sdarrenr continue; 5470145522Sdarrenr } 5471145522Sdarrenr 5472255332Scy if (np->in_plabel != -1) { 5473255332Scy if (!ipf_proxy_ok(fin, tcp, np)) { 5474145522Sdarrenr continue; 547553642Sguido } 5476145522Sdarrenr } 5477145522Sdarrenr 5478255332Scy if (np->in_flags & IPN_NO) { 5479145522Sdarrenr np->in_hits++; 5480145522Sdarrenr break; 5481255332Scy } 548260852Sdarrenr 5483255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 5484255332Scy /* 5485255332Scy * If we've matched a round-robin rule but it has 5486255332Scy * moved in the list since we got it, start over as 5487255332Scy * this is now no longer correct. 5488255332Scy */ 5489255332Scy if (npnext != np->in_rnext) { 5490255332Scy if ((np->in_flags & IPN_ROUNDR) != 0) { 5491255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 5492255332Scy goto retry_roundrobin; 5493255332Scy } 5494255332Scy npnext = np->in_rnext; 5495145522Sdarrenr } 5496255332Scy 5497255332Scy nat = ipf_nat_add(fin, np, NULL, nflags, NAT_INBOUND); 5498255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 5499255332Scy if (nat != NULL) { 5500255332Scy natfailed = 0; 5501255332Scy break; 5502145522Sdarrenr } 5503255332Scy natfailed = -1; 550453642Sguido } 5505255332Scy if ((np == NULL) && (rmsk < softn->ipf_nat_rdr_max)) { 5506255332Scy rmsk++; 5507255332Scy goto maskloop; 5508255332Scy } 550953642Sguido } 5510255332Scy 5511145522Sdarrenr if (nat != NULL) { 5512255332Scy rval = ipf_nat_in(fin, nat, natadd, nflags); 5513145522Sdarrenr if (rval == 1) { 5514145522Sdarrenr MUTEX_ENTER(&nat->nat_lock); 5515255332Scy ipf_nat_update(fin, nat); 5516255332Scy nat->nat_bytes[0] += fin->fin_plen; 5517255332Scy nat->nat_pkts[0]++; 5518255332Scy fin->fin_pktnum = nat->nat_pkts[0]; 5519145522Sdarrenr MUTEX_EXIT(&nat->nat_lock); 5520145522Sdarrenr } 5521145522Sdarrenr } else 5522145522Sdarrenr rval = natfailed; 5523255332Scyinmatchfail: 5524255332Scy RWLOCK_EXIT(&softc->ipf_nat); 552572006Sdarrenr 5526255332Scy switch (rval) 5527255332Scy { 5528255332Scy case -1 : 5529255332Scy if (passp != NULL) { 5530255332Scy DT1(frb_natv4in, fr_info_t *, fin); 5531255332Scy NBUMPSIDED(0, ns_drop); 5532145522Sdarrenr *passp = FR_BLOCK; 5533255332Scy fin->fin_reason = FRB_NATV4; 5534255332Scy } 5535145522Sdarrenr fin->fin_flx |= FI_BADNAT; 5536255332Scy NBUMPSIDED(0, ns_badnat); 5537255332Scy break; 5538255332Scy case 0 : 5539255332Scy NBUMPSIDE(0, ns_ignored); 5540255332Scy break; 5541255332Scy case 1 : 5542255332Scy NBUMPSIDE(0, ns_translated); 5543255332Scy break; 5544145522Sdarrenr } 5545145522Sdarrenr return rval; 5546145522Sdarrenr} 5547145522Sdarrenr 5548145522Sdarrenr 5549145522Sdarrenr/* ------------------------------------------------------------------------ */ 5550255332Scy/* Function: ipf_nat_in */ 5551145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 5552145522Sdarrenr/* 1 == packet was successfully translated. */ 5553145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 5554145522Sdarrenr/* nat(I) - pointer to NAT structure */ 5555145522Sdarrenr/* natadd(I) - flag indicating if it is safe to add frag cache */ 5556145522Sdarrenr/* nflags(I) - NAT flags set for this packet */ 5557255332Scy/* Locks Held: ipf_nat(READ) */ 5558145522Sdarrenr/* */ 5559145522Sdarrenr/* Translate a packet coming "in" on an interface. */ 5560145522Sdarrenr/* ------------------------------------------------------------------------ */ 5561255332Scyint 5562255332Scyipf_nat_in(fin, nat, natadd, nflags) 5563255332Scy fr_info_t *fin; 5564255332Scy nat_t *nat; 5565255332Scy int natadd; 5566255332Scy u_32_t nflags; 5567145522Sdarrenr{ 5568255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 5569255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 5570255332Scy u_32_t sumd, ipsumd, sum1, sum2; 5571145522Sdarrenr icmphdr_t *icmp; 5572145522Sdarrenr tcphdr_t *tcp; 5573145522Sdarrenr ipnat_t *np; 5574255332Scy int skip; 5575145522Sdarrenr int i; 5576145522Sdarrenr 5577145522Sdarrenr tcp = NULL; 5578145522Sdarrenr np = nat->nat_ptr; 5579145522Sdarrenr fin->fin_fr = nat->nat_fr; 5580145522Sdarrenr 5581145522Sdarrenr if (np != NULL) { 5582145522Sdarrenr if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) 5583255332Scy (void) ipf_frag_natnew(softc, fin, 0, nat); 5584145522Sdarrenr 5585145522Sdarrenr /* ------------------------------------------------------------- */ 5586255332Scy /* A few quick notes: */ 5587255332Scy /* Following are test conditions prior to calling the */ 5588255332Scy /* ipf_proxy_check routine. */ 5589255332Scy /* */ 5590255332Scy /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 5591255332Scy /* with a map rule, we attempt to match the packet's */ 5592255332Scy /* source port against in_dport, otherwise we'd compare the */ 5593255332Scy /* packet's destination. */ 5594145522Sdarrenr /* ------------------------------------------------------------- */ 5595145522Sdarrenr if (np->in_apr != NULL) { 5596255332Scy i = ipf_proxy_check(fin, nat); 559760852Sdarrenr if (i == -1) { 5598255332Scy NBUMPSIDED(0, ns_ipf_proxy_fail); 5599145522Sdarrenr return -1; 560060852Sdarrenr } 560160852Sdarrenr } 5602145522Sdarrenr } 560353642Sguido 5604255332Scy ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); 5605145522Sdarrenr 5606255332Scy ipsumd = nat->nat_ipsumd; 5607145522Sdarrenr /* 5608145522Sdarrenr * Fix up checksums, not by recalculating them, but 5609145522Sdarrenr * simply computing adjustments. 5610145522Sdarrenr * Why only do this for some platforms on inbound packets ? 5611145522Sdarrenr * Because for those that it is done, IP processing is yet to happen 5612145522Sdarrenr * and so the IPv4 header checksum has not yet been evaluated. 5613145522Sdarrenr * Perhaps it should always be done for the benefit of things like 5614145522Sdarrenr * fast forwarding (so that it doesn't need to be recomputed) but with 5615145522Sdarrenr * header checksum offloading, perhaps it is a moot point. 5616145522Sdarrenr */ 5617255332Scy 5618255332Scy switch (nat->nat_dir) 5619255332Scy { 5620255332Scy case NAT_INBOUND : 5621255332Scy if ((fin->fin_flx & FI_ICMPERR) == 0) { 5622255332Scy fin->fin_ip->ip_src = nat->nat_nsrcip; 5623255332Scy fin->fin_saddr = nat->nat_nsrcaddr; 5624255332Scy } else { 5625255332Scy sum1 = nat->nat_osrcaddr; 5626255332Scy sum2 = nat->nat_nsrcaddr; 5627255332Scy CALC_SUMD(sum1, sum2, sumd); 5628255332Scy ipsumd -= sumd; 5629255332Scy } 5630255332Scy fin->fin_ip->ip_dst = nat->nat_ndstip; 5631255332Scy fin->fin_daddr = nat->nat_ndstaddr; 5632145522Sdarrenr#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 5633145522Sdarrenr defined(__osf__) || defined(linux) 5634255332Scy ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, ipsumd, 0); 5635145522Sdarrenr#endif 5636255332Scy break; 5637145522Sdarrenr 5638255332Scy case NAT_OUTBOUND : 5639255332Scy if ((fin->fin_flx & FI_ICMPERR) == 0) { 5640255332Scy fin->fin_ip->ip_src = nat->nat_odstip; 5641255332Scy fin->fin_saddr = nat->nat_odstaddr; 5642255332Scy } else { 5643255332Scy sum1 = nat->nat_odstaddr; 5644255332Scy sum2 = nat->nat_ndstaddr; 5645255332Scy CALC_SUMD(sum1, sum2, sumd); 5646255332Scy ipsumd -= sumd; 5647255332Scy } 5648255332Scy fin->fin_ip->ip_dst = nat->nat_osrcip; 5649255332Scy fin->fin_daddr = nat->nat_osrcaddr; 5650255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 5651255332Scy defined(__osf__) || defined(linux) 5652255332Scy ipf_fix_incksum(0, &fin->fin_ip->ip_sum, ipsumd, 0); 5653255332Scy#endif 5654255332Scy break; 5655255332Scy 5656255332Scy case NAT_DIVERTIN : 5657255332Scy { 5658255332Scy udphdr_t *uh; 5659255332Scy ip_t *ip; 5660255332Scy mb_t *m; 5661255332Scy 5662255332Scy m = M_DUP(np->in_divmp); 5663255332Scy if (m == NULL) { 5664255332Scy NBUMPSIDED(0, ns_divert_dup); 5665255332Scy return -1; 5666255332Scy } 5667255332Scy 5668255332Scy ip = MTOD(m, ip_t *); 5669255332Scy ip->ip_id = htons(ipf_nextipid(fin)); 5670255332Scy sum1 = ntohs(ip->ip_len); 5671255332Scy ip->ip_len = ntohs(ip->ip_len); 5672255332Scy ip->ip_len += fin->fin_plen; 5673255332Scy ip->ip_len = htons(ip->ip_len); 5674255332Scy 5675255332Scy uh = (udphdr_t *)(ip + 1); 5676255332Scy uh->uh_ulen += fin->fin_plen; 5677255332Scy uh->uh_ulen = htons(uh->uh_ulen); 5678255332Scy 5679255332Scy sum2 = ntohs(ip->ip_id) + ntohs(ip->ip_len); 5680255332Scy sum2 += ntohs(ip->ip_off) & IP_DF; 5681255332Scy CALC_SUMD(sum1, sum2, sumd); 5682255332Scy 5683255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 5684255332Scy defined(__osf__) || defined(linux) 5685255332Scy ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0); 5686255332Scy#endif 5687255332Scy PREP_MB_T(fin, m); 5688255332Scy 5689255332Scy fin->fin_ip = ip; 5690255332Scy fin->fin_plen += sizeof(ip_t) + 8; /* UDP + new IPv4 hdr */ 5691255332Scy fin->fin_dlen += sizeof(ip_t) + 8; /* UDP + old IPv4 hdr */ 5692255332Scy 5693255332Scy nflags &= ~IPN_TCPUDPICMP; 5694255332Scy 5695255332Scy break; 5696255332Scy } 5697255332Scy 5698255332Scy case NAT_DIVERTOUT : 5699255332Scy { 5700255332Scy mb_t *m; 5701255332Scy 5702255332Scy skip = ipf_nat_decap(fin, nat); 5703255332Scy if (skip <= 0) { 5704255332Scy NBUMPSIDED(0, ns_decap_fail); 5705255332Scy return -1; 5706255332Scy } 5707255332Scy 5708255332Scy m = fin->fin_m; 5709255332Scy 5710255332Scy#if defined(MENTAT) && defined(_KERNEL) 5711255332Scy m->b_rptr += skip; 5712255332Scy#else 5713255332Scy m->m_data += skip; 5714255332Scy m->m_len -= skip; 5715255332Scy 5716255332Scy# ifdef M_PKTHDR 5717255332Scy if (m->m_flags & M_PKTHDR) 5718255332Scy m->m_pkthdr.len -= skip; 5719255332Scy# endif 5720255332Scy#endif 5721255332Scy 5722255332Scy ipf_nat_update(fin, nat); 5723255332Scy nflags &= ~IPN_TCPUDPICMP; 5724255332Scy fin->fin_flx |= FI_NATED; 5725255332Scy if (np != NULL && np->in_tag.ipt_num[0] != 0) 5726255332Scy fin->fin_nattag = &np->in_tag; 5727255332Scy return 1; 5728255332Scy /* NOTREACHED */ 5729255332Scy } 5730255332Scy } 5731255332Scy if (nflags & IPN_TCPUDP) 5732255332Scy tcp = fin->fin_dp; 5733255332Scy 5734145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 5735255332Scy u_short *csump; 5736255332Scy 5737255332Scy if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) { 5738255332Scy switch (nat->nat_dir) 5739255332Scy { 5740255332Scy case NAT_INBOUND : 5741255332Scy tcp->th_sport = nat->nat_nsport; 5742255332Scy fin->fin_data[0] = ntohs(nat->nat_nsport); 5743255332Scy tcp->th_dport = nat->nat_ndport; 5744255332Scy fin->fin_data[1] = ntohs(nat->nat_ndport); 5745255332Scy break; 5746255332Scy 5747255332Scy case NAT_OUTBOUND : 5748255332Scy tcp->th_sport = nat->nat_odport; 5749255332Scy fin->fin_data[0] = ntohs(nat->nat_odport); 5750255332Scy tcp->th_dport = nat->nat_osport; 5751255332Scy fin->fin_data[1] = ntohs(nat->nat_osport); 5752255332Scy break; 5753255332Scy } 575492685Sdarrenr } 575553642Sguido 5756145522Sdarrenr 5757255332Scy if ((nat->nat_odport != 0) && (nflags & IPN_ICMPQUERY)) { 5758145522Sdarrenr icmp = fin->fin_dp; 5759145522Sdarrenr 5760255332Scy icmp->icmp_id = nat->nat_nicmpid; 5761145522Sdarrenr } 5762145522Sdarrenr 5763255332Scy csump = ipf_nat_proto(fin, nat, nflags); 5764255332Scy 5765255332Scy /* 5766255332Scy * The above comments do not hold for layer 4 (or higher) 5767255332Scy * checksums... 5768255332Scy */ 5769255332Scy if (csump != NULL) { 5770255332Scy if (nat->nat_dir == NAT_OUTBOUND) 5771255332Scy ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0); 5772255332Scy else 5773255332Scy ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0); 5774255332Scy } 5775145522Sdarrenr } 5776145522Sdarrenr 5777145522Sdarrenr fin->fin_flx |= FI_NATED; 5778145522Sdarrenr if (np != NULL && np->in_tag.ipt_num[0] != 0) 5779145522Sdarrenr fin->fin_nattag = &np->in_tag; 5780145522Sdarrenr return 1; 5781145522Sdarrenr} 5782130886Sdarrenr 5783130886Sdarrenr 5784145522Sdarrenr/* ------------------------------------------------------------------------ */ 5785255332Scy/* Function: ipf_nat_proto */ 5786145522Sdarrenr/* Returns: u_short* - pointer to transport header checksum to update, */ 5787145522Sdarrenr/* NULL if the transport protocol is not recognised */ 5788145522Sdarrenr/* as needing a checksum update. */ 5789145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 5790145522Sdarrenr/* nat(I) - pointer to NAT structure */ 5791145522Sdarrenr/* nflags(I) - NAT flags set for this packet */ 5792145522Sdarrenr/* */ 5793145522Sdarrenr/* Return the pointer to the checksum field for each protocol so understood.*/ 5794145522Sdarrenr/* If support for making other changes to a protocol header is required, */ 5795145522Sdarrenr/* that is not strictly 'address' translation, such as clamping the MSS in */ 5796145522Sdarrenr/* TCP down to a specific value, then do it from here. */ 5797145522Sdarrenr/* ------------------------------------------------------------------------ */ 5798255332Scyu_short * 5799255332Scyipf_nat_proto(fin, nat, nflags) 5800255332Scy fr_info_t *fin; 5801255332Scy nat_t *nat; 5802255332Scy u_int nflags; 5803145522Sdarrenr{ 5804145522Sdarrenr icmphdr_t *icmp; 5805145522Sdarrenr u_short *csump; 5806145522Sdarrenr tcphdr_t *tcp; 5807145522Sdarrenr udphdr_t *udp; 580853642Sguido 5809145522Sdarrenr csump = NULL; 5810145522Sdarrenr if (fin->fin_out == 0) { 5811255332Scy fin->fin_rev = (nat->nat_dir & NAT_OUTBOUND); 5812145522Sdarrenr } else { 5813255332Scy fin->fin_rev = ((nat->nat_dir & NAT_OUTBOUND) == 0); 5814145522Sdarrenr } 581553642Sguido 5816145522Sdarrenr switch (fin->fin_p) 5817145522Sdarrenr { 5818145522Sdarrenr case IPPROTO_TCP : 5819145522Sdarrenr tcp = fin->fin_dp; 5820110916Sdarrenr 5821255332Scy if ((nflags & IPN_TCP) != 0) 5822255332Scy csump = &tcp->th_sum; 582353642Sguido 5824145522Sdarrenr /* 5825145522Sdarrenr * Do a MSS CLAMPING on a SYN packet, 5826145522Sdarrenr * only deal IPv4 for now. 5827145522Sdarrenr */ 5828145522Sdarrenr if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0) 5829255332Scy ipf_nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump); 583060852Sdarrenr 5831145522Sdarrenr break; 5832145522Sdarrenr 5833145522Sdarrenr case IPPROTO_UDP : 5834145522Sdarrenr udp = fin->fin_dp; 5835145522Sdarrenr 5836255332Scy if ((nflags & IPN_UDP) != 0) { 5837255332Scy if (udp->uh_sum != 0) 5838255332Scy csump = &udp->uh_sum; 5839255332Scy } 5840145522Sdarrenr break; 5841145522Sdarrenr 5842145522Sdarrenr case IPPROTO_ICMP : 5843145522Sdarrenr icmp = fin->fin_dp; 5844145522Sdarrenr 5845145522Sdarrenr if ((nflags & IPN_ICMPQUERY) != 0) { 5846145522Sdarrenr if (icmp->icmp_cksum != 0) 5847145522Sdarrenr csump = &icmp->icmp_cksum; 584853642Sguido } 5849145522Sdarrenr break; 585053642Sguido 5851255332Scy#ifdef USE_INET6 5852255332Scy case IPPROTO_ICMPV6 : 5853255332Scy { 5854255332Scy struct icmp6_hdr *icmp6 = (struct icmp6_hdr *)fin->fin_dp; 585553642Sguido 5856255332Scy icmp6 = fin->fin_dp; 5857145522Sdarrenr 5858255332Scy if ((nflags & IPN_ICMPQUERY) != 0) { 5859255332Scy if (icmp6->icmp6_cksum != 0) 5860255332Scy csump = &icmp6->icmp6_cksum; 5861255332Scy } 5862255332Scy break; 5863255332Scy } 5864255332Scy#endif 5865145522Sdarrenr } 5866255332Scy return csump; 586753642Sguido} 586853642Sguido 586953642Sguido 5870145522Sdarrenr/* ------------------------------------------------------------------------ */ 5871255332Scy/* Function: ipf_nat_expire */ 5872145522Sdarrenr/* Returns: Nil */ 5873255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 5874145522Sdarrenr/* */ 5875145522Sdarrenr/* Check all of the timeout queues for entries at the top which need to be */ 5876145522Sdarrenr/* expired. */ 5877145522Sdarrenr/* ------------------------------------------------------------------------ */ 5878255332Scyvoid 5879255332Scyipf_nat_expire(softc) 5880255332Scy ipf_main_softc_t *softc; 588153642Sguido{ 5882255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 5883145522Sdarrenr ipftq_t *ifq, *ifqnext; 5884145522Sdarrenr ipftqent_t *tqe, *tqn; 5885161356Sguido int i; 5886153876Sguido SPL_INT(s); 588753642Sguido 588853642Sguido SPL_NET(s); 5889255332Scy WRITE_ENTER(&softc->ipf_nat); 5890255332Scy for (ifq = softn->ipf_nat_tcptq, i = 0; ifq != NULL; 5891255332Scy ifq = ifq->ifq_next) { 5892145522Sdarrenr for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { 5893255332Scy if (tqe->tqe_die > softc->ipf_ticks) 5894145522Sdarrenr break; 5895145522Sdarrenr tqn = tqe->tqe_next; 5896255332Scy ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE); 589753642Sguido } 589853642Sguido } 5899145522Sdarrenr 5900255332Scy for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifq->ifq_next) { 5901145522Sdarrenr for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { 5902255332Scy if (tqe->tqe_die > softc->ipf_ticks) 5903145522Sdarrenr break; 5904145522Sdarrenr tqn = tqe->tqe_next; 5905255332Scy ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE); 5906145522Sdarrenr } 5907145522Sdarrenr } 5908145522Sdarrenr 5909255332Scy for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) { 5910145522Sdarrenr ifqnext = ifq->ifq_next; 5911145522Sdarrenr 5912145522Sdarrenr if (((ifq->ifq_flags & IFQF_DELETE) != 0) && 5913145522Sdarrenr (ifq->ifq_ref == 0)) { 5914255332Scy ipf_freetimeoutqueue(softc, ifq); 5915145522Sdarrenr } 5916145522Sdarrenr } 5917145522Sdarrenr 5918255332Scy if (softn->ipf_nat_doflush != 0) { 5919255332Scy ipf_nat_extraflush(softc, softn, 2); 5920255332Scy softn->ipf_nat_doflush = 0; 5921170268Sdarrenr } 5922170268Sdarrenr 5923255332Scy RWLOCK_EXIT(&softc->ipf_nat); 592453642Sguido SPL_X(s); 592553642Sguido} 592653642Sguido 592753642Sguido 5928145522Sdarrenr/* ------------------------------------------------------------------------ */ 5929255332Scy/* Function: ipf_nat_sync */ 5930145522Sdarrenr/* Returns: Nil */ 5931255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 5932255332Scy/* ifp(I) - pointer to network interface */ 5933145522Sdarrenr/* */ 5934145522Sdarrenr/* Walk through all of the currently active NAT sessions, looking for those */ 5935145522Sdarrenr/* which need to have their translated address updated. */ 5936145522Sdarrenr/* ------------------------------------------------------------------------ */ 5937255332Scyvoid 5938255332Scyipf_nat_sync(softc, ifp) 5939255332Scy ipf_main_softc_t *softc; 5940255332Scy void *ifp; 594153642Sguido{ 5942255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 5943145522Sdarrenr u_32_t sum1, sum2, sumd; 5944255332Scy i6addr_t in; 5945145522Sdarrenr ipnat_t *n; 5946145522Sdarrenr nat_t *nat; 594753642Sguido void *ifp2; 5948255332Scy int idx; 5949153876Sguido SPL_INT(s); 595053642Sguido 5951255332Scy if (softc->ipf_running <= 0) 5952145522Sdarrenr return; 5953145522Sdarrenr 595453642Sguido /* 595553642Sguido * Change IP addresses for NAT sessions for any protocol except TCP 5956145522Sdarrenr * since it will break the TCP connection anyway. The only rules 5957145522Sdarrenr * which will get changed are those which are "map ... -> 0/32", 5958145522Sdarrenr * where the rule specifies the address is taken from the interface. 595953642Sguido */ 596053642Sguido SPL_NET(s); 5961255332Scy WRITE_ENTER(&softc->ipf_nat); 5962145522Sdarrenr 5963255332Scy if (softc->ipf_running <= 0) { 5964255332Scy RWLOCK_EXIT(&softc->ipf_nat); 5965145522Sdarrenr return; 5966145522Sdarrenr } 5967145522Sdarrenr 5968255332Scy for (nat = softn->ipf_nat_instances; nat; nat = nat->nat_next) { 5969145522Sdarrenr if ((nat->nat_flags & IPN_TCP) != 0) 5970145522Sdarrenr continue; 5971255332Scy 5972145522Sdarrenr n = nat->nat_ptr; 5973255332Scy if (n != NULL) { 5974255332Scy if (n->in_v[1] == 4) { 5975255332Scy if (n->in_redir & NAT_MAP) { 5976255332Scy if ((n->in_nsrcaddr != 0) || 5977255332Scy (n->in_nsrcmsk != 0xffffffff)) 5978255332Scy continue; 5979255332Scy } else if (n->in_redir & NAT_REDIRECT) { 5980255332Scy if ((n->in_ndstaddr != 0) || 5981255332Scy (n->in_ndstmsk != 0xffffffff)) 5982255332Scy continue; 5983255332Scy } 5984255332Scy } 5985255332Scy#ifdef USE_INET6 5986255332Scy if (n->in_v[1] == 4) { 5987255332Scy if (n->in_redir & NAT_MAP) { 5988255332Scy if (!IP6_ISZERO(&n->in_nsrcaddr) || 5989255332Scy !IP6_ISONES(&n->in_nsrcmsk)) 5990255332Scy continue; 5991255332Scy } else if (n->in_redir & NAT_REDIRECT) { 5992255332Scy if (!IP6_ISZERO(&n->in_ndstaddr) || 5993255332Scy !IP6_ISONES(&n->in_ndstmsk)) 5994255332Scy continue; 5995255332Scy } 5996255332Scy } 5997255332Scy#endif 5998255332Scy } 5999255332Scy 6000145522Sdarrenr if (((ifp == NULL) || (ifp == nat->nat_ifps[0]) || 6001145522Sdarrenr (ifp == nat->nat_ifps[1]))) { 6002255332Scy nat->nat_ifps[0] = GETIFP(nat->nat_ifnames[0], 6003255332Scy nat->nat_v[0]); 6004255332Scy if ((nat->nat_ifps[0] != NULL) && 6005255332Scy (nat->nat_ifps[0] != (void *)-1)) { 6006255332Scy nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); 6007255332Scy } 6008145522Sdarrenr if (nat->nat_ifnames[1][0] != '\0') { 6009145522Sdarrenr nat->nat_ifps[1] = GETIFP(nat->nat_ifnames[1], 6010255332Scy nat->nat_v[1]); 6011255332Scy } else { 6012145522Sdarrenr nat->nat_ifps[1] = nat->nat_ifps[0]; 6013255332Scy } 6014255332Scy if ((nat->nat_ifps[1] != NULL) && 6015255332Scy (nat->nat_ifps[1] != (void *)-1)) { 6016255332Scy nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); 6017255332Scy } 6018145522Sdarrenr ifp2 = nat->nat_ifps[0]; 6019145522Sdarrenr if (ifp2 == NULL) 6020145522Sdarrenr continue; 6021145522Sdarrenr 602253642Sguido /* 602353642Sguido * Change the map-to address to be the same as the 602453642Sguido * new one. 602553642Sguido */ 6026255332Scy sum1 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6); 6027255332Scy if (ipf_ifpaddr(softc, nat->nat_v[0], FRI_NORMAL, ifp2, 6028255332Scy &in, NULL) != -1) { 6029255332Scy if (nat->nat_v[0] == 4) 6030255332Scy nat->nat_nsrcip = in.in4; 6031255332Scy } 6032255332Scy sum2 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6); 603353642Sguido 603453642Sguido if (sum1 == sum2) 603553642Sguido continue; 603653642Sguido /* 603753642Sguido * Readjust the checksum adjustment to take into 603853642Sguido * account the new IP#. 603953642Sguido */ 604053642Sguido CALC_SUMD(sum1, sum2, sumd); 604155929Sguido /* XXX - dont change for TCP when solaris does 604255929Sguido * hardware checksumming. 604355929Sguido */ 604455929Sguido sumd += nat->nat_sumd[0]; 604555929Sguido nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 604655929Sguido nat->nat_sumd[1] = nat->nat_sumd[0]; 604753642Sguido } 6048145522Sdarrenr } 604953642Sguido 6050255332Scy for (n = softn->ipf_nat_list; (n != NULL); n = n->in_next) { 6051255332Scy char *base = n->in_names; 6052255332Scy 6053145522Sdarrenr if ((ifp == NULL) || (n->in_ifps[0] == ifp)) 6054255332Scy n->in_ifps[0] = ipf_resolvenic(softc, 6055255332Scy base + n->in_ifnames[0], 6056255332Scy n->in_v[0]); 6057145522Sdarrenr if ((ifp == NULL) || (n->in_ifps[1] == ifp)) 6058255332Scy n->in_ifps[1] = ipf_resolvenic(softc, 6059255332Scy base + n->in_ifnames[1], 6060255332Scy n->in_v[1]); 6061255332Scy 6062255332Scy if (n->in_redir & NAT_REDIRECT) 6063255332Scy idx = 1; 6064255332Scy else 6065255332Scy idx = 0; 6066255332Scy 6067255332Scy if (((ifp == NULL) || (n->in_ifps[idx] == ifp)) && 6068255332Scy (n->in_ifps[idx] != NULL && 6069255332Scy n->in_ifps[idx] != (void *)-1)) { 6070255332Scy 6071255332Scy ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, 6072255332Scy 0, n->in_ifps[idx]); 6073255332Scy ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, 6074255332Scy 0, n->in_ifps[idx]); 6075255332Scy ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, 6076255332Scy 0, n->in_ifps[idx]); 6077255332Scy ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, 6078255332Scy 0, n->in_ifps[idx]); 6079255332Scy } 6080145522Sdarrenr } 6081255332Scy RWLOCK_EXIT(&softc->ipf_nat); 608253642Sguido SPL_X(s); 608353642Sguido} 608453642Sguido 608553642Sguido 6086145522Sdarrenr/* ------------------------------------------------------------------------ */ 6087255332Scy/* Function: ipf_nat_icmpquerytype */ 6088145522Sdarrenr/* Returns: int - 1 == success, 0 == failure */ 6089145522Sdarrenr/* Parameters: icmptype(I) - ICMP type number */ 6090145522Sdarrenr/* */ 6091145522Sdarrenr/* Tests to see if the ICMP type number passed is a query/response type or */ 6092145522Sdarrenr/* not. */ 6093145522Sdarrenr/* ------------------------------------------------------------------------ */ 6094255332Scystatic int 6095255332Scyipf_nat_icmpquerytype(icmptype) 6096255332Scy int icmptype; 6097145522Sdarrenr{ 6098145522Sdarrenr 6099145522Sdarrenr /* 6100145522Sdarrenr * For the ICMP query NAT code, it is essential that both the query 6101145522Sdarrenr * and the reply match on the NAT rule. Because the NAT structure 6102145522Sdarrenr * does not keep track of the icmptype, and a single NAT structure 6103145522Sdarrenr * is used for all icmp types with the same src, dest and id, we 6104145522Sdarrenr * simply define the replies as queries as well. The funny thing is, 6105145522Sdarrenr * altough it seems silly to call a reply a query, this is exactly 6106145522Sdarrenr * as it is defined in the IPv4 specification 6107145522Sdarrenr */ 6108145522Sdarrenr switch (icmptype) 6109145522Sdarrenr { 6110145522Sdarrenr case ICMP_ECHOREPLY: 6111145522Sdarrenr case ICMP_ECHO: 6112145522Sdarrenr /* route aedvertisement/solliciation is currently unsupported: */ 6113145522Sdarrenr /* it would require rewriting the ICMP data section */ 6114145522Sdarrenr case ICMP_TSTAMP: 6115145522Sdarrenr case ICMP_TSTAMPREPLY: 6116145522Sdarrenr case ICMP_IREQ: 6117145522Sdarrenr case ICMP_IREQREPLY: 6118145522Sdarrenr case ICMP_MASKREQ: 6119145522Sdarrenr case ICMP_MASKREPLY: 6120145522Sdarrenr return 1; 6121145522Sdarrenr default: 6122145522Sdarrenr return 0; 6123145522Sdarrenr } 6124145522Sdarrenr} 6125145522Sdarrenr 6126145522Sdarrenr 6127145522Sdarrenr/* ------------------------------------------------------------------------ */ 6128145522Sdarrenr/* Function: nat_log */ 6129145522Sdarrenr/* Returns: Nil */ 6130255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6131255332Scy/* softn(I) - pointer to NAT context structure */ 6132255332Scy/* nat(I) - pointer to NAT structure */ 6133255332Scy/* action(I) - action related to NAT structure being performed */ 6134145522Sdarrenr/* */ 6135145522Sdarrenr/* Creates a NAT log entry. */ 6136145522Sdarrenr/* ------------------------------------------------------------------------ */ 6137255332Scyvoid 6138255332Scyipf_nat_log(softc, softn, nat, action) 6139255332Scy ipf_main_softc_t *softc; 6140255332Scy ipf_nat_softc_t *softn; 6141255332Scy struct nat *nat; 6142255332Scy u_int action; 614353642Sguido{ 6144145522Sdarrenr#ifdef IPFILTER_LOG 6145139005Smlaier# ifndef LARGE_NAT 614653642Sguido struct ipnat *np; 6147138979Sdarrenr int rulen; 6148138979Sdarrenr# endif 614953642Sguido struct natlog natl; 615053642Sguido void *items[1]; 615153642Sguido size_t sizes[1]; 6152138979Sdarrenr int types[1]; 615353642Sguido 6154255332Scy bcopy((char *)&nat->nat_osrc6, (char *)&natl.nl_osrcip, 6155255332Scy sizeof(natl.nl_osrcip)); 6156255332Scy bcopy((char *)&nat->nat_nsrc6, (char *)&natl.nl_nsrcip, 6157255332Scy sizeof(natl.nl_nsrcip)); 6158255332Scy bcopy((char *)&nat->nat_odst6, (char *)&natl.nl_odstip, 6159255332Scy sizeof(natl.nl_odstip)); 6160255332Scy bcopy((char *)&nat->nat_ndst6, (char *)&natl.nl_ndstip, 6161255332Scy sizeof(natl.nl_ndstip)); 6162255332Scy 6163145522Sdarrenr natl.nl_bytes[0] = nat->nat_bytes[0]; 6164145522Sdarrenr natl.nl_bytes[1] = nat->nat_bytes[1]; 6165145522Sdarrenr natl.nl_pkts[0] = nat->nat_pkts[0]; 6166145522Sdarrenr natl.nl_pkts[1] = nat->nat_pkts[1]; 6167255332Scy natl.nl_odstport = nat->nat_odport; 6168255332Scy natl.nl_osrcport = nat->nat_osport; 6169255332Scy natl.nl_nsrcport = nat->nat_nsport; 6170255332Scy natl.nl_ndstport = nat->nat_ndport; 6171255332Scy natl.nl_p[0] = nat->nat_pr[0]; 6172255332Scy natl.nl_p[1] = nat->nat_pr[1]; 6173255332Scy natl.nl_v[0] = nat->nat_v[0]; 6174255332Scy natl.nl_v[1] = nat->nat_v[1]; 6175255332Scy natl.nl_type = nat->nat_redir; 6176255332Scy natl.nl_action = action; 617753642Sguido natl.nl_rule = -1; 6178255332Scy 6179255332Scy bcopy(nat->nat_ifnames[0], natl.nl_ifnames[0], 6180255332Scy sizeof(nat->nat_ifnames[0])); 6181255332Scy bcopy(nat->nat_ifnames[1], natl.nl_ifnames[1], 6182255332Scy sizeof(nat->nat_ifnames[1])); 6183255332Scy 6184145522Sdarrenr# ifndef LARGE_NAT 618553642Sguido if (nat->nat_ptr != NULL) { 6186255332Scy for (rulen = 0, np = softn->ipf_nat_list; np != NULL; 6187255332Scy np = np->in_next, rulen++) 618853642Sguido if (np == nat->nat_ptr) { 618953642Sguido natl.nl_rule = rulen; 619053642Sguido break; 619153642Sguido } 619253642Sguido } 6193145522Sdarrenr# endif 619453642Sguido items[0] = &natl; 619553642Sguido sizes[0] = sizeof(natl); 619653642Sguido types[0] = 0; 619753642Sguido 6198255332Scy (void) ipf_log_items(softc, IPL_LOGNAT, NULL, items, sizes, types, 1); 6199145522Sdarrenr#endif 620053642Sguido} 620192685Sdarrenr 620292685Sdarrenr 620392685Sdarrenr#if defined(__OpenBSD__) 6204145522Sdarrenr/* ------------------------------------------------------------------------ */ 6205255332Scy/* Function: ipf_nat_ifdetach */ 6206145522Sdarrenr/* Returns: Nil */ 6207145522Sdarrenr/* Parameters: ifp(I) - pointer to network interface */ 6208145522Sdarrenr/* */ 6209145522Sdarrenr/* Compatibility interface for OpenBSD to trigger the correct updating of */ 6210145522Sdarrenr/* interface references within IPFilter. */ 6211145522Sdarrenr/* ------------------------------------------------------------------------ */ 6212255332Scyvoid 6213255332Scyipf_nat_ifdetach(ifp) 6214255332Scy void *ifp; 621592685Sdarrenr{ 6216255332Scy ipf_main_softc_t *softc; 6217255332Scy 6218255332Scy softc = ipf_get_softc(0); 6219255332Scy 6220255332Scy ipf_sync(ifp); 622192685Sdarrenr return; 622292685Sdarrenr} 622392685Sdarrenr#endif 6224110916Sdarrenr 6225110916Sdarrenr 6226145522Sdarrenr/* ------------------------------------------------------------------------ */ 6227255332Scy/* Function: ipf_nat_rule_deref */ 6228170268Sdarrenr/* Returns: Nil */ 6229255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6230255332Scy/* inp(I) - pointer to pointer to NAT rule */ 6231170268Sdarrenr/* Write Locks: ipf_nat */ 6232170268Sdarrenr/* */ 6233255332Scy/* Dropping the refernce count for a rule means that whatever held the */ 6234255332Scy/* pointer to this rule (*inp) is no longer interested in it and when the */ 6235255332Scy/* reference count drops to zero, any resources allocated for the rule can */ 6236255332Scy/* be released and the rule itself free'd. */ 6237170268Sdarrenr/* ------------------------------------------------------------------------ */ 6238255332Scyvoid 6239255332Scyipf_nat_rule_deref(softc, inp) 6240255332Scy ipf_main_softc_t *softc; 6241255332Scy ipnat_t **inp; 6242170268Sdarrenr{ 6243255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 6244255332Scy ipnat_t *n; 6245170268Sdarrenr 6246255332Scy n = *inp; 6247170268Sdarrenr *inp = NULL; 6248255332Scy n->in_use--; 6249255332Scy if (n->in_use > 0) 6250255332Scy return; 6251255332Scy 6252255332Scy if (n->in_apr != NULL) 6253255332Scy ipf_proxy_deref(n->in_apr); 6254255332Scy 6255255332Scy ipf_nat_rule_fini(softc, n); 6256255332Scy 6257255332Scy if (n->in_redir & NAT_REDIRECT) { 6258255332Scy if ((n->in_flags & IPN_PROXYRULE) == 0) { 6259255332Scy ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_rdr); 6260255332Scy } 6261255332Scy } 6262255332Scy if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { 6263255332Scy if ((n->in_flags & IPN_PROXYRULE) == 0) { 6264255332Scy ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_map); 6265255332Scy } 6266255332Scy } 6267255332Scy 6268255332Scy if (n->in_tqehead[0] != NULL) { 6269255332Scy if (ipf_deletetimeoutqueue(n->in_tqehead[0]) == 0) { 6270255332Scy ipf_freetimeoutqueue(softc, n->in_tqehead[1]); 6271255332Scy } 6272255332Scy } 6273255332Scy 6274255332Scy if (n->in_tqehead[1] != NULL) { 6275255332Scy if (ipf_deletetimeoutqueue(n->in_tqehead[1]) == 0) { 6276255332Scy ipf_freetimeoutqueue(softc, n->in_tqehead[1]); 6277255332Scy } 6278255332Scy } 6279255332Scy 6280255332Scy if ((n->in_flags & IPN_PROXYRULE) == 0) { 6281255332Scy ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules); 6282255332Scy } 6283255332Scy 6284255332Scy MUTEX_DESTROY(&n->in_lock); 6285255332Scy 6286255332Scy KFREES(n, n->in_size); 6287255332Scy 6288255332Scy#if SOLARIS && !defined(INSTANCES) 6289255332Scy if (softn->ipf_nat_stats.ns_rules == 0) 6290255332Scy pfil_delayed_copy = 1; 6291170268Sdarrenr#endif 6292170268Sdarrenr} 6293170268Sdarrenr 6294170268Sdarrenr 6295170268Sdarrenr/* ------------------------------------------------------------------------ */ 6296255332Scy/* Function: ipf_nat_deref */ 6297145522Sdarrenr/* Returns: Nil */ 6298255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6299255332Scy/* natp(I) - pointer to pointer to NAT table entry */ 6300145522Sdarrenr/* */ 6301145522Sdarrenr/* Decrement the reference counter for this NAT table entry and free it if */ 6302145522Sdarrenr/* there are no more things using it. */ 6303172776Sdarrenr/* */ 6304172776Sdarrenr/* IF nat_ref == 1 when this function is called, then we have an orphan nat */ 6305172776Sdarrenr/* structure *because* it only gets called on paths _after_ nat_ref has been*/ 6306172776Sdarrenr/* incremented. If nat_ref == 1 then we shouldn't decrement it here */ 6307172776Sdarrenr/* because nat_delete() will do that and send nat_ref to -1. */ 6308172776Sdarrenr/* */ 6309172776Sdarrenr/* Holding the lock on nat_lock is required to serialise nat_delete() being */ 6310172776Sdarrenr/* called from a NAT flush ioctl with a deref happening because of a packet.*/ 6311145522Sdarrenr/* ------------------------------------------------------------------------ */ 6312255332Scyvoid 6313255332Scyipf_nat_deref(softc, natp) 6314255332Scy ipf_main_softc_t *softc; 6315255332Scy nat_t **natp; 6316145522Sdarrenr{ 6317145522Sdarrenr nat_t *nat; 6318145522Sdarrenr 6319145522Sdarrenr nat = *natp; 6320145522Sdarrenr *natp = NULL; 6321172776Sdarrenr 6322172776Sdarrenr MUTEX_ENTER(&nat->nat_lock); 6323172776Sdarrenr if (nat->nat_ref > 1) { 6324172776Sdarrenr nat->nat_ref--; 6325255332Scy ASSERT(nat->nat_ref >= 0); 6326172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 6327172776Sdarrenr return; 6328172776Sdarrenr } 6329172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 6330172776Sdarrenr 6331255332Scy WRITE_ENTER(&softc->ipf_nat); 6332255332Scy ipf_nat_delete(softc, nat, NL_EXPIRE); 6333255332Scy RWLOCK_EXIT(&softc->ipf_nat); 6334145522Sdarrenr} 6335145522Sdarrenr 6336145522Sdarrenr 6337145522Sdarrenr/* ------------------------------------------------------------------------ */ 6338255332Scy/* Function: ipf_nat_clone */ 6339145522Sdarrenr/* Returns: ipstate_t* - NULL == cloning failed, */ 6340145522Sdarrenr/* else pointer to new state structure */ 6341145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 6342145522Sdarrenr/* is(I) - pointer to master state structure */ 6343145522Sdarrenr/* Write Lock: ipf_nat */ 6344145522Sdarrenr/* */ 6345145522Sdarrenr/* Create a "duplcate" state table entry from the master. */ 6346145522Sdarrenr/* ------------------------------------------------------------------------ */ 6347255332Scynat_t * 6348255332Scyipf_nat_clone(fin, nat) 6349255332Scy fr_info_t *fin; 6350255332Scy nat_t *nat; 6351145522Sdarrenr{ 6352255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 6353255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 6354145522Sdarrenr frentry_t *fr; 6355145522Sdarrenr nat_t *clone; 6356145522Sdarrenr ipnat_t *np; 6357145522Sdarrenr 6358145522Sdarrenr KMALLOC(clone, nat_t *); 6359255332Scy if (clone == NULL) { 6360255332Scy NBUMPSIDED(fin->fin_out, ns_clone_nomem); 6361145522Sdarrenr return NULL; 6362255332Scy } 6363145522Sdarrenr bcopy((char *)nat, (char *)clone, sizeof(*clone)); 6364145522Sdarrenr 6365145522Sdarrenr MUTEX_NUKE(&clone->nat_lock); 6366145522Sdarrenr 6367255332Scy clone->nat_rev = fin->fin_rev; 6368153876Sguido clone->nat_aps = NULL; 6369153876Sguido /* 6370255332Scy * Initialize all these so that ipf_nat_delete() doesn't cause a crash. 6371153876Sguido */ 6372153876Sguido clone->nat_tqe.tqe_pnext = NULL; 6373153876Sguido clone->nat_tqe.tqe_next = NULL; 6374153876Sguido clone->nat_tqe.tqe_ifq = NULL; 6375153876Sguido clone->nat_tqe.tqe_parent = clone; 6376153876Sguido 6377145522Sdarrenr clone->nat_flags &= ~SI_CLONE; 6378145522Sdarrenr clone->nat_flags |= SI_CLONED; 6379145522Sdarrenr 6380153876Sguido if (clone->nat_hm) 6381153876Sguido clone->nat_hm->hm_ref++; 6382145522Sdarrenr 6383255332Scy if (ipf_nat_insert(softc, softn, clone) == -1) { 6384145522Sdarrenr KFREE(clone); 6385255332Scy NBUMPSIDED(fin->fin_out, ns_insert_fail); 6386145522Sdarrenr return NULL; 6387145522Sdarrenr } 6388255332Scy 6389145522Sdarrenr np = clone->nat_ptr; 6390145522Sdarrenr if (np != NULL) { 6391255332Scy if (softn->ipf_nat_logging) 6392255332Scy ipf_nat_log(softc, softn, clone, NL_CLONE); 6393145522Sdarrenr np->in_use++; 6394145522Sdarrenr } 6395145522Sdarrenr fr = clone->nat_fr; 6396145522Sdarrenr if (fr != NULL) { 6397145522Sdarrenr MUTEX_ENTER(&fr->fr_lock); 6398145522Sdarrenr fr->fr_ref++; 6399145522Sdarrenr MUTEX_EXIT(&fr->fr_lock); 6400145522Sdarrenr } 6401145522Sdarrenr 6402255332Scy 6403145522Sdarrenr /* 6404145522Sdarrenr * Because the clone is created outside the normal loop of things and 6405145522Sdarrenr * TCP has special needs in terms of state, initialise the timeout 6406145522Sdarrenr * state of the new NAT from here. 6407145522Sdarrenr */ 6408255332Scy if (clone->nat_pr[0] == IPPROTO_TCP) { 6409255332Scy (void) ipf_tcp_age(&clone->nat_tqe, fin, softn->ipf_nat_tcptq, 6410255332Scy clone->nat_flags, 2); 6411145522Sdarrenr } 6412255332Scy clone->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, clone); 6413255332Scy if (softn->ipf_nat_logging) 6414255332Scy ipf_nat_log(softc, softn, clone, NL_CLONE); 6415145522Sdarrenr return clone; 6416145522Sdarrenr} 6417145522Sdarrenr 6418145522Sdarrenr 6419145522Sdarrenr/* ------------------------------------------------------------------------ */ 6420255332Scy/* Function: ipf_nat_wildok */ 6421145522Sdarrenr/* Returns: int - 1 == packet's ports match wildcards */ 6422145522Sdarrenr/* 0 == packet's ports don't match wildcards */ 6423145522Sdarrenr/* Parameters: nat(I) - NAT entry */ 6424145522Sdarrenr/* sport(I) - source port */ 6425145522Sdarrenr/* dport(I) - destination port */ 6426145522Sdarrenr/* flags(I) - wildcard flags */ 6427145522Sdarrenr/* dir(I) - packet direction */ 6428145522Sdarrenr/* */ 6429145522Sdarrenr/* Use NAT entry and packet direction to determine which combination of */ 6430145522Sdarrenr/* wildcard flags should be used. */ 6431145522Sdarrenr/* ------------------------------------------------------------------------ */ 6432255332Scyint 6433255332Scyipf_nat_wildok(nat, sport, dport, flags, dir) 6434255332Scy nat_t *nat; 6435255332Scy int sport, dport, flags, dir; 6436145522Sdarrenr{ 6437145522Sdarrenr /* 6438145522Sdarrenr * When called by dir is set to 6439145522Sdarrenr * nat_inlookup NAT_INBOUND (0) 6440145522Sdarrenr * nat_outlookup NAT_OUTBOUND (1) 6441145522Sdarrenr * 6442145522Sdarrenr * We simply combine the packet's direction in dir with the original 6443145522Sdarrenr * "intended" direction of that NAT entry in nat->nat_dir to decide 6444145522Sdarrenr * which combination of wildcard flags to allow. 6445145522Sdarrenr */ 6446255332Scy switch ((dir << 1) | (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND))) 6447145522Sdarrenr { 6448145522Sdarrenr case 3: /* outbound packet / outbound entry */ 6449255332Scy if (((nat->nat_osport == sport) || 6450145522Sdarrenr (flags & SI_W_SPORT)) && 6451255332Scy ((nat->nat_odport == dport) || 6452145522Sdarrenr (flags & SI_W_DPORT))) 6453145522Sdarrenr return 1; 6454145522Sdarrenr break; 6455145522Sdarrenr case 2: /* outbound packet / inbound entry */ 6456255332Scy if (((nat->nat_osport == dport) || 6457255332Scy (flags & SI_W_SPORT)) && 6458255332Scy ((nat->nat_odport == sport) || 6459255332Scy (flags & SI_W_DPORT))) 6460145522Sdarrenr return 1; 6461145522Sdarrenr break; 6462145522Sdarrenr case 1: /* inbound packet / outbound entry */ 6463255332Scy if (((nat->nat_osport == dport) || 6464255332Scy (flags & SI_W_SPORT)) && 6465255332Scy ((nat->nat_odport == sport) || 6466255332Scy (flags & SI_W_DPORT))) 6467145522Sdarrenr return 1; 6468145522Sdarrenr break; 6469145522Sdarrenr case 0: /* inbound packet / inbound entry */ 6470255332Scy if (((nat->nat_osport == sport) || 6471145522Sdarrenr (flags & SI_W_SPORT)) && 6472255332Scy ((nat->nat_odport == dport) || 6473145522Sdarrenr (flags & SI_W_DPORT))) 6474145522Sdarrenr return 1; 6475145522Sdarrenr break; 6476145522Sdarrenr default: 6477145522Sdarrenr break; 6478145522Sdarrenr } 6479145522Sdarrenr 6480145522Sdarrenr return(0); 6481145522Sdarrenr} 6482145522Sdarrenr 6483145522Sdarrenr 6484145522Sdarrenr/* ------------------------------------------------------------------------ */ 6485145522Sdarrenr/* Function: nat_mssclamp */ 6486145522Sdarrenr/* Returns: Nil */ 6487145522Sdarrenr/* Parameters: tcp(I) - pointer to TCP header */ 6488145522Sdarrenr/* maxmss(I) - value to clamp the TCP MSS to */ 6489145522Sdarrenr/* fin(I) - pointer to packet information */ 6490145522Sdarrenr/* csump(I) - pointer to TCP checksum */ 6491145522Sdarrenr/* */ 6492145522Sdarrenr/* Check for MSS option and clamp it if necessary. If found and changed, */ 6493145522Sdarrenr/* then the TCP header checksum will be updated to reflect the change in */ 6494145522Sdarrenr/* the MSS. */ 6495145522Sdarrenr/* ------------------------------------------------------------------------ */ 6496255332Scystatic void 6497255332Scyipf_nat_mssclamp(tcp, maxmss, fin, csump) 6498255332Scy tcphdr_t *tcp; 6499255332Scy u_32_t maxmss; 6500255332Scy fr_info_t *fin; 6501255332Scy u_short *csump; 6502110916Sdarrenr{ 6503110916Sdarrenr u_char *cp, *ep, opt; 6504110916Sdarrenr int hlen, advance; 6505110916Sdarrenr u_32_t mss, sumd; 6506110916Sdarrenr 6507145522Sdarrenr hlen = TCP_OFF(tcp) << 2; 6508110916Sdarrenr if (hlen > sizeof(*tcp)) { 6509110916Sdarrenr cp = (u_char *)tcp + sizeof(*tcp); 6510110916Sdarrenr ep = (u_char *)tcp + hlen; 6511110916Sdarrenr 6512110916Sdarrenr while (cp < ep) { 6513110916Sdarrenr opt = cp[0]; 6514110916Sdarrenr if (opt == TCPOPT_EOL) 6515110916Sdarrenr break; 6516110916Sdarrenr else if (opt == TCPOPT_NOP) { 6517110916Sdarrenr cp++; 6518110916Sdarrenr continue; 6519110916Sdarrenr } 6520145522Sdarrenr 6521145522Sdarrenr if (cp + 1 >= ep) 6522110916Sdarrenr break; 6523110916Sdarrenr advance = cp[1]; 6524145522Sdarrenr if ((cp + advance > ep) || (advance <= 0)) 6525110916Sdarrenr break; 6526145522Sdarrenr switch (opt) 6527145522Sdarrenr { 6528110916Sdarrenr case TCPOPT_MAXSEG: 6529110916Sdarrenr if (advance != 4) 6530110916Sdarrenr break; 6531145522Sdarrenr mss = cp[2] * 256 + cp[3]; 6532110916Sdarrenr if (mss > maxmss) { 6533145522Sdarrenr cp[2] = maxmss / 256; 6534145522Sdarrenr cp[3] = maxmss & 0xff; 6535110916Sdarrenr CALC_SUMD(mss, maxmss, sumd); 6536255332Scy ipf_fix_outcksum(0, csump, sumd, 0); 6537110916Sdarrenr } 6538110916Sdarrenr break; 6539110916Sdarrenr default: 6540110916Sdarrenr /* ignore unknown options */ 6541110916Sdarrenr break; 6542110916Sdarrenr } 6543145522Sdarrenr 6544145522Sdarrenr cp += advance; 6545145522Sdarrenr } 6546145522Sdarrenr } 6547145522Sdarrenr} 6548145522Sdarrenr 6549145522Sdarrenr 6550145522Sdarrenr/* ------------------------------------------------------------------------ */ 6551255332Scy/* Function: ipf_nat_setqueue */ 6552145522Sdarrenr/* Returns: Nil */ 6553255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6554255332Scy/* softn(I) - pointer to NAT context structure */ 6555255332Scy/* nat(I)- pointer to NAT structure */ 6556145522Sdarrenr/* Locks: ipf_nat (read or write) */ 6557145522Sdarrenr/* */ 6558145522Sdarrenr/* Put the NAT entry on its default queue entry, using rev as a helped in */ 6559145522Sdarrenr/* determining which queue it should be placed on. */ 6560145522Sdarrenr/* ------------------------------------------------------------------------ */ 6561255332Scyvoid 6562255332Scyipf_nat_setqueue(softc, softn, nat) 6563255332Scy ipf_main_softc_t *softc; 6564255332Scy ipf_nat_softc_t *softn; 6565255332Scy nat_t *nat; 6566145522Sdarrenr{ 6567145522Sdarrenr ipftq_t *oifq, *nifq; 6568255332Scy int rev = nat->nat_rev; 6569145522Sdarrenr 6570145522Sdarrenr if (nat->nat_ptr != NULL) 6571145522Sdarrenr nifq = nat->nat_ptr->in_tqehead[rev]; 6572145522Sdarrenr else 6573145522Sdarrenr nifq = NULL; 6574145522Sdarrenr 6575145522Sdarrenr if (nifq == NULL) { 6576255332Scy switch (nat->nat_pr[0]) 6577145522Sdarrenr { 6578145522Sdarrenr case IPPROTO_UDP : 6579255332Scy nifq = &softn->ipf_nat_udptq; 6580145522Sdarrenr break; 6581145522Sdarrenr case IPPROTO_ICMP : 6582255332Scy nifq = &softn->ipf_nat_icmptq; 6583145522Sdarrenr break; 6584145522Sdarrenr case IPPROTO_TCP : 6585255332Scy nifq = softn->ipf_nat_tcptq + 6586255332Scy nat->nat_tqe.tqe_state[rev]; 6587145522Sdarrenr break; 6588145522Sdarrenr default : 6589255332Scy nifq = &softn->ipf_nat_iptq; 6590145522Sdarrenr break; 6591145522Sdarrenr } 6592145522Sdarrenr } 6593145522Sdarrenr 6594145522Sdarrenr oifq = nat->nat_tqe.tqe_ifq; 6595145522Sdarrenr /* 6596145522Sdarrenr * If it's currently on a timeout queue, move it from one queue to 6597145522Sdarrenr * another, else put it on the end of the newly determined queue. 6598145522Sdarrenr */ 6599145522Sdarrenr if (oifq != NULL) 6600255332Scy ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, nifq); 6601145522Sdarrenr else 6602255332Scy ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, nifq, nat); 6603145522Sdarrenr return; 6604145522Sdarrenr} 6605170268Sdarrenr 6606170268Sdarrenr 6607170268Sdarrenr/* ------------------------------------------------------------------------ */ 6608170268Sdarrenr/* Function: nat_getnext */ 6609170268Sdarrenr/* Returns: int - 0 == ok, else error */ 6610255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6611255332Scy/* t(I) - pointer to ipftoken structure */ 6612170268Sdarrenr/* itp(I) - pointer to ipfgeniter_t structure */ 6613170268Sdarrenr/* */ 6614170268Sdarrenr/* Fetch the next nat/ipnat structure pointer from the linked list and */ 6615170268Sdarrenr/* copy it out to the storage space pointed to by itp_data. The next item */ 6616170268Sdarrenr/* in the list to look at is put back in the ipftoken struture. */ 6617170268Sdarrenr/* ------------------------------------------------------------------------ */ 6618255332Scystatic int 6619255332Scyipf_nat_getnext(softc, t, itp, objp) 6620255332Scy ipf_main_softc_t *softc; 6621255332Scy ipftoken_t *t; 6622255332Scy ipfgeniter_t *itp; 6623255332Scy ipfobj_t *objp; 6624170268Sdarrenr{ 6625255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 6626170268Sdarrenr hostmap_t *hm, *nexthm = NULL, zerohm; 6627170268Sdarrenr ipnat_t *ipn, *nextipnat = NULL, zeroipn; 6628170268Sdarrenr nat_t *nat, *nextnat = NULL, zeronat; 6629255332Scy int error = 0; 6630255332Scy void *nnext; 6631170268Sdarrenr 6632255332Scy if (itp->igi_nitems != 1) { 6633255332Scy IPFERROR(60075); 6634172776Sdarrenr return ENOSPC; 6635255332Scy } 6636170268Sdarrenr 6637255332Scy READ_ENTER(&softc->ipf_nat); 6638170268Sdarrenr 6639170268Sdarrenr switch (itp->igi_type) 6640170268Sdarrenr { 6641170268Sdarrenr case IPFGENITER_HOSTMAP : 6642170268Sdarrenr hm = t->ipt_data; 6643170268Sdarrenr if (hm == NULL) { 6644255332Scy nexthm = softn->ipf_hm_maplist; 6645170268Sdarrenr } else { 6646170268Sdarrenr nexthm = hm->hm_next; 6647170268Sdarrenr } 6648255332Scy if (nexthm != NULL) { 6649255332Scy ATOMIC_INC32(nexthm->hm_ref); 6650255332Scy t->ipt_data = nexthm; 6651255332Scy } else { 6652255332Scy bzero(&zerohm, sizeof(zerohm)); 6653255332Scy nexthm = &zerohm; 6654255332Scy t->ipt_data = NULL; 6655255332Scy } 6656255332Scy nnext = nexthm->hm_next; 6657170268Sdarrenr break; 6658170268Sdarrenr 6659170268Sdarrenr case IPFGENITER_IPNAT : 6660170268Sdarrenr ipn = t->ipt_data; 6661170268Sdarrenr if (ipn == NULL) { 6662255332Scy nextipnat = softn->ipf_nat_list; 6663170268Sdarrenr } else { 6664170268Sdarrenr nextipnat = ipn->in_next; 6665170268Sdarrenr } 6666255332Scy if (nextipnat != NULL) { 6667255332Scy ATOMIC_INC32(nextipnat->in_use); 6668255332Scy t->ipt_data = nextipnat; 6669255332Scy } else { 6670255332Scy bzero(&zeroipn, sizeof(zeroipn)); 6671255332Scy nextipnat = &zeroipn; 6672255332Scy t->ipt_data = NULL; 6673255332Scy } 6674255332Scy nnext = nextipnat->in_next; 6675170268Sdarrenr break; 6676170268Sdarrenr 6677170268Sdarrenr case IPFGENITER_NAT : 6678170268Sdarrenr nat = t->ipt_data; 6679170268Sdarrenr if (nat == NULL) { 6680255332Scy nextnat = softn->ipf_nat_instances; 6681170268Sdarrenr } else { 6682170268Sdarrenr nextnat = nat->nat_next; 6683170268Sdarrenr } 6684255332Scy if (nextnat != NULL) { 6685255332Scy MUTEX_ENTER(&nextnat->nat_lock); 6686255332Scy nextnat->nat_ref++; 6687255332Scy MUTEX_EXIT(&nextnat->nat_lock); 6688255332Scy t->ipt_data = nextnat; 6689255332Scy } else { 6690255332Scy bzero(&zeronat, sizeof(zeronat)); 6691255332Scy nextnat = &zeronat; 6692255332Scy t->ipt_data = NULL; 6693255332Scy } 6694255332Scy nnext = nextnat->nat_next; 6695170268Sdarrenr break; 6696255332Scy 6697170268Sdarrenr default : 6698255332Scy RWLOCK_EXIT(&softc->ipf_nat); 6699255332Scy IPFERROR(60055); 6700170268Sdarrenr return EINVAL; 6701170268Sdarrenr } 6702170268Sdarrenr 6703255332Scy RWLOCK_EXIT(&softc->ipf_nat); 6704170268Sdarrenr 6705255332Scy objp->ipfo_ptr = itp->igi_data; 6706170268Sdarrenr 6707172776Sdarrenr switch (itp->igi_type) 6708172776Sdarrenr { 6709172776Sdarrenr case IPFGENITER_HOSTMAP : 6710255332Scy error = COPYOUT(nexthm, objp->ipfo_ptr, sizeof(*nexthm)); 6711255332Scy if (error != 0) { 6712255332Scy IPFERROR(60049); 6713255332Scy error = EFAULT; 6714255332Scy } 6715172776Sdarrenr if (hm != NULL) { 6716255332Scy WRITE_ENTER(&softc->ipf_nat); 6717255332Scy ipf_nat_hostmapdel(softc, &hm); 6718255332Scy RWLOCK_EXIT(&softc->ipf_nat); 6719172776Sdarrenr } 6720172776Sdarrenr break; 6721255332Scy 6722172776Sdarrenr case IPFGENITER_IPNAT : 6723255332Scy objp->ipfo_size = nextipnat->in_size; 6724255332Scy objp->ipfo_type = IPFOBJ_IPNAT; 6725255332Scy error = ipf_outobjk(softc, objp, nextipnat); 6726172776Sdarrenr if (ipn != NULL) { 6727255332Scy WRITE_ENTER(&softc->ipf_nat); 6728255332Scy ipf_nat_rule_deref(softc, &ipn); 6729255332Scy RWLOCK_EXIT(&softc->ipf_nat); 6730172776Sdarrenr } 6731172776Sdarrenr break; 6732172776Sdarrenr 6733170268Sdarrenr case IPFGENITER_NAT : 6734255332Scy objp->ipfo_size = sizeof(nat_t); 6735255332Scy objp->ipfo_type = IPFOBJ_NAT; 6736255332Scy error = ipf_outobjk(softc, objp, nextnat); 6737255332Scy if (nat != NULL) 6738255332Scy ipf_nat_deref(softc, &nat); 6739170268Sdarrenr 6740170268Sdarrenr break; 6741170268Sdarrenr } 6742170268Sdarrenr 6743255332Scy if (nnext == NULL) 6744255332Scy ipf_token_mark_complete(t); 6745255332Scy 6746170268Sdarrenr return error; 6747170268Sdarrenr} 6748170268Sdarrenr 6749170268Sdarrenr 6750170268Sdarrenr/* ------------------------------------------------------------------------ */ 6751170268Sdarrenr/* Function: nat_extraflush */ 6752170268Sdarrenr/* Returns: int - 0 == success, -1 == failure */ 6753255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6754255332Scy/* softn(I) - pointer to NAT context structure */ 6755255332Scy/* which(I) - how to flush the active NAT table */ 6756170268Sdarrenr/* Write Locks: ipf_nat */ 6757170268Sdarrenr/* */ 6758170268Sdarrenr/* Flush nat tables. Three actions currently defined: */ 6759170268Sdarrenr/* which == 0 : flush all nat table entries */ 6760170268Sdarrenr/* which == 1 : flush TCP connections which have started to close but are */ 6761170268Sdarrenr/* stuck for some reason. */ 6762170268Sdarrenr/* which == 2 : flush TCP connections which have been idle for a long time, */ 6763170268Sdarrenr/* starting at > 4 days idle and working back in successive half-*/ 6764170268Sdarrenr/* days to at most 12 hours old. If this fails to free enough */ 6765170268Sdarrenr/* slots then work backwards in half hour slots to 30 minutes. */ 6766170268Sdarrenr/* If that too fails, then work backwards in 30 second intervals */ 6767170268Sdarrenr/* for the last 30 minutes to at worst 30 seconds idle. */ 6768170268Sdarrenr/* ------------------------------------------------------------------------ */ 6769255332Scystatic int 6770255332Scyipf_nat_extraflush(softc, softn, which) 6771255332Scy ipf_main_softc_t *softc; 6772255332Scy ipf_nat_softc_t *softn; 6773255332Scy int which; 6774170268Sdarrenr{ 6775170268Sdarrenr nat_t *nat, **natp; 6776170268Sdarrenr ipftqent_t *tqn; 6777255332Scy ipftq_t *ifq; 6778170268Sdarrenr int removed; 6779170268Sdarrenr SPL_INT(s); 6780170268Sdarrenr 6781170268Sdarrenr removed = 0; 6782170268Sdarrenr 6783170268Sdarrenr SPL_NET(s); 6784170268Sdarrenr switch (which) 6785170268Sdarrenr { 6786170268Sdarrenr case 0 : 6787255332Scy softn->ipf_nat_stats.ns_flush_all++; 6788170268Sdarrenr /* 6789170268Sdarrenr * Style 0 flush removes everything... 6790170268Sdarrenr */ 6791255332Scy for (natp = &softn->ipf_nat_instances; 6792255332Scy ((nat = *natp) != NULL); ) { 6793255332Scy ipf_nat_delete(softc, nat, NL_FLUSH); 6794170268Sdarrenr removed++; 6795170268Sdarrenr } 6796170268Sdarrenr break; 6797170268Sdarrenr 6798170268Sdarrenr case 1 : 6799255332Scy softn->ipf_nat_stats.ns_flush_closing++; 6800170268Sdarrenr /* 6801170268Sdarrenr * Since we're only interested in things that are closing, 6802170268Sdarrenr * we can start with the appropriate timeout queue. 6803170268Sdarrenr */ 6804255332Scy for (ifq = softn->ipf_nat_tcptq + IPF_TCPS_CLOSE_WAIT; 6805255332Scy ifq != NULL; ifq = ifq->ifq_next) { 6806170268Sdarrenr 6807170268Sdarrenr for (tqn = ifq->ifq_head; tqn != NULL; ) { 6808170268Sdarrenr nat = tqn->tqe_parent; 6809170268Sdarrenr tqn = tqn->tqe_next; 6810255332Scy if (nat->nat_pr[0] != IPPROTO_TCP || 6811255332Scy nat->nat_pr[1] != IPPROTO_TCP) 6812170268Sdarrenr break; 6813255332Scy ipf_nat_delete(softc, nat, NL_EXPIRE); 6814170268Sdarrenr removed++; 6815170268Sdarrenr } 6816170268Sdarrenr } 6817170268Sdarrenr 6818170268Sdarrenr /* 6819170268Sdarrenr * Also need to look through the user defined queues. 6820170268Sdarrenr */ 6821255332Scy for (ifq = softn->ipf_nat_utqe; ifq != NULL; 6822255332Scy ifq = ifq->ifq_next) { 6823170268Sdarrenr for (tqn = ifq->ifq_head; tqn != NULL; ) { 6824170268Sdarrenr nat = tqn->tqe_parent; 6825170268Sdarrenr tqn = tqn->tqe_next; 6826255332Scy if (nat->nat_pr[0] != IPPROTO_TCP || 6827255332Scy nat->nat_pr[1] != IPPROTO_TCP) 6828170268Sdarrenr continue; 6829170268Sdarrenr 6830170268Sdarrenr if ((nat->nat_tcpstate[0] > 6831170268Sdarrenr IPF_TCPS_ESTABLISHED) && 6832170268Sdarrenr (nat->nat_tcpstate[1] > 6833170268Sdarrenr IPF_TCPS_ESTABLISHED)) { 6834255332Scy ipf_nat_delete(softc, nat, NL_EXPIRE); 6835170268Sdarrenr removed++; 6836170268Sdarrenr } 6837170268Sdarrenr } 6838170268Sdarrenr } 6839170268Sdarrenr break; 6840170268Sdarrenr 6841170268Sdarrenr /* 6842170268Sdarrenr * Args 5-11 correspond to flushing those particular states 6843170268Sdarrenr * for TCP connections. 6844170268Sdarrenr */ 6845170268Sdarrenr case IPF_TCPS_CLOSE_WAIT : 6846170268Sdarrenr case IPF_TCPS_FIN_WAIT_1 : 6847170268Sdarrenr case IPF_TCPS_CLOSING : 6848170268Sdarrenr case IPF_TCPS_LAST_ACK : 6849170268Sdarrenr case IPF_TCPS_FIN_WAIT_2 : 6850170268Sdarrenr case IPF_TCPS_TIME_WAIT : 6851170268Sdarrenr case IPF_TCPS_CLOSED : 6852255332Scy softn->ipf_nat_stats.ns_flush_state++; 6853255332Scy tqn = softn->ipf_nat_tcptq[which].ifq_head; 6854170268Sdarrenr while (tqn != NULL) { 6855170268Sdarrenr nat = tqn->tqe_parent; 6856170268Sdarrenr tqn = tqn->tqe_next; 6857255332Scy ipf_nat_delete(softc, nat, NL_FLUSH); 6858170268Sdarrenr removed++; 6859170268Sdarrenr } 6860170268Sdarrenr break; 6861255332Scy 6862170268Sdarrenr default : 6863170268Sdarrenr if (which < 30) 6864170268Sdarrenr break; 6865255332Scy 6866255332Scy softn->ipf_nat_stats.ns_flush_timeout++; 6867170268Sdarrenr /* 6868170268Sdarrenr * Take a large arbitrary number to mean the number of seconds 6869170268Sdarrenr * for which which consider to be the maximum value we'll allow 6870170268Sdarrenr * the expiration to be. 6871170268Sdarrenr */ 6872170268Sdarrenr which = IPF_TTLVAL(which); 6873255332Scy for (natp = &softn->ipf_nat_instances; 6874255332Scy ((nat = *natp) != NULL); ) { 6875255332Scy if (softc->ipf_ticks - nat->nat_touched > which) { 6876255332Scy ipf_nat_delete(softc, nat, NL_FLUSH); 6877170268Sdarrenr removed++; 6878170268Sdarrenr } else 6879170268Sdarrenr natp = &nat->nat_next; 6880170268Sdarrenr } 6881170268Sdarrenr break; 6882170268Sdarrenr } 6883170268Sdarrenr 6884170268Sdarrenr if (which != 2) { 6885170268Sdarrenr SPL_X(s); 6886170268Sdarrenr return removed; 6887170268Sdarrenr } 6888170268Sdarrenr 6889255332Scy softn->ipf_nat_stats.ns_flush_queue++; 6890255332Scy 6891170268Sdarrenr /* 6892255332Scy * Asked to remove inactive entries because the table is full, try 6893255332Scy * again, 3 times, if first attempt failed with a different criteria 6894255332Scy * each time. The order tried in must be in decreasing age. 6895255332Scy * Another alternative is to implement random drop and drop N entries 6896255332Scy * at random until N have been freed up. 6897170268Sdarrenr */ 6898255332Scy if (softc->ipf_ticks - softn->ipf_nat_last_force_flush > 6899255332Scy IPF_TTLVAL(5)) { 6900255332Scy softn->ipf_nat_last_force_flush = softc->ipf_ticks; 6901255332Scy 6902255332Scy removed = ipf_queueflush(softc, ipf_nat_flush_entry, 6903255332Scy softn->ipf_nat_tcptq, 6904255332Scy softn->ipf_nat_utqe, 6905255332Scy &softn->ipf_nat_stats.ns_active, 6906255332Scy softn->ipf_nat_table_sz, 6907255332Scy softn->ipf_nat_table_wm_low); 6908170268Sdarrenr } 6909170268Sdarrenr 6910170268Sdarrenr SPL_X(s); 6911170268Sdarrenr return removed; 6912170268Sdarrenr} 6913170268Sdarrenr 6914170268Sdarrenr 6915170268Sdarrenr/* ------------------------------------------------------------------------ */ 6916255332Scy/* Function: ipf_nat_flush_entry */ 6917170268Sdarrenr/* Returns: 0 - always succeeds */ 6918255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6919255332Scy/* entry(I) - pointer to NAT entry */ 6920170268Sdarrenr/* Write Locks: ipf_nat */ 6921170268Sdarrenr/* */ 6922170268Sdarrenr/* This function is a stepping stone between ipf_queueflush() and */ 6923170268Sdarrenr/* nat_dlete(). It is used so we can provide a uniform interface via the */ 6924170268Sdarrenr/* ipf_queueflush() function. Since the nat_delete() function returns void */ 6925170268Sdarrenr/* we translate that to mean it always succeeds in deleting something. */ 6926170268Sdarrenr/* ------------------------------------------------------------------------ */ 6927255332Scystatic int 6928255332Scyipf_nat_flush_entry(softc, entry) 6929255332Scy ipf_main_softc_t *softc; 6930255332Scy void *entry; 6931170268Sdarrenr{ 6932255332Scy ipf_nat_delete(softc, entry, NL_FLUSH); 6933170268Sdarrenr return 0; 6934170268Sdarrenr} 6935172776Sdarrenr 6936172776Sdarrenr 6937172776Sdarrenr/* ------------------------------------------------------------------------ */ 6938255332Scy/* Function: ipf_nat_iterator */ 6939255332Scy/* Returns: int - 0 == ok, else error */ 6940255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6941255332Scy/* token(I) - pointer to ipftoken structure */ 6942255332Scy/* itp(I) - pointer to ipfgeniter_t structure */ 6943255332Scy/* obj(I) - pointer to data description structure */ 6944255332Scy/* */ 6945255332Scy/* This function acts as a handler for the SIOCGENITER ioctls that use a */ 6946255332Scy/* generic structure to iterate through a list. There are three different */ 6947255332Scy/* linked lists of NAT related information to go through: NAT rules, active */ 6948255332Scy/* NAT mappings and the NAT fragment cache. */ 6949255332Scy/* ------------------------------------------------------------------------ */ 6950255332Scystatic int 6951255332Scyipf_nat_iterator(softc, token, itp, obj) 6952255332Scy ipf_main_softc_t *softc; 6953255332Scy ipftoken_t *token; 6954255332Scy ipfgeniter_t *itp; 6955255332Scy ipfobj_t *obj; 6956255332Scy{ 6957255332Scy int error; 6958255332Scy 6959255332Scy if (itp->igi_data == NULL) { 6960255332Scy IPFERROR(60052); 6961255332Scy return EFAULT; 6962255332Scy } 6963255332Scy 6964255332Scy switch (itp->igi_type) 6965255332Scy { 6966255332Scy case IPFGENITER_HOSTMAP : 6967255332Scy case IPFGENITER_IPNAT : 6968255332Scy case IPFGENITER_NAT : 6969255332Scy error = ipf_nat_getnext(softc, token, itp, obj); 6970255332Scy break; 6971255332Scy 6972255332Scy case IPFGENITER_NATFRAG : 6973255332Scy error = ipf_frag_nat_next(softc, token, itp); 6974255332Scy break; 6975255332Scy default : 6976255332Scy IPFERROR(60053); 6977255332Scy error = EINVAL; 6978255332Scy break; 6979255332Scy } 6980255332Scy 6981255332Scy return error; 6982255332Scy} 6983255332Scy 6984255332Scy 6985255332Scy/* ------------------------------------------------------------------------ */ 6986255332Scy/* Function: ipf_nat_setpending */ 6987255332Scy/* Returns: Nil */ 6988255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6989255332Scy/* nat(I) - pointer to NAT structure */ 6990255332Scy/* Locks: ipf_nat (read or write) */ 6991255332Scy/* */ 6992255332Scy/* Put the NAT entry on to the pending queue - this queue has a very short */ 6993255332Scy/* lifetime where items are put that can't be deleted straight away because */ 6994255332Scy/* of locking issues but we want to delete them ASAP, anyway. In calling */ 6995255332Scy/* this function, it is assumed that the owner (if there is one, as shown */ 6996255332Scy/* by nat_me) is no longer interested in it. */ 6997255332Scy/* ------------------------------------------------------------------------ */ 6998255332Scyvoid 6999255332Scyipf_nat_setpending(softc, nat) 7000255332Scy ipf_main_softc_t *softc; 7001255332Scy nat_t *nat; 7002255332Scy{ 7003255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 7004255332Scy ipftq_t *oifq; 7005255332Scy 7006255332Scy oifq = nat->nat_tqe.tqe_ifq; 7007255332Scy if (oifq != NULL) 7008255332Scy ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, 7009255332Scy &softn->ipf_nat_pending); 7010255332Scy else 7011255332Scy ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, 7012255332Scy &softn->ipf_nat_pending, nat); 7013255332Scy 7014255332Scy if (nat->nat_me != NULL) { 7015255332Scy *nat->nat_me = NULL; 7016255332Scy nat->nat_me = NULL; 7017255332Scy nat->nat_ref--; 7018255332Scy ASSERT(nat->nat_ref >= 0); 7019255332Scy } 7020255332Scy} 7021255332Scy 7022255332Scy 7023255332Scy/* ------------------------------------------------------------------------ */ 7024255332Scy/* Function: nat_newrewrite */ 7025255332Scy/* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ 7026255332Scy/* allow rule to be moved if IPN_ROUNDR is set. */ 7027255332Scy/* Parameters: fin(I) - pointer to packet information */ 7028255332Scy/* nat(I) - pointer to NAT entry */ 7029255332Scy/* ni(I) - pointer to structure with misc. information needed */ 7030255332Scy/* to create new NAT entry. */ 7031255332Scy/* Write Lock: ipf_nat */ 7032255332Scy/* */ 7033255332Scy/* This function is responsible for setting up an active NAT session where */ 7034255332Scy/* we are changing both the source and destination parameters at the same */ 7035255332Scy/* time. The loop in here works differently to elsewhere - each iteration */ 7036255332Scy/* is responsible for changing a single parameter that can be incremented. */ 7037255332Scy/* So one pass may increase the source IP#, next source port, next dest. IP#*/ 7038255332Scy/* and the last destination port for a total of 4 iterations to try each. */ 7039255332Scy/* This is done to try and exhaustively use the translation space available.*/ 7040255332Scy/* ------------------------------------------------------------------------ */ 7041255332Scystatic int 7042255332Scyipf_nat_newrewrite(fin, nat, nai) 7043255332Scy fr_info_t *fin; 7044255332Scy nat_t *nat; 7045255332Scy natinfo_t *nai; 7046255332Scy{ 7047255332Scy int src_search = 1; 7048255332Scy int dst_search = 1; 7049255332Scy fr_info_t frnat; 7050255332Scy u_32_t flags; 7051255332Scy u_short swap; 7052255332Scy ipnat_t *np; 7053255332Scy nat_t *natl; 7054255332Scy int l = 0; 7055255332Scy int changed; 7056255332Scy 7057255332Scy natl = NULL; 7058255332Scy changed = -1; 7059255332Scy np = nai->nai_np; 7060255332Scy flags = nat->nat_flags; 7061255332Scy bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); 7062255332Scy 7063255332Scy nat->nat_hm = NULL; 7064255332Scy 7065255332Scy do { 7066255332Scy changed = -1; 7067255332Scy /* TRACE (l, src_search, dst_search, np) */ 7068255332Scy 7069255332Scy if ((src_search == 0) && (np->in_spnext == 0) && 7070255332Scy (dst_search == 0) && (np->in_dpnext == 0)) { 7071255332Scy if (l > 0) 7072255332Scy return -1; 7073255332Scy } 7074255332Scy 7075255332Scy /* 7076255332Scy * Find a new source address 7077255332Scy */ 7078255332Scy if (ipf_nat_nextaddr(fin, &np->in_nsrc, &frnat.fin_saddr, 7079255332Scy &frnat.fin_saddr) == -1) { 7080255332Scy return -1; 7081255332Scy } 7082255332Scy 7083255332Scy if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0xffffffff)) { 7084255332Scy src_search = 0; 7085255332Scy if (np->in_stepnext == 0) 7086255332Scy np->in_stepnext = 1; 7087255332Scy 7088255332Scy } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) { 7089255332Scy src_search = 0; 7090255332Scy if (np->in_stepnext == 0) 7091255332Scy np->in_stepnext = 1; 7092255332Scy 7093255332Scy } else if (np->in_nsrcmsk == 0xffffffff) { 7094255332Scy src_search = 0; 7095255332Scy if (np->in_stepnext == 0) 7096255332Scy np->in_stepnext = 1; 7097255332Scy 7098255332Scy } else if (np->in_nsrcmsk != 0xffffffff) { 7099255332Scy if (np->in_stepnext == 0 && changed == -1) { 7100255332Scy np->in_snip++; 7101255332Scy np->in_stepnext++; 7102255332Scy changed = 0; 7103255332Scy } 7104255332Scy } 7105255332Scy 7106255332Scy if ((flags & IPN_TCPUDPICMP) != 0) { 7107255332Scy if (np->in_spnext != 0) 7108255332Scy frnat.fin_data[0] = np->in_spnext; 7109255332Scy 7110255332Scy /* 7111255332Scy * Standard port translation. Select next port. 7112255332Scy */ 7113255332Scy if ((flags & IPN_FIXEDSPORT) != 0) { 7114255332Scy np->in_stepnext = 2; 7115255332Scy } else if ((np->in_stepnext == 1) && 7116255332Scy (changed == -1) && (natl != NULL)) { 7117255332Scy np->in_spnext++; 7118255332Scy np->in_stepnext++; 7119255332Scy changed = 1; 7120255332Scy if (np->in_spnext > np->in_spmax) 7121255332Scy np->in_spnext = np->in_spmin; 7122255332Scy } 7123255332Scy } else { 7124255332Scy np->in_stepnext = 2; 7125255332Scy } 7126255332Scy np->in_stepnext &= 0x3; 7127255332Scy 7128255332Scy /* 7129255332Scy * Find a new destination address 7130255332Scy */ 7131255332Scy /* TRACE (fin, np, l, frnat) */ 7132255332Scy 7133255332Scy if (ipf_nat_nextaddr(fin, &np->in_ndst, &frnat.fin_daddr, 7134255332Scy &frnat.fin_daddr) == -1) 7135255332Scy return -1; 7136255332Scy if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) { 7137255332Scy dst_search = 0; 7138255332Scy if (np->in_stepnext == 2) 7139255332Scy np->in_stepnext = 3; 7140255332Scy 7141255332Scy } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0)) { 7142255332Scy dst_search = 0; 7143255332Scy if (np->in_stepnext == 2) 7144255332Scy np->in_stepnext = 3; 7145255332Scy 7146255332Scy } else if (np->in_ndstmsk == 0xffffffff) { 7147255332Scy dst_search = 0; 7148255332Scy if (np->in_stepnext == 2) 7149255332Scy np->in_stepnext = 3; 7150255332Scy 7151255332Scy } else if (np->in_ndstmsk != 0xffffffff) { 7152255332Scy if ((np->in_stepnext == 2) && (changed == -1) && 7153255332Scy (natl != NULL)) { 7154255332Scy changed = 2; 7155255332Scy np->in_stepnext++; 7156255332Scy np->in_dnip++; 7157255332Scy } 7158255332Scy } 7159255332Scy 7160255332Scy if ((flags & IPN_TCPUDPICMP) != 0) { 7161255332Scy if (np->in_dpnext != 0) 7162255332Scy frnat.fin_data[1] = np->in_dpnext; 7163255332Scy 7164255332Scy /* 7165255332Scy * Standard port translation. Select next port. 7166255332Scy */ 7167255332Scy if ((flags & IPN_FIXEDDPORT) != 0) { 7168255332Scy np->in_stepnext = 0; 7169255332Scy } else if (np->in_stepnext == 3 && changed == -1) { 7170255332Scy np->in_dpnext++; 7171255332Scy np->in_stepnext++; 7172255332Scy changed = 3; 7173255332Scy if (np->in_dpnext > np->in_dpmax) 7174255332Scy np->in_dpnext = np->in_dpmin; 7175255332Scy } 7176255332Scy } else { 7177255332Scy if (np->in_stepnext == 3) 7178255332Scy np->in_stepnext = 0; 7179255332Scy } 7180255332Scy 7181255332Scy /* TRACE (frnat) */ 7182255332Scy 7183255332Scy /* 7184255332Scy * Here we do a lookup of the connection as seen from 7185255332Scy * the outside. If an IP# pair already exists, try 7186255332Scy * again. So if you have A->B becomes C->B, you can 7187255332Scy * also have D->E become C->E but not D->B causing 7188255332Scy * another C->B. Also take protocol and ports into 7189255332Scy * account when determining whether a pre-existing 7190255332Scy * NAT setup will cause an external conflict where 7191255332Scy * this is appropriate. 7192255332Scy * 7193255332Scy * fin_data[] is swapped around because we are doing a 7194255332Scy * lookup of the packet is if it were moving in the opposite 7195255332Scy * direction of the one we are working with now. 7196255332Scy */ 7197255332Scy if (flags & IPN_TCPUDP) { 7198255332Scy swap = frnat.fin_data[0]; 7199255332Scy frnat.fin_data[0] = frnat.fin_data[1]; 7200255332Scy frnat.fin_data[1] = swap; 7201255332Scy } 7202255332Scy if (fin->fin_out == 1) { 7203255332Scy natl = ipf_nat_inlookup(&frnat, 7204255332Scy flags & ~(SI_WILDP|NAT_SEARCH), 7205255332Scy (u_int)frnat.fin_p, 7206255332Scy frnat.fin_dst, frnat.fin_src); 7207255332Scy 7208255332Scy } else { 7209255332Scy natl = ipf_nat_outlookup(&frnat, 7210255332Scy flags & ~(SI_WILDP|NAT_SEARCH), 7211255332Scy (u_int)frnat.fin_p, 7212255332Scy frnat.fin_dst, frnat.fin_src); 7213255332Scy } 7214255332Scy if (flags & IPN_TCPUDP) { 7215255332Scy swap = frnat.fin_data[0]; 7216255332Scy frnat.fin_data[0] = frnat.fin_data[1]; 7217255332Scy frnat.fin_data[1] = swap; 7218255332Scy } 7219255332Scy 7220255332Scy /* TRACE natl, in_stepnext, l */ 7221255332Scy 7222255332Scy if ((natl != NULL) && (l > 8)) /* XXX 8 is arbitrary */ 7223255332Scy return -1; 7224255332Scy 7225255332Scy np->in_stepnext &= 0x3; 7226255332Scy 7227255332Scy l++; 7228255332Scy changed = -1; 7229255332Scy } while (natl != NULL); 7230255332Scy 7231255332Scy nat->nat_osrcip = fin->fin_src; 7232255332Scy nat->nat_odstip = fin->fin_dst; 7233255332Scy nat->nat_nsrcip = frnat.fin_src; 7234255332Scy nat->nat_ndstip = frnat.fin_dst; 7235255332Scy 7236255332Scy if ((flags & IPN_TCPUDP) != 0) { 7237255332Scy nat->nat_osport = htons(fin->fin_data[0]); 7238255332Scy nat->nat_odport = htons(fin->fin_data[1]); 7239255332Scy nat->nat_nsport = htons(frnat.fin_data[0]); 7240255332Scy nat->nat_ndport = htons(frnat.fin_data[1]); 7241255332Scy } else if ((flags & IPN_ICMPQUERY) != 0) { 7242255332Scy nat->nat_oicmpid = fin->fin_data[1]; 7243255332Scy nat->nat_nicmpid = frnat.fin_data[1]; 7244255332Scy } 7245255332Scy 7246255332Scy return 0; 7247255332Scy} 7248255332Scy 7249255332Scy 7250255332Scy/* ------------------------------------------------------------------------ */ 7251255332Scy/* Function: nat_newdivert */ 7252255332Scy/* Returns: int - -1 == error, 0 == success */ 7253255332Scy/* Parameters: fin(I) - pointer to packet information */ 7254255332Scy/* nat(I) - pointer to NAT entry */ 7255255332Scy/* ni(I) - pointer to structure with misc. information needed */ 7256255332Scy/* to create new NAT entry. */ 7257255332Scy/* Write Lock: ipf_nat */ 7258255332Scy/* */ 7259255332Scy/* Create a new NAT divert session as defined by the NAT rule. This is */ 7260255332Scy/* somewhat different to other NAT session creation routines because we */ 7261255332Scy/* do not iterate through either port numbers or IP addresses, searching */ 7262255332Scy/* for a unique mapping, however, a complimentary duplicate check is made. */ 7263255332Scy/* ------------------------------------------------------------------------ */ 7264255332Scystatic int 7265255332Scyipf_nat_newdivert(fin, nat, nai) 7266255332Scy fr_info_t *fin; 7267255332Scy nat_t *nat; 7268255332Scy natinfo_t *nai; 7269255332Scy{ 7270255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 7271255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 7272255332Scy fr_info_t frnat; 7273255332Scy ipnat_t *np; 7274255332Scy nat_t *natl; 7275255332Scy int p; 7276255332Scy 7277255332Scy np = nai->nai_np; 7278255332Scy bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); 7279255332Scy 7280255332Scy nat->nat_pr[0] = 0; 7281255332Scy nat->nat_osrcaddr = fin->fin_saddr; 7282255332Scy nat->nat_odstaddr = fin->fin_daddr; 7283255332Scy frnat.fin_saddr = htonl(np->in_snip); 7284255332Scy frnat.fin_daddr = htonl(np->in_dnip); 7285255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 7286255332Scy nat->nat_osport = htons(fin->fin_data[0]); 7287255332Scy nat->nat_odport = htons(fin->fin_data[1]); 7288255332Scy } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { 7289255332Scy nat->nat_oicmpid = fin->fin_data[1]; 7290255332Scy } 7291255332Scy 7292255332Scy if (np->in_redir & NAT_DIVERTUDP) { 7293255332Scy frnat.fin_data[0] = np->in_spnext; 7294255332Scy frnat.fin_data[1] = np->in_dpnext; 7295255332Scy frnat.fin_flx |= FI_TCPUDP; 7296255332Scy p = IPPROTO_UDP; 7297255332Scy } else { 7298255332Scy frnat.fin_flx &= ~FI_TCPUDP; 7299255332Scy p = IPPROTO_IPIP; 7300255332Scy } 7301255332Scy 7302255332Scy if (fin->fin_out == 1) { 7303255332Scy natl = ipf_nat_inlookup(&frnat, 0, p, 7304255332Scy frnat.fin_dst, frnat.fin_src); 7305255332Scy 7306255332Scy } else { 7307255332Scy natl = ipf_nat_outlookup(&frnat, 0, p, 7308255332Scy frnat.fin_dst, frnat.fin_src); 7309255332Scy } 7310255332Scy 7311255332Scy if (natl != NULL) { 7312255332Scy NBUMPSIDED(fin->fin_out, ns_divert_exist); 7313255332Scy return -1; 7314255332Scy } 7315255332Scy 7316255332Scy nat->nat_nsrcaddr = frnat.fin_saddr; 7317255332Scy nat->nat_ndstaddr = frnat.fin_daddr; 7318255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 7319255332Scy nat->nat_nsport = htons(frnat.fin_data[0]); 7320255332Scy nat->nat_ndport = htons(frnat.fin_data[1]); 7321255332Scy } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { 7322255332Scy nat->nat_nicmpid = frnat.fin_data[1]; 7323255332Scy } 7324255332Scy 7325255332Scy nat->nat_pr[fin->fin_out] = fin->fin_p; 7326255332Scy nat->nat_pr[1 - fin->fin_out] = p; 7327255332Scy 7328255332Scy if (np->in_redir & NAT_REDIRECT) 7329255332Scy nat->nat_dir = NAT_DIVERTIN; 7330255332Scy else 7331255332Scy nat->nat_dir = NAT_DIVERTOUT; 7332255332Scy 7333255332Scy return 0; 7334255332Scy} 7335255332Scy 7336255332Scy 7337255332Scy/* ------------------------------------------------------------------------ */ 7338255332Scy/* Function: nat_builddivertmp */ 7339255332Scy/* Returns: int - -1 == error, 0 == success */ 7340255332Scy/* Parameters: softn(I) - pointer to NAT context structure */ 7341255332Scy/* np(I) - pointer to a NAT rule */ 7342255332Scy/* */ 7343255332Scy/* For divert rules, a skeleton packet representing what will be prepended */ 7344255332Scy/* to the real packet is created. Even though we don't have the full */ 7345255332Scy/* packet here, a checksum is calculated that we update later when we */ 7346255332Scy/* fill in the final details. At present a 0 checksum for UDP is being set */ 7347255332Scy/* here because it is expected that divert will be used for localhost. */ 7348255332Scy/* ------------------------------------------------------------------------ */ 7349255332Scystatic int 7350255332Scyipf_nat_builddivertmp(softn, np) 7351255332Scy ipf_nat_softc_t *softn; 7352255332Scy ipnat_t *np; 7353255332Scy{ 7354255332Scy udphdr_t *uh; 7355255332Scy size_t len; 7356255332Scy ip_t *ip; 7357255332Scy 7358255332Scy if ((np->in_redir & NAT_DIVERTUDP) != 0) 7359255332Scy len = sizeof(ip_t) + sizeof(udphdr_t); 7360255332Scy else 7361255332Scy len = sizeof(ip_t); 7362255332Scy 7363255332Scy ALLOC_MB_T(np->in_divmp, len); 7364255332Scy if (np->in_divmp == NULL) { 7365255332Scy NBUMPD(ipf_nat_stats, ns_divert_build); 7366255332Scy return -1; 7367255332Scy } 7368255332Scy 7369255332Scy /* 7370255332Scy * First, the header to get the packet diverted to the new destination 7371255332Scy */ 7372255332Scy ip = MTOD(np->in_divmp, ip_t *); 7373255332Scy IP_V_A(ip, 4); 7374255332Scy IP_HL_A(ip, 5); 7375255332Scy ip->ip_tos = 0; 7376255332Scy if ((np->in_redir & NAT_DIVERTUDP) != 0) 7377255332Scy ip->ip_p = IPPROTO_UDP; 7378255332Scy else 7379255332Scy ip->ip_p = IPPROTO_IPIP; 7380255332Scy ip->ip_ttl = 255; 7381255332Scy ip->ip_off = 0; 7382255332Scy ip->ip_sum = 0; 7383255332Scy ip->ip_len = htons(len); 7384255332Scy ip->ip_id = 0; 7385255332Scy ip->ip_src.s_addr = htonl(np->in_snip); 7386255332Scy ip->ip_dst.s_addr = htonl(np->in_dnip); 7387255332Scy ip->ip_sum = ipf_cksum((u_short *)ip, sizeof(*ip)); 7388255332Scy 7389255332Scy if (np->in_redir & NAT_DIVERTUDP) { 7390255332Scy uh = (udphdr_t *)(ip + 1); 7391255332Scy uh->uh_sum = 0; 7392255332Scy uh->uh_ulen = 8; 7393255332Scy uh->uh_sport = htons(np->in_spnext); 7394255332Scy uh->uh_dport = htons(np->in_dpnext); 7395255332Scy } 7396255332Scy 7397255332Scy return 0; 7398255332Scy} 7399255332Scy 7400255332Scy 7401255332Scy#define MINDECAP (sizeof(ip_t) + sizeof(udphdr_t) + sizeof(ip_t)) 7402255332Scy 7403255332Scy/* ------------------------------------------------------------------------ */ 7404255332Scy/* Function: nat_decap */ 7405255332Scy/* Returns: int - -1 == error, 0 == success */ 7406255332Scy/* Parameters: fin(I) - pointer to packet information */ 7407255332Scy/* nat(I) - pointer to current NAT session */ 7408255332Scy/* */ 7409255332Scy/* This function is responsible for undoing a packet's encapsulation in the */ 7410255332Scy/* reverse of an encap/divert rule. After removing the outer encapsulation */ 7411255332Scy/* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/ 7412255332Scy/* match the "new" packet as it may still be used by IPFilter elsewhere. */ 7413255332Scy/* We use "dir" here as the basis for some of the expectations about the */ 7414255332Scy/* outer header. If we return an error, the goal is to leave the original */ 7415255332Scy/* packet information undisturbed - this falls short at the end where we'd */ 7416255332Scy/* need to back a backup copy of "fin" - expensive. */ 7417255332Scy/* ------------------------------------------------------------------------ */ 7418255332Scystatic int 7419255332Scyipf_nat_decap(fin, nat) 7420255332Scy fr_info_t *fin; 7421255332Scy nat_t *nat; 7422255332Scy{ 7423255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 7424255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 7425255332Scy char *hdr; 7426255332Scy int hlen; 7427255332Scy int skip; 7428255332Scy mb_t *m; 7429255332Scy 7430255332Scy if ((fin->fin_flx & FI_ICMPERR) != 0) { 7431255332Scy /* 7432255332Scy * ICMP packets don't get decapsulated, instead what we need 7433255332Scy * to do is change the ICMP reply from including (in the data 7434255332Scy * portion for errors) the encapsulated packet that we sent 7435255332Scy * out to something that resembles the original packet prior 7436255332Scy * to encapsulation. This isn't done here - all we're doing 7437255332Scy * here is changing the outer address to ensure that it gets 7438255332Scy * targetted back to the correct system. 7439255332Scy */ 7440255332Scy 7441255332Scy if (nat->nat_dir & NAT_OUTBOUND) { 7442255332Scy u_32_t sum1, sum2, sumd; 7443255332Scy 7444255332Scy sum1 = ntohl(fin->fin_daddr); 7445255332Scy sum2 = ntohl(nat->nat_osrcaddr); 7446255332Scy CALC_SUMD(sum1, sum2, sumd); 7447255332Scy fin->fin_ip->ip_dst = nat->nat_osrcip; 7448255332Scy fin->fin_daddr = nat->nat_osrcaddr; 7449255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 7450255332Scy defined(__osf__) || defined(linux) 7451255332Scy ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, sumd, 0); 7452255332Scy#endif 7453255332Scy } 7454255332Scy return 0; 7455255332Scy } 7456255332Scy 7457255332Scy m = fin->fin_m; 7458255332Scy skip = fin->fin_hlen; 7459255332Scy 7460255332Scy switch (nat->nat_dir) 7461255332Scy { 7462255332Scy case NAT_DIVERTIN : 7463255332Scy case NAT_DIVERTOUT : 7464255332Scy if (fin->fin_plen < MINDECAP) 7465255332Scy return -1; 7466255332Scy skip += sizeof(udphdr_t); 7467255332Scy break; 7468255332Scy 7469255332Scy case NAT_ENCAPIN : 7470255332Scy case NAT_ENCAPOUT : 7471255332Scy if (fin->fin_plen < (skip + sizeof(ip_t))) 7472255332Scy return -1; 7473255332Scy break; 7474255332Scy default : 7475255332Scy return -1; 7476255332Scy /* NOTREACHED */ 7477255332Scy } 7478255332Scy 7479255332Scy /* 7480255332Scy * The aim here is to keep the original packet details in "fin" for 7481255332Scy * as long as possible so that returning with an error is for the 7482255332Scy * original packet and there is little undoing work to do. 7483255332Scy */ 7484255332Scy if (M_LEN(m) < skip + sizeof(ip_t)) { 7485255332Scy if (ipf_pr_pullup(fin, skip + sizeof(ip_t)) == -1) 7486255332Scy return -1; 7487255332Scy } 7488255332Scy 7489255332Scy hdr = MTOD(fin->fin_m, char *); 7490255332Scy fin->fin_ip = (ip_t *)(hdr + skip); 7491255332Scy hlen = IP_HL(fin->fin_ip) << 2; 7492255332Scy 7493255332Scy if (ipf_pr_pullup(fin, skip + hlen) == -1) { 7494255332Scy NBUMPSIDED(fin->fin_out, ns_decap_pullup); 7495255332Scy return -1; 7496255332Scy } 7497255332Scy 7498255332Scy fin->fin_hlen = hlen; 7499255332Scy fin->fin_dlen -= skip; 7500255332Scy fin->fin_plen -= skip; 7501255332Scy fin->fin_ipoff += skip; 7502255332Scy 7503255332Scy if (ipf_makefrip(hlen, (ip_t *)hdr, fin) == -1) { 7504255332Scy NBUMPSIDED(fin->fin_out, ns_decap_bad); 7505255332Scy return -1; 7506255332Scy } 7507255332Scy 7508255332Scy return skip; 7509255332Scy} 7510255332Scy 7511255332Scy 7512255332Scy/* ------------------------------------------------------------------------ */ 7513255332Scy/* Function: nat_nextaddr */ 7514255332Scy/* Returns: int - -1 == bad input (no new address), */ 7515255332Scy/* 0 == success and dst has new address */ 7516255332Scy/* Parameters: fin(I) - pointer to packet information */ 7517255332Scy/* na(I) - how to generate new address */ 7518255332Scy/* old(I) - original address being replaced */ 7519255332Scy/* dst(O) - where to put the new address */ 7520255332Scy/* Write Lock: ipf_nat */ 7521255332Scy/* */ 7522255332Scy/* This function uses the contents of the "na" structure, in combination */ 7523255332Scy/* with "old" to produce a new address to store in "dst". Not all of the */ 7524255332Scy/* possible uses of "na" will result in a new address. */ 7525255332Scy/* ------------------------------------------------------------------------ */ 7526255332Scystatic int 7527255332Scyipf_nat_nextaddr(fin, na, old, dst) 7528255332Scy fr_info_t *fin; 7529255332Scy nat_addr_t *na; 7530255332Scy u_32_t *old, *dst; 7531255332Scy{ 7532255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 7533255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 7534255332Scy u_32_t amin, amax, new; 7535255332Scy i6addr_t newip; 7536255332Scy int error; 7537255332Scy 7538255332Scy new = 0; 7539255332Scy amin = na->na_addr[0].in4.s_addr; 7540255332Scy 7541255332Scy switch (na->na_atype) 7542255332Scy { 7543255332Scy case FRI_RANGE : 7544255332Scy amax = na->na_addr[1].in4.s_addr; 7545255332Scy break; 7546255332Scy 7547255332Scy case FRI_NETMASKED : 7548255332Scy case FRI_DYNAMIC : 7549255332Scy case FRI_NORMAL : 7550255332Scy /* 7551255332Scy * Compute the maximum address by adding the inverse of the 7552255332Scy * netmask to the minimum address. 7553255332Scy */ 7554255332Scy amax = ~na->na_addr[1].in4.s_addr; 7555255332Scy amax |= amin; 7556255332Scy break; 7557255332Scy 7558255332Scy case FRI_LOOKUP : 7559255332Scy break; 7560255332Scy 7561255332Scy case FRI_BROADCAST : 7562255332Scy case FRI_PEERADDR : 7563255332Scy case FRI_NETWORK : 7564255332Scy default : 7565255332Scy return -1; 7566255332Scy } 7567255332Scy 7568255332Scy error = -1; 7569255332Scy 7570255332Scy if (na->na_atype == FRI_LOOKUP) { 7571255332Scy if (na->na_type == IPLT_DSTLIST) { 7572255332Scy error = ipf_dstlist_select_node(fin, na->na_ptr, dst, 7573255332Scy NULL); 7574255332Scy } else { 7575255332Scy NBUMPSIDE(fin->fin_out, ns_badnextaddr); 7576255332Scy } 7577255332Scy 7578255332Scy } else if (na->na_atype == IPLT_NONE) { 7579255332Scy /* 7580255332Scy * 0/0 as the new address means leave it alone. 7581255332Scy */ 7582255332Scy if (na->na_addr[0].in4.s_addr == 0 && 7583255332Scy na->na_addr[1].in4.s_addr == 0) { 7584255332Scy new = *old; 7585255332Scy 7586255332Scy /* 7587255332Scy * 0/32 means get the interface's address 7588255332Scy */ 7589255332Scy } else if (na->na_addr[0].in4.s_addr == 0 && 7590255332Scy na->na_addr[1].in4.s_addr == 0xffffffff) { 7591255332Scy if (ipf_ifpaddr(softc, 4, na->na_atype, 7592255332Scy fin->fin_ifp, &newip, NULL) == -1) { 7593255332Scy NBUMPSIDED(fin->fin_out, ns_ifpaddrfail); 7594255332Scy return -1; 7595255332Scy } 7596255332Scy new = newip.in4.s_addr; 7597255332Scy } else { 7598255332Scy new = htonl(na->na_nextip); 7599255332Scy } 7600255332Scy *dst = new; 7601255332Scy error = 0; 7602255332Scy 7603255332Scy } else { 7604255332Scy NBUMPSIDE(fin->fin_out, ns_badnextaddr); 7605255332Scy } 7606255332Scy 7607255332Scy return error; 7608255332Scy} 7609255332Scy 7610255332Scy 7611255332Scy/* ------------------------------------------------------------------------ */ 7612255332Scy/* Function: nat_nextaddrinit */ 7613255332Scy/* Returns: int - 0 == success, else error number */ 7614255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7615255332Scy/* na(I) - NAT address information for generating new addr*/ 7616255332Scy/* initial(I) - flag indicating if it is the first call for */ 7617255332Scy/* this "na" structure. */ 7618255332Scy/* ifp(I) - network interface to derive address */ 7619255332Scy/* information from. */ 7620255332Scy/* */ 7621255332Scy/* This function is expected to be called in two scenarious: when a new NAT */ 7622255332Scy/* rule is loaded into the kernel and when the list of NAT rules is sync'd */ 7623255332Scy/* up with the valid network interfaces (possibly due to them changing.) */ 7624255332Scy/* To distinguish between these, the "initial" parameter is used. If it is */ 7625255332Scy/* 1 then this indicates the rule has just been reloaded and 0 for when we */ 7626255332Scy/* are updating information. This difference is important because in */ 7627255332Scy/* instances where we are not updating address information associated with */ 7628255332Scy/* a network interface, we don't want to disturb what the "next" address to */ 7629255332Scy/* come out of ipf_nat_nextaddr() will be. */ 7630255332Scy/* ------------------------------------------------------------------------ */ 7631255332Scystatic int 7632255332Scyipf_nat_nextaddrinit(softc, base, na, initial, ifp) 7633255332Scy ipf_main_softc_t *softc; 7634255332Scy char *base; 7635255332Scy nat_addr_t *na; 7636255332Scy int initial; 7637255332Scy void *ifp; 7638255332Scy{ 7639255332Scy 7640255332Scy switch (na->na_atype) 7641255332Scy { 7642255332Scy case FRI_LOOKUP : 7643255332Scy if (na->na_subtype == 0) { 7644255332Scy na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT, 7645255332Scy na->na_type, 7646255332Scy na->na_num, 7647255332Scy &na->na_func); 7648255332Scy } else if (na->na_subtype == 1) { 7649255332Scy na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT, 7650255332Scy na->na_type, 7651255332Scy base + na->na_num, 7652255332Scy &na->na_func); 7653255332Scy } 7654255332Scy if (na->na_func == NULL) { 7655255332Scy IPFERROR(60060); 7656255332Scy return ESRCH; 7657255332Scy } 7658255332Scy if (na->na_ptr == NULL) { 7659255332Scy IPFERROR(60056); 7660255332Scy return ESRCH; 7661255332Scy } 7662255332Scy break; 7663255332Scy 7664255332Scy case FRI_DYNAMIC : 7665255332Scy case FRI_BROADCAST : 7666255332Scy case FRI_NETWORK : 7667255332Scy case FRI_NETMASKED : 7668255332Scy case FRI_PEERADDR : 7669255332Scy if (ifp != NULL) 7670255332Scy (void )ipf_ifpaddr(softc, 4, na->na_atype, ifp, 7671255332Scy &na->na_addr[0], &na->na_addr[1]); 7672255332Scy break; 7673255332Scy 7674255332Scy case FRI_SPLIT : 7675255332Scy case FRI_RANGE : 7676255332Scy if (initial) 7677255332Scy na->na_nextip = ntohl(na->na_addr[0].in4.s_addr); 7678255332Scy break; 7679255332Scy 7680255332Scy case FRI_NONE : 7681255332Scy na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr; 7682255332Scy return 0; 7683255332Scy 7684255332Scy case FRI_NORMAL : 7685255332Scy na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr; 7686255332Scy break; 7687255332Scy 7688255332Scy default : 7689255332Scy IPFERROR(60054); 7690255332Scy return EINVAL; 7691255332Scy } 7692255332Scy 7693255332Scy if (initial && (na->na_atype == FRI_NORMAL)) { 7694255332Scy if (na->na_addr[0].in4.s_addr == 0) { 7695255332Scy if ((na->na_addr[1].in4.s_addr == 0xffffffff) || 7696255332Scy (na->na_addr[1].in4.s_addr == 0)) { 7697255332Scy return 0; 7698255332Scy } 7699255332Scy } 7700255332Scy 7701255332Scy if (na->na_addr[1].in4.s_addr == 0xffffffff) { 7702255332Scy na->na_nextip = ntohl(na->na_addr[0].in4.s_addr); 7703255332Scy } else { 7704255332Scy na->na_nextip = ntohl(na->na_addr[0].in4.s_addr) + 1; 7705255332Scy } 7706255332Scy } 7707255332Scy 7708255332Scy return 0; 7709255332Scy} 7710255332Scy 7711255332Scy 7712255332Scy/* ------------------------------------------------------------------------ */ 7713255332Scy/* Function: ipf_nat_matchflush */ 7714255332Scy/* Returns: int - -1 == error, 0 == success */ 7715255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7716255332Scy/* softn(I) - pointer to NAT context structure */ 7717255332Scy/* nat(I) - pointer to current NAT session */ 7718255332Scy/* */ 7719255332Scy/* ------------------------------------------------------------------------ */ 7720255332Scystatic int 7721255332Scyipf_nat_matchflush(softc, softn, data) 7722255332Scy ipf_main_softc_t *softc; 7723255332Scy ipf_nat_softc_t *softn; 7724255332Scy caddr_t data; 7725255332Scy{ 7726255332Scy int *array, flushed, error; 7727255332Scy nat_t *nat, *natnext; 7728255332Scy ipfobj_t obj; 7729255332Scy 7730255332Scy error = ipf_matcharray_load(softc, data, &obj, &array); 7731255332Scy if (error != 0) 7732255332Scy return error; 7733255332Scy 7734255332Scy flushed = 0; 7735255332Scy 7736255332Scy for (nat = softn->ipf_nat_instances; nat != NULL; nat = natnext) { 7737255332Scy natnext = nat->nat_next; 7738255332Scy if (ipf_nat_matcharray(nat, array, softc->ipf_ticks) == 0) { 7739255332Scy ipf_nat_delete(softc, nat, NL_FLUSH); 7740255332Scy flushed++; 7741255332Scy } 7742255332Scy } 7743255332Scy 7744255332Scy obj.ipfo_retval = flushed; 7745255332Scy error = BCOPYOUT(&obj, data, sizeof(obj)); 7746255332Scy 7747255332Scy KFREES(array, array[0] * sizeof(*array)); 7748255332Scy 7749255332Scy return error; 7750255332Scy} 7751255332Scy 7752255332Scy 7753255332Scy/* ------------------------------------------------------------------------ */ 7754255332Scy/* Function: ipf_nat_matcharray */ 7755255332Scy/* Returns: int - -1 == error, 0 == success */ 7756255332Scy/* Parameters: fin(I) - pointer to packet information */ 7757255332Scy/* nat(I) - pointer to current NAT session */ 7758255332Scy/* */ 7759255332Scy/* ------------------------------------------------------------------------ */ 7760255332Scystatic int 7761255332Scyipf_nat_matcharray(nat, array, ticks) 7762255332Scy nat_t *nat; 7763255332Scy int *array; 7764255332Scy u_long ticks; 7765255332Scy{ 7766255332Scy int i, n, *x, e, p; 7767255332Scy 7768255332Scy e = 0; 7769255332Scy n = array[0]; 7770255332Scy x = array + 1; 7771255332Scy 7772255332Scy for (; n > 0; x += 3 + x[2]) { 7773255332Scy if (x[0] == IPF_EXP_END) 7774255332Scy break; 7775255332Scy e = 0; 7776255332Scy 7777255332Scy n -= x[2] + 3; 7778255332Scy if (n < 0) 7779255332Scy break; 7780255332Scy 7781255332Scy p = x[0] >> 16; 7782255332Scy if (p != 0 && p != nat->nat_pr[1]) 7783255332Scy break; 7784255332Scy 7785255332Scy switch (x[0]) 7786255332Scy { 7787255332Scy case IPF_EXP_IP_PR : 7788255332Scy for (i = 0; !e && i < x[2]; i++) { 7789255332Scy e |= (nat->nat_pr[1] == x[i + 3]); 7790255332Scy } 7791255332Scy break; 7792255332Scy 7793255332Scy case IPF_EXP_IP_SRCADDR : 7794255332Scy if (nat->nat_v[0] == 4) { 7795255332Scy for (i = 0; !e && i < x[2]; i++) { 7796255332Scy e |= ((nat->nat_osrcaddr & x[i + 4]) == 7797255332Scy x[i + 3]); 7798255332Scy } 7799255332Scy } 7800255332Scy if (nat->nat_v[1] == 4) { 7801255332Scy for (i = 0; !e && i < x[2]; i++) { 7802255332Scy e |= ((nat->nat_nsrcaddr & x[i + 4]) == 7803255332Scy x[i + 3]); 7804255332Scy } 7805255332Scy } 7806255332Scy break; 7807255332Scy 7808255332Scy case IPF_EXP_IP_DSTADDR : 7809255332Scy if (nat->nat_v[0] == 4) { 7810255332Scy for (i = 0; !e && i < x[2]; i++) { 7811255332Scy e |= ((nat->nat_odstaddr & x[i + 4]) == 7812255332Scy x[i + 3]); 7813255332Scy } 7814255332Scy } 7815255332Scy if (nat->nat_v[1] == 4) { 7816255332Scy for (i = 0; !e && i < x[2]; i++) { 7817255332Scy e |= ((nat->nat_ndstaddr & x[i + 4]) == 7818255332Scy x[i + 3]); 7819255332Scy } 7820255332Scy } 7821255332Scy break; 7822255332Scy 7823255332Scy case IPF_EXP_IP_ADDR : 7824255332Scy for (i = 0; !e && i < x[2]; i++) { 7825255332Scy if (nat->nat_v[0] == 4) { 7826255332Scy e |= ((nat->nat_osrcaddr & x[i + 4]) == 7827255332Scy x[i + 3]); 7828255332Scy } 7829255332Scy if (nat->nat_v[1] == 4) { 7830255332Scy e |= ((nat->nat_nsrcaddr & x[i + 4]) == 7831255332Scy x[i + 3]); 7832255332Scy } 7833255332Scy if (nat->nat_v[0] == 4) { 7834255332Scy e |= ((nat->nat_odstaddr & x[i + 4]) == 7835255332Scy x[i + 3]); 7836255332Scy } 7837255332Scy if (nat->nat_v[1] == 4) { 7838255332Scy e |= ((nat->nat_ndstaddr & x[i + 4]) == 7839255332Scy x[i + 3]); 7840255332Scy } 7841255332Scy } 7842255332Scy break; 7843255332Scy 7844255332Scy#ifdef USE_INET6 7845255332Scy case IPF_EXP_IP6_SRCADDR : 7846255332Scy if (nat->nat_v[0] == 6) { 7847255332Scy for (i = 0; !e && i < x[3]; i++) { 7848255332Scy e |= IP6_MASKEQ(&nat->nat_osrc6, 7849255332Scy x + i + 7, x + i + 3); 7850255332Scy } 7851255332Scy } 7852255332Scy if (nat->nat_v[1] == 6) { 7853255332Scy for (i = 0; !e && i < x[3]; i++) { 7854255332Scy e |= IP6_MASKEQ(&nat->nat_nsrc6, 7855255332Scy x + i + 7, x + i + 3); 7856255332Scy } 7857255332Scy } 7858255332Scy break; 7859255332Scy 7860255332Scy case IPF_EXP_IP6_DSTADDR : 7861255332Scy if (nat->nat_v[0] == 6) { 7862255332Scy for (i = 0; !e && i < x[3]; i++) { 7863255332Scy e |= IP6_MASKEQ(&nat->nat_odst6, 7864255332Scy x + i + 7, 7865255332Scy x + i + 3); 7866255332Scy } 7867255332Scy } 7868255332Scy if (nat->nat_v[1] == 6) { 7869255332Scy for (i = 0; !e && i < x[3]; i++) { 7870255332Scy e |= IP6_MASKEQ(&nat->nat_ndst6, 7871255332Scy x + i + 7, 7872255332Scy x + i + 3); 7873255332Scy } 7874255332Scy } 7875255332Scy break; 7876255332Scy 7877255332Scy case IPF_EXP_IP6_ADDR : 7878255332Scy for (i = 0; !e && i < x[3]; i++) { 7879255332Scy if (nat->nat_v[0] == 6) { 7880255332Scy e |= IP6_MASKEQ(&nat->nat_osrc6, 7881255332Scy x + i + 7, 7882255332Scy x + i + 3); 7883255332Scy } 7884255332Scy if (nat->nat_v[0] == 6) { 7885255332Scy e |= IP6_MASKEQ(&nat->nat_odst6, 7886255332Scy x + i + 7, 7887255332Scy x + i + 3); 7888255332Scy } 7889255332Scy if (nat->nat_v[1] == 6) { 7890255332Scy e |= IP6_MASKEQ(&nat->nat_nsrc6, 7891255332Scy x + i + 7, 7892255332Scy x + i + 3); 7893255332Scy } 7894255332Scy if (nat->nat_v[1] == 6) { 7895255332Scy e |= IP6_MASKEQ(&nat->nat_ndst6, 7896255332Scy x + i + 7, 7897255332Scy x + i + 3); 7898255332Scy } 7899255332Scy } 7900255332Scy break; 7901255332Scy#endif 7902255332Scy 7903255332Scy case IPF_EXP_UDP_PORT : 7904255332Scy case IPF_EXP_TCP_PORT : 7905255332Scy for (i = 0; !e && i < x[2]; i++) { 7906255332Scy e |= (nat->nat_nsport == x[i + 3]) || 7907255332Scy (nat->nat_ndport == x[i + 3]); 7908255332Scy } 7909255332Scy break; 7910255332Scy 7911255332Scy case IPF_EXP_UDP_SPORT : 7912255332Scy case IPF_EXP_TCP_SPORT : 7913255332Scy for (i = 0; !e && i < x[2]; i++) { 7914255332Scy e |= (nat->nat_nsport == x[i + 3]); 7915255332Scy } 7916255332Scy break; 7917255332Scy 7918255332Scy case IPF_EXP_UDP_DPORT : 7919255332Scy case IPF_EXP_TCP_DPORT : 7920255332Scy for (i = 0; !e && i < x[2]; i++) { 7921255332Scy e |= (nat->nat_ndport == x[i + 3]); 7922255332Scy } 7923255332Scy break; 7924255332Scy 7925255332Scy case IPF_EXP_TCP_STATE : 7926255332Scy for (i = 0; !e && i < x[2]; i++) { 7927255332Scy e |= (nat->nat_tcpstate[0] == x[i + 3]) || 7928255332Scy (nat->nat_tcpstate[1] == x[i + 3]); 7929255332Scy } 7930255332Scy break; 7931255332Scy 7932255332Scy case IPF_EXP_IDLE_GT : 7933255332Scy e |= (ticks - nat->nat_touched > x[3]); 7934255332Scy break; 7935255332Scy } 7936255332Scy e ^= x[1]; 7937255332Scy 7938255332Scy if (!e) 7939255332Scy break; 7940255332Scy } 7941255332Scy 7942255332Scy return e; 7943255332Scy} 7944255332Scy 7945255332Scy 7946255332Scy/* ------------------------------------------------------------------------ */ 7947255332Scy/* Function: ipf_nat_gettable */ 7948172776Sdarrenr/* Returns: int - 0 = success, else error */ 7949255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7950255332Scy/* softn(I) - pointer to NAT context structure */ 7951255332Scy/* data(I) - pointer to ioctl data */ 7952172776Sdarrenr/* */ 7953172776Sdarrenr/* This function handles ioctl requests for tables of nat information. */ 7954172776Sdarrenr/* At present the only table it deals with is the hash bucket statistics. */ 7955172776Sdarrenr/* ------------------------------------------------------------------------ */ 7956255332Scystatic int 7957255332Scyipf_nat_gettable(softc, softn, data) 7958255332Scy ipf_main_softc_t *softc; 7959255332Scy ipf_nat_softc_t *softn; 7960255332Scy char *data; 7961172776Sdarrenr{ 7962172776Sdarrenr ipftable_t table; 7963172776Sdarrenr int error; 7964172776Sdarrenr 7965255332Scy error = ipf_inobj(softc, data, NULL, &table, IPFOBJ_GTABLE); 7966172776Sdarrenr if (error != 0) 7967172776Sdarrenr return error; 7968172776Sdarrenr 7969172776Sdarrenr switch (table.ita_type) 7970172776Sdarrenr { 7971172776Sdarrenr case IPFTABLE_BUCKETS_NATIN : 7972255332Scy error = COPYOUT(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, 7973255332Scy table.ita_table, 7974255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 7975172776Sdarrenr break; 7976172776Sdarrenr 7977172776Sdarrenr case IPFTABLE_BUCKETS_NATOUT : 7978255332Scy error = COPYOUT(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, 7979255332Scy table.ita_table, 7980255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 7981172776Sdarrenr break; 7982172776Sdarrenr 7983172776Sdarrenr default : 7984255332Scy IPFERROR(60058); 7985172776Sdarrenr return EINVAL; 7986172776Sdarrenr } 7987172776Sdarrenr 7988172776Sdarrenr if (error != 0) { 7989255332Scy IPFERROR(60059); 7990172776Sdarrenr error = EFAULT; 7991172776Sdarrenr } 7992172776Sdarrenr return error; 7993172776Sdarrenr} 7994255332Scy 7995255332Scy 7996255332Scy/* ------------------------------------------------------------------------ */ 7997255332Scy/* Function: ipf_nat_settimeout */ 7998255332Scy/* Returns: int - 0 = success, else failure */ 7999255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8000255332Scy/* t(I) - pointer to tunable */ 8001255332Scy/* p(I) - pointer to new tuning data */ 8002255332Scy/* */ 8003255332Scy/* Apply the timeout change to the NAT timeout queues. */ 8004255332Scy/* ------------------------------------------------------------------------ */ 8005255332Scyint 8006255332Scyipf_nat_settimeout(softc, t, p) 8007255332Scy struct ipf_main_softc_s *softc; 8008255332Scy ipftuneable_t *t; 8009255332Scy ipftuneval_t *p; 8010255332Scy{ 8011255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 8012255332Scy 8013255332Scy if (!strncmp(t->ipft_name, "tcp_", 4)) 8014255332Scy return ipf_settimeout_tcp(t, p, softn->ipf_nat_tcptq); 8015255332Scy 8016255332Scy if (!strcmp(t->ipft_name, "udp_timeout")) { 8017255332Scy ipf_apply_timeout(&softn->ipf_nat_udptq, p->ipftu_int); 8018255332Scy } else if (!strcmp(t->ipft_name, "udp_ack_timeout")) { 8019255332Scy ipf_apply_timeout(&softn->ipf_nat_udpacktq, p->ipftu_int); 8020255332Scy } else if (!strcmp(t->ipft_name, "icmp_timeout")) { 8021255332Scy ipf_apply_timeout(&softn->ipf_nat_icmptq, p->ipftu_int); 8022255332Scy } else if (!strcmp(t->ipft_name, "icmp_ack_timeout")) { 8023255332Scy ipf_apply_timeout(&softn->ipf_nat_icmpacktq, p->ipftu_int); 8024255332Scy } else if (!strcmp(t->ipft_name, "ip_timeout")) { 8025255332Scy ipf_apply_timeout(&softn->ipf_nat_iptq, p->ipftu_int); 8026255332Scy } else { 8027255332Scy IPFERROR(60062); 8028255332Scy return ESRCH; 8029255332Scy } 8030255332Scy return 0; 8031255332Scy} 8032255332Scy 8033255332Scy 8034255332Scy/* ------------------------------------------------------------------------ */ 8035255332Scy/* Function: ipf_nat_rehash */ 8036255332Scy/* Returns: int - 0 = success, else failure */ 8037255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8038255332Scy/* t(I) - pointer to tunable */ 8039255332Scy/* p(I) - pointer to new tuning data */ 8040255332Scy/* */ 8041255332Scy/* To change the size of the basic NAT table, we need to first allocate the */ 8042255332Scy/* new tables (lest it fails and we've got nowhere to store all of the NAT */ 8043255332Scy/* sessions currently active) and then walk through the entire list and */ 8044255332Scy/* insert them into the table. There are two tables here: an inbound one */ 8045255332Scy/* and an outbound one. Each NAT entry goes into each table once. */ 8046255332Scy/* ------------------------------------------------------------------------ */ 8047255332Scyint 8048255332Scyipf_nat_rehash(softc, t, p) 8049255332Scy ipf_main_softc_t *softc; 8050255332Scy ipftuneable_t *t; 8051255332Scy ipftuneval_t *p; 8052255332Scy{ 8053255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 8054255332Scy nat_t **newtab[2], *nat, **natp; 8055255332Scy u_int *bucketlens[2]; 8056255332Scy u_int maxbucket; 8057255332Scy u_int newsize; 8058255332Scy int error; 8059255332Scy u_int hv; 8060255332Scy int i; 8061255332Scy 8062255332Scy newsize = p->ipftu_int; 8063255332Scy /* 8064255332Scy * In case there is nothing to do... 8065255332Scy */ 8066255332Scy if (newsize == softn->ipf_nat_table_sz) 8067255332Scy return 0; 8068255332Scy 8069255332Scy newtab[0] = NULL; 8070255332Scy newtab[1] = NULL; 8071255332Scy bucketlens[0] = NULL; 8072255332Scy bucketlens[1] = NULL; 8073255332Scy /* 8074255332Scy * 4 tables depend on the NAT table size: the inbound looking table, 8075255332Scy * the outbound lookup table and the hash chain length for each. 8076255332Scy */ 8077255332Scy KMALLOCS(newtab[0], nat_t **, newsize * sizeof(nat_t *)); 8078255332Scy if (newtab == NULL) { 8079255332Scy error = 60063; 8080255332Scy goto badrehash; 8081255332Scy } 8082255332Scy 8083255332Scy KMALLOCS(newtab[1], nat_t **, newsize * sizeof(nat_t *)); 8084255332Scy if (newtab == NULL) { 8085255332Scy error = 60064; 8086255332Scy goto badrehash; 8087255332Scy } 8088255332Scy 8089255332Scy KMALLOCS(bucketlens[0], u_int *, newsize * sizeof(u_int)); 8090255332Scy if (bucketlens[0] == NULL) { 8091255332Scy error = 60065; 8092255332Scy goto badrehash; 8093255332Scy } 8094255332Scy 8095255332Scy KMALLOCS(bucketlens[1], u_int *, newsize * sizeof(u_int)); 8096255332Scy if (bucketlens[1] == NULL) { 8097255332Scy error = 60066; 8098255332Scy goto badrehash; 8099255332Scy } 8100255332Scy 8101255332Scy /* 8102255332Scy * Recalculate the maximum length based on the new size. 8103255332Scy */ 8104255332Scy for (maxbucket = 0, i = newsize; i > 0; i >>= 1) 8105255332Scy maxbucket++; 8106255332Scy maxbucket *= 2; 8107255332Scy 8108255332Scy bzero((char *)newtab[0], newsize * sizeof(nat_t *)); 8109255332Scy bzero((char *)newtab[1], newsize * sizeof(nat_t *)); 8110255332Scy bzero((char *)bucketlens[0], newsize * sizeof(u_int)); 8111255332Scy bzero((char *)bucketlens[1], newsize * sizeof(u_int)); 8112255332Scy 8113255332Scy WRITE_ENTER(&softc->ipf_nat); 8114255332Scy 8115255332Scy if (softn->ipf_nat_table[0] != NULL) { 8116255332Scy KFREES(softn->ipf_nat_table[0], 8117255332Scy softn->ipf_nat_table_sz * 8118255332Scy sizeof(*softn->ipf_nat_table[0])); 8119255332Scy } 8120255332Scy softn->ipf_nat_table[0] = newtab[0]; 8121255332Scy 8122255332Scy if (softn->ipf_nat_table[1] != NULL) { 8123255332Scy KFREES(softn->ipf_nat_table[1], 8124255332Scy softn->ipf_nat_table_sz * 8125255332Scy sizeof(*softn->ipf_nat_table[1])); 8126255332Scy } 8127255332Scy softn->ipf_nat_table[1] = newtab[1]; 8128255332Scy 8129255332Scy if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) { 8130255332Scy KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, 8131255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 8132255332Scy } 8133255332Scy softn->ipf_nat_stats.ns_side[0].ns_bucketlen = bucketlens[0]; 8134255332Scy 8135255332Scy if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) { 8136255332Scy KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, 8137255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 8138255332Scy } 8139255332Scy softn->ipf_nat_stats.ns_side[1].ns_bucketlen = bucketlens[1]; 8140255332Scy 8141255332Scy#ifdef USE_INET6 8142255332Scy if (softn->ipf_nat_stats.ns_side6[0].ns_bucketlen != NULL) { 8143255332Scy KFREES(softn->ipf_nat_stats.ns_side6[0].ns_bucketlen, 8144255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 8145255332Scy } 8146255332Scy softn->ipf_nat_stats.ns_side6[0].ns_bucketlen = bucketlens[0]; 8147255332Scy 8148255332Scy if (softn->ipf_nat_stats.ns_side6[1].ns_bucketlen != NULL) { 8149255332Scy KFREES(softn->ipf_nat_stats.ns_side6[1].ns_bucketlen, 8150255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 8151255332Scy } 8152255332Scy softn->ipf_nat_stats.ns_side6[1].ns_bucketlen = bucketlens[1]; 8153255332Scy#endif 8154255332Scy 8155255332Scy softn->ipf_nat_maxbucket = maxbucket; 8156255332Scy softn->ipf_nat_table_sz = newsize; 8157255332Scy /* 8158255332Scy * Walk through the entire list of NAT table entries and put them 8159255332Scy * in the new NAT table, somewhere. Because we have a new table, 8160255332Scy * we need to restart the counter of how many chains are in use. 8161255332Scy */ 8162255332Scy softn->ipf_nat_stats.ns_side[0].ns_inuse = 0; 8163255332Scy softn->ipf_nat_stats.ns_side[1].ns_inuse = 0; 8164255332Scy#ifdef USE_INET6 8165255332Scy softn->ipf_nat_stats.ns_side6[0].ns_inuse = 0; 8166255332Scy softn->ipf_nat_stats.ns_side6[1].ns_inuse = 0; 8167255332Scy#endif 8168255332Scy 8169255332Scy for (nat = softn->ipf_nat_instances; nat != NULL; nat = nat->nat_next) { 8170255332Scy nat->nat_hnext[0] = NULL; 8171255332Scy nat->nat_phnext[0] = NULL; 8172255332Scy hv = nat->nat_hv[0] % softn->ipf_nat_table_sz; 8173255332Scy 8174255332Scy natp = &softn->ipf_nat_table[0][hv]; 8175255332Scy if (*natp) { 8176255332Scy (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 8177255332Scy } else { 8178255332Scy NBUMPSIDE(0, ns_inuse); 8179255332Scy } 8180255332Scy nat->nat_phnext[0] = natp; 8181255332Scy nat->nat_hnext[0] = *natp; 8182255332Scy *natp = nat; 8183255332Scy NBUMPSIDE(0, ns_bucketlen[hv]); 8184255332Scy 8185255332Scy nat->nat_hnext[1] = NULL; 8186255332Scy nat->nat_phnext[1] = NULL; 8187255332Scy hv = nat->nat_hv[1] % softn->ipf_nat_table_sz; 8188255332Scy 8189255332Scy natp = &softn->ipf_nat_table[1][hv]; 8190255332Scy if (*natp) { 8191255332Scy (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 8192255332Scy } else { 8193255332Scy NBUMPSIDE(1, ns_inuse); 8194255332Scy } 8195255332Scy nat->nat_phnext[1] = natp; 8196255332Scy nat->nat_hnext[1] = *natp; 8197255332Scy *natp = nat; 8198255332Scy NBUMPSIDE(1, ns_bucketlen[hv]); 8199255332Scy } 8200255332Scy RWLOCK_EXIT(&softc->ipf_nat); 8201255332Scy 8202255332Scy return 0; 8203255332Scy 8204255332Scybadrehash: 8205255332Scy if (bucketlens[1] != NULL) { 8206255332Scy KFREES(bucketlens[0], newsize * sizeof(u_int)); 8207255332Scy } 8208255332Scy if (bucketlens[0] != NULL) { 8209255332Scy KFREES(bucketlens[0], newsize * sizeof(u_int)); 8210255332Scy } 8211255332Scy if (newtab[0] != NULL) { 8212255332Scy KFREES(newtab[0], newsize * sizeof(nat_t *)); 8213255332Scy } 8214255332Scy if (newtab[1] != NULL) { 8215255332Scy KFREES(newtab[1], newsize * sizeof(nat_t *)); 8216255332Scy } 8217255332Scy IPFERROR(error); 8218255332Scy return ENOMEM; 8219255332Scy} 8220255332Scy 8221255332Scy 8222255332Scy/* ------------------------------------------------------------------------ */ 8223255332Scy/* Function: ipf_nat_rehash_rules */ 8224255332Scy/* Returns: int - 0 = success, else failure */ 8225255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8226255332Scy/* t(I) - pointer to tunable */ 8227255332Scy/* p(I) - pointer to new tuning data */ 8228255332Scy/* */ 8229255332Scy/* All of the NAT rules hang off of a hash table that is searched with a */ 8230255332Scy/* hash on address after the netmask is applied. There is a different table*/ 8231255332Scy/* for both inbound rules (rdr) and outbound (map.) The resizing will only */ 8232255332Scy/* affect one of these two tables. */ 8233255332Scy/* ------------------------------------------------------------------------ */ 8234255332Scyint 8235255332Scyipf_nat_rehash_rules(softc, t, p) 8236255332Scy ipf_main_softc_t *softc; 8237255332Scy ipftuneable_t *t; 8238255332Scy ipftuneval_t *p; 8239255332Scy{ 8240255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 8241255332Scy ipnat_t **newtab, *np, ***old, **npp; 8242255332Scy u_int newsize; 8243255332Scy u_int mask; 8244255332Scy u_int hv; 8245255332Scy 8246255332Scy newsize = p->ipftu_int; 8247255332Scy /* 8248255332Scy * In case there is nothing to do... 8249255332Scy */ 8250255332Scy if (newsize == *t->ipft_pint) 8251255332Scy return 0; 8252255332Scy 8253255332Scy /* 8254255332Scy * All inbound rules have the NAT_REDIRECT bit set in in_redir and 8255255332Scy * all outbound rules have either NAT_MAP or MAT_MAPBLK set. 8256255332Scy * This if statement allows for some more generic code to be below, 8257255332Scy * rather than two huge gobs of code that almost do the same thing. 8258255332Scy */ 8259255332Scy if (t->ipft_pint == &softn->ipf_nat_rdrrules_sz) { 8260255332Scy old = &softn->ipf_nat_rdr_rules; 8261255332Scy mask = NAT_REDIRECT; 8262255332Scy } else { 8263255332Scy old = &softn->ipf_nat_map_rules; 8264255332Scy mask = NAT_MAP|NAT_MAPBLK; 8265255332Scy } 8266255332Scy 8267255332Scy KMALLOCS(newtab, ipnat_t **, newsize * sizeof(ipnat_t *)); 8268255332Scy if (newtab == NULL) { 8269255332Scy IPFERROR(60067); 8270255332Scy return ENOMEM; 8271255332Scy } 8272255332Scy 8273255332Scy bzero((char *)newtab, newsize * sizeof(ipnat_t *)); 8274255332Scy 8275255332Scy WRITE_ENTER(&softc->ipf_nat); 8276255332Scy 8277255332Scy if (*old != NULL) { 8278255332Scy KFREES(*old, *t->ipft_pint * sizeof(ipnat_t **)); 8279255332Scy } 8280255332Scy *old = newtab; 8281255332Scy *t->ipft_pint = newsize; 8282255332Scy 8283255332Scy for (np = softn->ipf_nat_list; np != NULL; np = np->in_next) { 8284255332Scy if ((np->in_redir & mask) == 0) 8285255332Scy continue; 8286255332Scy 8287255332Scy if (np->in_redir & NAT_REDIRECT) { 8288255332Scy np->in_rnext = NULL; 8289255332Scy hv = np->in_hv[0] % newsize; 8290255332Scy for (npp = newtab + hv; *npp != NULL; ) 8291255332Scy npp = &(*npp)->in_rnext; 8292255332Scy np->in_prnext = npp; 8293255332Scy *npp = np; 8294255332Scy } 8295255332Scy if (np->in_redir & NAT_MAP) { 8296255332Scy np->in_mnext = NULL; 8297255332Scy hv = np->in_hv[1] % newsize; 8298255332Scy for (npp = newtab + hv; *npp != NULL; ) 8299255332Scy npp = &(*npp)->in_mnext; 8300255332Scy np->in_pmnext = npp; 8301255332Scy *npp = np; 8302255332Scy } 8303255332Scy 8304255332Scy } 8305255332Scy RWLOCK_EXIT(&softc->ipf_nat); 8306255332Scy 8307255332Scy return 0; 8308255332Scy} 8309255332Scy 8310255332Scy 8311255332Scy/* ------------------------------------------------------------------------ */ 8312255332Scy/* Function: ipf_nat_hostmap_rehash */ 8313255332Scy/* Returns: int - 0 = success, else failure */ 8314255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8315255332Scy/* t(I) - pointer to tunable */ 8316255332Scy/* p(I) - pointer to new tuning data */ 8317255332Scy/* */ 8318255332Scy/* Allocate and populate a new hash table that will contain a reference to */ 8319255332Scy/* all of the active IP# translations currently in place. */ 8320255332Scy/* ------------------------------------------------------------------------ */ 8321255332Scyint 8322255332Scyipf_nat_hostmap_rehash(softc, t, p) 8323255332Scy ipf_main_softc_t *softc; 8324255332Scy ipftuneable_t *t; 8325255332Scy ipftuneval_t *p; 8326255332Scy{ 8327255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 8328255332Scy hostmap_t *hm, **newtab; 8329255332Scy u_int newsize; 8330255332Scy u_int hv; 8331255332Scy 8332255332Scy newsize = p->ipftu_int; 8333255332Scy /* 8334255332Scy * In case there is nothing to do... 8335255332Scy */ 8336255332Scy if (newsize == *t->ipft_pint) 8337255332Scy return 0; 8338255332Scy 8339255332Scy KMALLOCS(newtab, hostmap_t **, newsize * sizeof(hostmap_t *)); 8340255332Scy if (newtab == NULL) { 8341255332Scy IPFERROR(60068); 8342255332Scy return ENOMEM; 8343255332Scy } 8344255332Scy 8345255332Scy bzero((char *)newtab, newsize * sizeof(hostmap_t *)); 8346255332Scy 8347255332Scy WRITE_ENTER(&softc->ipf_nat); 8348255332Scy if (softn->ipf_hm_maptable != NULL) { 8349255332Scy KFREES(softn->ipf_hm_maptable, 8350255332Scy softn->ipf_nat_hostmap_sz * sizeof(hostmap_t *)); 8351255332Scy } 8352255332Scy softn->ipf_hm_maptable = newtab; 8353255332Scy softn->ipf_nat_hostmap_sz = newsize; 8354255332Scy 8355255332Scy for (hm = softn->ipf_hm_maplist; hm != NULL; hm = hm->hm_next) { 8356255332Scy hv = hm->hm_hv % softn->ipf_nat_hostmap_sz; 8357255332Scy hm->hm_hnext = softn->ipf_hm_maptable[hv]; 8358255332Scy hm->hm_phnext = softn->ipf_hm_maptable + hv; 8359255332Scy if (softn->ipf_hm_maptable[hv] != NULL) 8360255332Scy softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; 8361255332Scy softn->ipf_hm_maptable[hv] = hm; 8362255332Scy } 8363255332Scy RWLOCK_EXIT(&softc->ipf_nat); 8364255332Scy 8365255332Scy return 0; 8366255332Scy} 8367255332Scy 8368255332Scy 8369255332Scy/* ------------------------------------------------------------------------ */ 8370255332Scy/* Function: ipf_nat_add_tq */ 8371255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8372255332Scy/* */ 8373255332Scy/* ------------------------------------------------------------------------ */ 8374255332Scyipftq_t * 8375255332Scyipf_nat_add_tq(softc, ttl) 8376255332Scy ipf_main_softc_t *softc; 8377255332Scy int ttl; 8378255332Scy{ 8379255332Scy ipf_nat_softc_t *softs = softc->ipf_nat_soft; 8380255332Scy 8381255332Scy return ipf_addtimeoutqueue(softc, &softs->ipf_nat_utqe, ttl); 8382255332Scy} 8383255332Scy 8384255332Scy/* ------------------------------------------------------------------------ */ 8385255332Scy/* Function: ipf_nat_uncreate */ 8386255332Scy/* Returns: Nil */ 8387255332Scy/* Parameters: fin(I) - pointer to packet information */ 8388255332Scy/* */ 8389255332Scy/* This function is used to remove a NAT entry from the NAT table when we */ 8390255332Scy/* decide that the create was actually in error. It is thus assumed that */ 8391255332Scy/* fin_flx will have both FI_NATED and FI_NATNEW set. Because we're dealing */ 8392255332Scy/* with the translated packet (not the original), we have to reverse the */ 8393255332Scy/* lookup. Although doing the lookup is expensive (relatively speaking), it */ 8394255332Scy/* is not anticipated that this will be a frequent occurance for normal */ 8395255332Scy/* traffic patterns. */ 8396255332Scy/* ------------------------------------------------------------------------ */ 8397255332Scyvoid 8398255332Scyipf_nat_uncreate(fin) 8399255332Scy fr_info_t *fin; 8400255332Scy{ 8401255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 8402255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 8403255332Scy int nflags; 8404255332Scy nat_t *nat; 8405255332Scy 8406255332Scy switch (fin->fin_p) 8407255332Scy { 8408255332Scy case IPPROTO_TCP : 8409255332Scy nflags = IPN_TCP; 8410255332Scy break; 8411255332Scy case IPPROTO_UDP : 8412255332Scy nflags = IPN_UDP; 8413255332Scy break; 8414255332Scy default : 8415255332Scy nflags = 0; 8416255332Scy break; 8417255332Scy } 8418255332Scy 8419255332Scy WRITE_ENTER(&softc->ipf_nat); 8420255332Scy 8421255332Scy if (fin->fin_out == 0) { 8422255332Scy nat = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p, 8423255332Scy fin->fin_dst, fin->fin_src); 8424255332Scy } else { 8425255332Scy nat = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p, 8426255332Scy fin->fin_src, fin->fin_dst); 8427255332Scy } 8428255332Scy 8429255332Scy if (nat != NULL) { 8430255332Scy NBUMPSIDE(fin->fin_out, ns_uncreate[0]); 8431255332Scy ipf_nat_delete(softc, nat, NL_DESTROY); 8432255332Scy } else { 8433255332Scy NBUMPSIDE(fin->fin_out, ns_uncreate[1]); 8434255332Scy } 8435255332Scy 8436255332Scy RWLOCK_EXIT(&softc->ipf_nat); 8437255332Scy} 8438255332Scy 8439255332Scy 8440255332Scy/* ------------------------------------------------------------------------ */ 8441255332Scy/* Function: ipf_nat_cmp_rules */ 8442255332Scy/* Returns: int - 0 == success, else rules do not match. */ 8443255332Scy/* Parameters: n1(I) - first rule to compare */ 8444255332Scy/* n2(I) - first rule to compare */ 8445255332Scy/* */ 8446255332Scy/* Compare two rules using pointers to each rule. A straight bcmp will not */ 8447255332Scy/* work as some fields (such as in_dst, in_pkts) actually do change once */ 8448255332Scy/* the rule has been loaded into the kernel. Whilst this function returns */ 8449255332Scy/* various non-zero returns, they're strictly to aid in debugging. Use of */ 8450255332Scy/* this function should simply care if the result is zero or not. */ 8451255332Scy/* ------------------------------------------------------------------------ */ 8452255332Scystatic int 8453255332Scyipf_nat_cmp_rules(n1, n2) 8454255332Scy ipnat_t *n1, *n2; 8455255332Scy{ 8456255332Scy if (n1->in_size != n2->in_size) 8457255332Scy return 1; 8458255332Scy 8459255332Scy if (bcmp((char *)&n1->in_v, (char *)&n2->in_v, 8460255332Scy offsetof(ipnat_t, in_ndst) - offsetof(ipnat_t, in_v)) != 0) 8461255332Scy return 2; 8462255332Scy 8463255332Scy if (bcmp((char *)&n1->in_tuc, (char *)&n2->in_tuc, 8464255332Scy n1->in_size - offsetof(ipnat_t, in_tuc)) != 0) 8465255332Scy return 3; 8466255332Scy if (n1->in_ndst.na_atype != n2->in_ndst.na_atype) 8467255332Scy return 5; 8468255332Scy if (n1->in_ndst.na_function != n2->in_ndst.na_function) 8469255332Scy return 6; 8470255332Scy if (bcmp((char *)&n1->in_ndst.na_addr, (char *)&n2->in_ndst.na_addr, 8471255332Scy sizeof(n1->in_ndst.na_addr))) 8472255332Scy return 7; 8473255332Scy if (n1->in_nsrc.na_atype != n2->in_nsrc.na_atype) 8474255332Scy return 8; 8475255332Scy if (n1->in_nsrc.na_function != n2->in_nsrc.na_function) 8476255332Scy return 9; 8477255332Scy if (bcmp((char *)&n1->in_nsrc.na_addr, (char *)&n2->in_nsrc.na_addr, 8478255332Scy sizeof(n1->in_nsrc.na_addr))) 8479255332Scy return 10; 8480255332Scy if (n1->in_odst.na_atype != n2->in_odst.na_atype) 8481255332Scy return 11; 8482255332Scy if (n1->in_odst.na_function != n2->in_odst.na_function) 8483255332Scy return 12; 8484255332Scy if (bcmp((char *)&n1->in_odst.na_addr, (char *)&n2->in_odst.na_addr, 8485255332Scy sizeof(n1->in_odst.na_addr))) 8486255332Scy return 13; 8487255332Scy if (n1->in_osrc.na_atype != n2->in_osrc.na_atype) 8488255332Scy return 14; 8489255332Scy if (n1->in_osrc.na_function != n2->in_osrc.na_function) 8490255332Scy return 15; 8491255332Scy if (bcmp((char *)&n1->in_osrc.na_addr, (char *)&n2->in_osrc.na_addr, 8492255332Scy sizeof(n1->in_osrc.na_addr))) 8493255332Scy return 16; 8494255332Scy return 0; 8495255332Scy} 8496255332Scy 8497255332Scy 8498255332Scy/* ------------------------------------------------------------------------ */ 8499255332Scy/* Function: ipf_nat_rule_init */ 8500255332Scy/* Returns: int - 0 == success, else rules do not match. */ 8501255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8502255332Scy/* softn(I) - pointer to NAT context structure */ 8503255332Scy/* n(I) - first rule to compare */ 8504255332Scy/* */ 8505255332Scy/* ------------------------------------------------------------------------ */ 8506255332Scystatic int 8507255332Scyipf_nat_rule_init(softc, softn, n) 8508255332Scy ipf_main_softc_t *softc; 8509255332Scy ipf_nat_softc_t *softn; 8510255332Scy ipnat_t *n; 8511255332Scy{ 8512255332Scy int error = 0; 8513255332Scy 8514255332Scy if ((n->in_flags & IPN_SIPRANGE) != 0) 8515255332Scy n->in_nsrcatype = FRI_RANGE; 8516255332Scy 8517255332Scy if ((n->in_flags & IPN_DIPRANGE) != 0) 8518255332Scy n->in_ndstatype = FRI_RANGE; 8519255332Scy 8520255332Scy if ((n->in_flags & IPN_SPLIT) != 0) 8521255332Scy n->in_ndstatype = FRI_SPLIT; 8522255332Scy 8523255332Scy if ((n->in_redir & (NAT_MAP|NAT_REWRITE|NAT_DIVERTUDP)) != 0) 8524255332Scy n->in_spnext = n->in_spmin; 8525255332Scy 8526255332Scy if ((n->in_redir & (NAT_REWRITE|NAT_DIVERTUDP)) != 0) { 8527255332Scy n->in_dpnext = n->in_dpmin; 8528255332Scy } else if (n->in_redir == NAT_REDIRECT) { 8529255332Scy n->in_dpnext = n->in_dpmin; 8530255332Scy } 8531255332Scy 8532255332Scy n->in_stepnext = 0; 8533255332Scy 8534255332Scy switch (n->in_v[0]) 8535255332Scy { 8536255332Scy case 4 : 8537255332Scy error = ipf_nat_ruleaddrinit(softc, softn, n); 8538255332Scy if (error != 0) 8539255332Scy return error; 8540255332Scy break; 8541255332Scy#ifdef USE_INET6 8542255332Scy case 6 : 8543255332Scy error = ipf_nat6_ruleaddrinit(softc, softn, n); 8544255332Scy if (error != 0) 8545255332Scy return error; 8546255332Scy break; 8547255332Scy#endif 8548255332Scy default : 8549255332Scy break; 8550255332Scy } 8551255332Scy 8552255332Scy if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) { 8553255332Scy /* 8554255332Scy * Prerecord whether or not the destination of the divert 8555255332Scy * is local or not to the interface the packet is going 8556255332Scy * to be sent out. 8557255332Scy */ 8558255332Scy n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1], 8559255332Scy n->in_ifps[1], &n->in_ndstip6); 8560255332Scy } 8561255332Scy 8562255332Scy return error; 8563255332Scy} 8564255332Scy 8565255332Scy 8566255332Scy/* ------------------------------------------------------------------------ */ 8567255332Scy/* Function: ipf_nat_rule_fini */ 8568255332Scy/* Returns: int - 0 == success, else rules do not match. */ 8569255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8570255332Scy/* n(I) - rule to work on */ 8571255332Scy/* */ 8572255332Scy/* This function is used to release any objects that were referenced during */ 8573255332Scy/* the rule initialisation. This is useful both when free'ing the rule and */ 8574255332Scy/* when handling ioctls that need to initialise these fields but not */ 8575255332Scy/* actually use them after the ioctl processing has finished. */ 8576255332Scy/* ------------------------------------------------------------------------ */ 8577255332Scystatic void 8578255332Scyipf_nat_rule_fini(softc, n) 8579255332Scy ipf_main_softc_t *softc; 8580255332Scy ipnat_t *n; 8581255332Scy{ 8582255332Scy if (n->in_odst.na_atype == FRI_LOOKUP && n->in_odst.na_ptr != NULL) 8583255332Scy ipf_lookup_deref(softc, n->in_odst.na_type, n->in_odst.na_ptr); 8584255332Scy 8585255332Scy if (n->in_osrc.na_atype == FRI_LOOKUP && n->in_osrc.na_ptr != NULL) 8586255332Scy ipf_lookup_deref(softc, n->in_osrc.na_type, n->in_osrc.na_ptr); 8587255332Scy 8588255332Scy if (n->in_ndst.na_atype == FRI_LOOKUP && n->in_ndst.na_ptr != NULL) 8589255332Scy ipf_lookup_deref(softc, n->in_ndst.na_type, n->in_ndst.na_ptr); 8590255332Scy 8591255332Scy if (n->in_nsrc.na_atype == FRI_LOOKUP && n->in_nsrc.na_ptr != NULL) 8592255332Scy ipf_lookup_deref(softc, n->in_nsrc.na_type, n->in_nsrc.na_ptr); 8593255332Scy 8594255332Scy if (n->in_divmp != NULL) 8595255332Scy FREE_MB_T(n->in_divmp); 8596255332Scy} 8597