1145522Sdarrenr/* $FreeBSD: stable/10/sys/contrib/ipfilter/netinet/ip_nat.c 344113 2019-02-14 00:52:03Z 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: stable/10/sys/contrib/ipfilter/netinet/ip_nat.c 344113 2019-02-14 00:52:03Z 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{ 1685145522Sdarrenr if (getlock) { 1686255332Scy WRITE_ENTER(&softc->ipf_nat); 1687145522Sdarrenr } 1688145522Sdarrenr 1689255332Scy ipf_nat_delrule(softc, softn, n, 1); 1690145522Sdarrenr 1691145522Sdarrenr if (getlock) { 1692255332Scy RWLOCK_EXIT(&softc->ipf_nat); /* READ/WRITE */ 1693145522Sdarrenr } 1694145522Sdarrenr} 1695145522Sdarrenr 1696145522Sdarrenr 1697145522Sdarrenr/* ------------------------------------------------------------------------ */ 1698255332Scy/* Function: ipf_nat_getsz */ 1699145522Sdarrenr/* Returns: int - 0 == success, != 0 is the error value. */ 1700255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1701255332Scy/* data(I) - pointer to natget structure with kernel */ 1702255332Scy/* pointer get the size of. */ 1703255332Scy/* getlock(I) - flag indicating whether or not the caller */ 1704255332Scy/* holds a lock on ipf_nat */ 1705145522Sdarrenr/* */ 1706145522Sdarrenr/* Handle SIOCSTGSZ. */ 1707145522Sdarrenr/* Return the size of the nat list entry to be copied back to user space. */ 1708145522Sdarrenr/* The size of the entry is stored in the ng_sz field and the enture natget */ 1709145522Sdarrenr/* structure is copied back to the user. */ 1710145522Sdarrenr/* ------------------------------------------------------------------------ */ 1711255332Scystatic int 1712255332Scyipf_nat_getsz(softc, data, getlock) 1713255332Scy ipf_main_softc_t *softc; 1714255332Scy caddr_t data; 1715255332Scy int getlock; 171660852Sdarrenr{ 1717255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 171860852Sdarrenr ap_session_t *aps; 171960852Sdarrenr nat_t *nat, *n; 172060852Sdarrenr natget_t ng; 1721255332Scy int error; 172260852Sdarrenr 1723255332Scy error = BCOPYIN(data, &ng, sizeof(ng)); 1724255332Scy if (error != 0) { 1725255332Scy IPFERROR(60024); 1726170268Sdarrenr return EFAULT; 1727255332Scy } 172860852Sdarrenr 1729173181Sdarrenr if (getlock) { 1730255332Scy READ_ENTER(&softc->ipf_nat); 1731173181Sdarrenr } 1732173181Sdarrenr 173360852Sdarrenr nat = ng.ng_ptr; 173460852Sdarrenr if (!nat) { 1735255332Scy nat = softn->ipf_nat_instances; 173660852Sdarrenr ng.ng_sz = 0; 1737145522Sdarrenr /* 1738145522Sdarrenr * Empty list so the size returned is 0. Simple. 1739145522Sdarrenr */ 174060852Sdarrenr if (nat == NULL) { 1741173181Sdarrenr if (getlock) { 1742255332Scy RWLOCK_EXIT(&softc->ipf_nat); 1743173181Sdarrenr } 1744255332Scy error = BCOPYOUT(&ng, data, sizeof(ng)); 1745255332Scy if (error != 0) { 1746255332Scy IPFERROR(60025); 1747170268Sdarrenr return EFAULT; 1748255332Scy } 1749145522Sdarrenr return 0; 175060852Sdarrenr } 175160852Sdarrenr } else { 175260852Sdarrenr /* 175360852Sdarrenr * Make sure the pointer we're copying from exists in the 175460852Sdarrenr * current list of entries. Security precaution to prevent 175560852Sdarrenr * copying of random kernel data. 175660852Sdarrenr */ 1757255332Scy for (n = softn->ipf_nat_instances; n; n = n->nat_next) 175860852Sdarrenr if (n == nat) 175960852Sdarrenr break; 1760173181Sdarrenr if (n == NULL) { 1761173181Sdarrenr if (getlock) { 1762255332Scy RWLOCK_EXIT(&softc->ipf_nat); 1763173181Sdarrenr } 1764255332Scy IPFERROR(60026); 176560852Sdarrenr return ESRCH; 1766173181Sdarrenr } 176760852Sdarrenr } 176860852Sdarrenr 1769145522Sdarrenr /* 1770145522Sdarrenr * Incluse any space required for proxy data structures. 1771145522Sdarrenr */ 177260852Sdarrenr ng.ng_sz = sizeof(nat_save_t); 177360852Sdarrenr aps = nat->nat_aps; 1774145522Sdarrenr if (aps != NULL) { 1775145522Sdarrenr ng.ng_sz += sizeof(ap_session_t) - 4; 1776145522Sdarrenr if (aps->aps_data != 0) 1777145522Sdarrenr ng.ng_sz += aps->aps_psiz; 177860852Sdarrenr } 1779173181Sdarrenr if (getlock) { 1780255332Scy RWLOCK_EXIT(&softc->ipf_nat); 1781173181Sdarrenr } 178260852Sdarrenr 1783255332Scy error = BCOPYOUT(&ng, data, sizeof(ng)); 1784255332Scy if (error != 0) { 1785255332Scy IPFERROR(60027); 1786170268Sdarrenr return EFAULT; 1787255332Scy } 1788145522Sdarrenr return 0; 178960852Sdarrenr} 179060852Sdarrenr 179160852Sdarrenr 1792145522Sdarrenr/* ------------------------------------------------------------------------ */ 1793255332Scy/* Function: ipf_nat_getent */ 1794145522Sdarrenr/* Returns: int - 0 == success, != 0 is the error value. */ 1795255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1796255332Scy/* data(I) - pointer to natget structure with kernel pointer*/ 1797255332Scy/* to NAT structure to copy out. */ 1798255332Scy/* getlock(I) - flag indicating whether or not the caller */ 1799255332Scy/* holds a lock on ipf_nat */ 1800145522Sdarrenr/* */ 1801145522Sdarrenr/* Handle SIOCSTGET. */ 1802145522Sdarrenr/* Copies out NAT entry to user space. Any additional data held for a */ 1803145522Sdarrenr/* proxy is also copied, as to is the NAT rule which was responsible for it */ 1804145522Sdarrenr/* ------------------------------------------------------------------------ */ 1805255332Scystatic int 1806255332Scyipf_nat_getent(softc, data, getlock) 1807255332Scy ipf_main_softc_t *softc; 1808255332Scy caddr_t data; 1809255332Scy int getlock; 181060852Sdarrenr{ 1811255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 1812145522Sdarrenr int error, outsize; 181360852Sdarrenr ap_session_t *aps; 1814145522Sdarrenr nat_save_t *ipn, ipns; 1815145522Sdarrenr nat_t *n, *nat; 181660852Sdarrenr 1817255332Scy error = ipf_inobj(softc, data, NULL, &ipns, IPFOBJ_NATSAVE); 1818145522Sdarrenr if (error != 0) 1819145522Sdarrenr return error; 182060852Sdarrenr 1821255332Scy if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) { 1822255332Scy IPFERROR(60028); 1823145522Sdarrenr return EINVAL; 1824255332Scy } 1825145522Sdarrenr 1826145522Sdarrenr KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize); 1827255332Scy if (ipn == NULL) { 1828255332Scy IPFERROR(60029); 1829145522Sdarrenr return ENOMEM; 1830255332Scy } 1831145522Sdarrenr 1832173181Sdarrenr if (getlock) { 1833255332Scy READ_ENTER(&softc->ipf_nat); 1834173181Sdarrenr } 1835173181Sdarrenr 1836145522Sdarrenr ipn->ipn_dsize = ipns.ipn_dsize; 1837145522Sdarrenr nat = ipns.ipn_next; 1838145522Sdarrenr if (nat == NULL) { 1839255332Scy nat = softn->ipf_nat_instances; 184060852Sdarrenr if (nat == NULL) { 1841255332Scy if (softn->ipf_nat_instances == NULL) { 1842255332Scy IPFERROR(60030); 1843145522Sdarrenr error = ENOENT; 1844255332Scy } 1845145522Sdarrenr goto finished; 184660852Sdarrenr } 184760852Sdarrenr } else { 184860852Sdarrenr /* 184960852Sdarrenr * Make sure the pointer we're copying from exists in the 185060852Sdarrenr * current list of entries. Security precaution to prevent 185160852Sdarrenr * copying of random kernel data. 185260852Sdarrenr */ 1853255332Scy for (n = softn->ipf_nat_instances; n; n = n->nat_next) 185460852Sdarrenr if (n == nat) 185560852Sdarrenr break; 1856145522Sdarrenr if (n == NULL) { 1857255332Scy IPFERROR(60031); 1858145522Sdarrenr error = ESRCH; 1859145522Sdarrenr goto finished; 1860145522Sdarrenr } 186160852Sdarrenr } 1862145522Sdarrenr ipn->ipn_next = nat->nat_next; 186360852Sdarrenr 1864145522Sdarrenr /* 1865145522Sdarrenr * Copy the NAT structure. 1866145522Sdarrenr */ 1867145522Sdarrenr bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat)); 186860852Sdarrenr 1869145522Sdarrenr /* 1870145522Sdarrenr * If we have a pointer to the NAT rule it belongs to, save that too. 1871145522Sdarrenr */ 1872145522Sdarrenr if (nat->nat_ptr != NULL) 1873145522Sdarrenr bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat, 1874255332Scy ipn->ipn_ipnat.in_size); 187560852Sdarrenr 1876145522Sdarrenr /* 1877145522Sdarrenr * If we also know the NAT entry has an associated filter rule, 1878145522Sdarrenr * save that too. 1879145522Sdarrenr */ 1880145522Sdarrenr if (nat->nat_fr != NULL) 1881145522Sdarrenr bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr, 1882145522Sdarrenr sizeof(ipn->ipn_fr)); 188360852Sdarrenr 1884145522Sdarrenr /* 1885145522Sdarrenr * Last but not least, if there is an application proxy session set 1886145522Sdarrenr * up for this NAT entry, then copy that out too, including any 1887145522Sdarrenr * private data saved along side it by the proxy. 1888145522Sdarrenr */ 1889145522Sdarrenr aps = nat->nat_aps; 1890145522Sdarrenr outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data); 1891145522Sdarrenr if (aps != NULL) { 1892145522Sdarrenr char *s; 189360852Sdarrenr 1894145522Sdarrenr if (outsize < sizeof(*aps)) { 1895255332Scy IPFERROR(60032); 1896145522Sdarrenr error = ENOBUFS; 1897145522Sdarrenr goto finished; 189860852Sdarrenr } 1899145522Sdarrenr 1900145522Sdarrenr s = ipn->ipn_data; 1901145522Sdarrenr bcopy((char *)aps, s, sizeof(*aps)); 1902145522Sdarrenr s += sizeof(*aps); 1903145522Sdarrenr outsize -= sizeof(*aps); 1904145522Sdarrenr if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz)) 1905145522Sdarrenr bcopy(aps->aps_data, s, aps->aps_psiz); 1906255332Scy else { 1907255332Scy IPFERROR(60033); 1908145522Sdarrenr error = ENOBUFS; 1909255332Scy } 191060852Sdarrenr } 1911145522Sdarrenr if (error == 0) { 1912255332Scy error = ipf_outobjsz(softc, data, ipn, IPFOBJ_NATSAVE, 1913255332Scy ipns.ipn_dsize); 1914145522Sdarrenr } 1915145522Sdarrenr 1916145522Sdarrenrfinished: 1917145522Sdarrenr if (ipn != NULL) { 1918145522Sdarrenr KFREES(ipn, ipns.ipn_dsize); 1919145522Sdarrenr } 1920344113Scy if (getlock) { 1921344113Scy RWLOCK_EXIT(&softc->ipf_nat); 1922344113Scy } 192364580Sdarrenr return error; 192460852Sdarrenr} 192560852Sdarrenr 192660852Sdarrenr 1927145522Sdarrenr/* ------------------------------------------------------------------------ */ 1928255332Scy/* Function: ipf_nat_putent */ 1929145522Sdarrenr/* Returns: int - 0 == success, != 0 is the error value. */ 1930255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1931255332Scy/* data(I) - pointer to natget structure with NAT */ 1932255332Scy/* structure information to load into the kernel */ 1933145522Sdarrenr/* getlock(I) - flag indicating whether or not a write lock */ 1934255332Scy/* on is already held. */ 1935145522Sdarrenr/* */ 1936145522Sdarrenr/* Handle SIOCSTPUT. */ 1937145522Sdarrenr/* Loads a NAT table entry from user space, including a NAT rule, proxy and */ 1938145522Sdarrenr/* firewall rule data structures, if pointers to them indicate so. */ 1939145522Sdarrenr/* ------------------------------------------------------------------------ */ 1940255332Scystatic int 1941255332Scyipf_nat_putent(softc, data, getlock) 1942255332Scy ipf_main_softc_t *softc; 1943255332Scy caddr_t data; 1944255332Scy int getlock; 194560852Sdarrenr{ 1946255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 1947145522Sdarrenr nat_save_t ipn, *ipnn; 194860852Sdarrenr ap_session_t *aps; 1949145522Sdarrenr nat_t *n, *nat; 195060852Sdarrenr frentry_t *fr; 1951145522Sdarrenr fr_info_t fin; 195260852Sdarrenr ipnat_t *in; 195360852Sdarrenr int error; 195460852Sdarrenr 1955255332Scy error = ipf_inobj(softc, data, NULL, &ipn, IPFOBJ_NATSAVE); 1956145522Sdarrenr if (error != 0) 1957145522Sdarrenr return error; 1958145522Sdarrenr 1959145522Sdarrenr /* 1960145522Sdarrenr * Initialise early because of code at junkput label. 1961145522Sdarrenr */ 1962255332Scy n = NULL; 1963145522Sdarrenr in = NULL; 1964145522Sdarrenr aps = NULL; 196564580Sdarrenr nat = NULL; 1966145522Sdarrenr ipnn = NULL; 1967170268Sdarrenr fr = NULL; 1968145522Sdarrenr 1969145522Sdarrenr /* 1970145522Sdarrenr * New entry, copy in the rest of the NAT entry if it's size is more 1971145522Sdarrenr * than just the nat_t structure. 1972145522Sdarrenr */ 1973145522Sdarrenr if (ipn.ipn_dsize > sizeof(ipn)) { 1974145522Sdarrenr if (ipn.ipn_dsize > 81920) { 1975255332Scy IPFERROR(60034); 1976145522Sdarrenr error = ENOMEM; 1977145522Sdarrenr goto junkput; 1978145522Sdarrenr } 1979145522Sdarrenr 1980145522Sdarrenr KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize); 1981255332Scy if (ipnn == NULL) { 1982255332Scy IPFERROR(60035); 198360852Sdarrenr return ENOMEM; 1984255332Scy } 1985145522Sdarrenr 1986255332Scy bzero(ipnn, ipn.ipn_dsize); 1987255332Scy error = ipf_inobjsz(softc, data, ipnn, IPFOBJ_NATSAVE, 1988255332Scy ipn.ipn_dsize); 1989145522Sdarrenr if (error != 0) { 199064580Sdarrenr goto junkput; 199164580Sdarrenr } 199260852Sdarrenr } else 1993145522Sdarrenr ipnn = &ipn; 199460852Sdarrenr 199560852Sdarrenr KMALLOC(nat, nat_t *); 199664580Sdarrenr if (nat == NULL) { 1997255332Scy IPFERROR(60037); 1998145522Sdarrenr error = ENOMEM; 199964580Sdarrenr goto junkput; 200064580Sdarrenr } 200160852Sdarrenr 2002145522Sdarrenr bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat)); 2003255332Scy 2004255332Scy switch (nat->nat_v[0]) 2005255332Scy { 2006255332Scy case 4: 2007255332Scy#ifdef USE_INET6 2008255332Scy case 6 : 2009255332Scy#endif 2010255332Scy break; 2011255332Scy default : 2012255332Scy IPFERROR(60061); 2013255332Scy error = EPROTONOSUPPORT; 2014255332Scy goto junkput; 2015255332Scy /*NOTREACHED*/ 2016255332Scy } 2017255332Scy 201860852Sdarrenr /* 2019255332Scy * Initialize all these so that ipf_nat_delete() doesn't cause a crash. 202060852Sdarrenr */ 2021145522Sdarrenr bzero((char *)nat, offsetof(struct nat, nat_tqe)); 2022145522Sdarrenr nat->nat_tqe.tqe_pnext = NULL; 2023145522Sdarrenr nat->nat_tqe.tqe_next = NULL; 2024145522Sdarrenr nat->nat_tqe.tqe_ifq = NULL; 2025145522Sdarrenr nat->nat_tqe.tqe_parent = nat; 202660852Sdarrenr 202760852Sdarrenr /* 202860852Sdarrenr * Restore the rule associated with this nat session 202960852Sdarrenr */ 2030145522Sdarrenr in = ipnn->ipn_nat.nat_ptr; 2031145522Sdarrenr if (in != NULL) { 2032255332Scy KMALLOCS(in, ipnat_t *, ipnn->ipn_ipnat.in_size); 2033145522Sdarrenr nat->nat_ptr = in; 203460852Sdarrenr if (in == NULL) { 2035255332Scy IPFERROR(60038); 203660852Sdarrenr error = ENOMEM; 203760852Sdarrenr goto junkput; 203860852Sdarrenr } 2039255332Scy bcopy((char *)&ipnn->ipn_ipnat, (char *)in, 2040255332Scy ipnn->ipn_ipnat.in_size); 204160852Sdarrenr in->in_use = 1; 204260852Sdarrenr in->in_flags |= IPN_DELETE; 2043145522Sdarrenr 2044255332Scy ATOMIC_INC32(softn->ipf_nat_stats.ns_rules); 2045145522Sdarrenr 2046255332Scy if (ipf_nat_resolverule(softc, in) != 0) { 2047255332Scy IPFERROR(60039); 2048161356Sguido error = ESRCH; 2049161356Sguido goto junkput; 2050161356Sguido } 2051145522Sdarrenr } 2052145522Sdarrenr 2053145522Sdarrenr /* 2054145522Sdarrenr * Check that the NAT entry doesn't already exist in the kernel. 2055161356Sguido * 2056161356Sguido * For NAT_OUTBOUND, we're lookup for a duplicate MAP entry. To do 2057161356Sguido * this, we check to see if the inbound combination of addresses and 2058161356Sguido * ports is already known. Similar logic is applied for NAT_INBOUND. 2059255332Scy * 2060145522Sdarrenr */ 2061145522Sdarrenr bzero((char *)&fin, sizeof(fin)); 2062255332Scy fin.fin_v = nat->nat_v[0]; 2063255332Scy fin.fin_p = nat->nat_pr[0]; 2064255332Scy fin.fin_rev = nat->nat_rev; 2065255332Scy fin.fin_ifp = nat->nat_ifps[0]; 2066255332Scy fin.fin_data[0] = ntohs(nat->nat_ndport); 2067255332Scy fin.fin_data[1] = ntohs(nat->nat_nsport); 2068255332Scy 2069255332Scy switch (nat->nat_dir) 2070255332Scy { 2071255332Scy case NAT_OUTBOUND : 2072255332Scy case NAT_DIVERTOUT : 2073153876Sguido if (getlock) { 2074255332Scy READ_ENTER(&softc->ipf_nat); 2075153876Sguido } 2076255332Scy 2077255332Scy fin.fin_v = nat->nat_v[1]; 2078255332Scy if (nat->nat_v[1] == 4) { 2079255332Scy n = ipf_nat_inlookup(&fin, nat->nat_flags, fin.fin_p, 2080255332Scy nat->nat_ndstip, nat->nat_nsrcip); 2081255332Scy#ifdef USE_INET6 2082255332Scy } else if (nat->nat_v[1] == 6) { 2083255332Scy n = ipf_nat6_inlookup(&fin, nat->nat_flags, fin.fin_p, 2084255332Scy &nat->nat_ndst6.in6, 2085255332Scy &nat->nat_nsrc6.in6); 2086255332Scy#endif 2087255332Scy } 2088255332Scy 2089153876Sguido if (getlock) { 2090255332Scy RWLOCK_EXIT(&softc->ipf_nat); 2091153876Sguido } 2092153876Sguido if (n != NULL) { 2093255332Scy IPFERROR(60040); 2094145522Sdarrenr error = EEXIST; 2095145522Sdarrenr goto junkput; 209660852Sdarrenr } 2097255332Scy break; 2098255332Scy 2099255332Scy case NAT_INBOUND : 2100255332Scy case NAT_DIVERTIN : 2101153876Sguido if (getlock) { 2102255332Scy READ_ENTER(&softc->ipf_nat); 2103153876Sguido } 2104255332Scy 2105255332Scy if (fin.fin_v == 4) { 2106255332Scy n = ipf_nat_outlookup(&fin, nat->nat_flags, fin.fin_p, 2107255332Scy nat->nat_ndstip, 2108255332Scy nat->nat_nsrcip); 2109255332Scy#ifdef USE_INET6 2110255332Scy } else if (fin.fin_v == 6) { 2111255332Scy n = ipf_nat6_outlookup(&fin, nat->nat_flags, fin.fin_p, 2112255332Scy &nat->nat_ndst6.in6, 2113255332Scy &nat->nat_nsrc6.in6); 2114255332Scy#endif 2115255332Scy } 2116255332Scy 2117153876Sguido if (getlock) { 2118255332Scy RWLOCK_EXIT(&softc->ipf_nat); 2119153876Sguido } 2120153876Sguido if (n != NULL) { 2121255332Scy IPFERROR(60041); 2122145522Sdarrenr error = EEXIST; 2123145522Sdarrenr goto junkput; 2124145522Sdarrenr } 2125255332Scy break; 2126255332Scy 2127255332Scy default : 2128255332Scy IPFERROR(60042); 2129145522Sdarrenr error = EINVAL; 2130145522Sdarrenr goto junkput; 213160852Sdarrenr } 213260852Sdarrenr 213360852Sdarrenr /* 213460852Sdarrenr * Restore ap_session_t structure. Include the private data allocated 213560852Sdarrenr * if it was there. 213660852Sdarrenr */ 2137145522Sdarrenr aps = nat->nat_aps; 2138145522Sdarrenr if (aps != NULL) { 213960852Sdarrenr KMALLOC(aps, ap_session_t *); 2140145522Sdarrenr nat->nat_aps = aps; 214160852Sdarrenr if (aps == NULL) { 2142255332Scy IPFERROR(60043); 214360852Sdarrenr error = ENOMEM; 214460852Sdarrenr goto junkput; 214560852Sdarrenr } 214660852Sdarrenr bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps)); 2147145522Sdarrenr if (in != NULL) 214860852Sdarrenr aps->aps_apr = in->in_apr; 2149145522Sdarrenr else 2150145522Sdarrenr aps->aps_apr = NULL; 2151145522Sdarrenr if (aps->aps_psiz != 0) { 2152145522Sdarrenr if (aps->aps_psiz > 81920) { 2153255332Scy IPFERROR(60044); 2154145522Sdarrenr error = ENOMEM; 2155145522Sdarrenr goto junkput; 2156145522Sdarrenr } 215760852Sdarrenr KMALLOCS(aps->aps_data, void *, aps->aps_psiz); 215860852Sdarrenr if (aps->aps_data == NULL) { 2159255332Scy IPFERROR(60045); 216060852Sdarrenr error = ENOMEM; 216160852Sdarrenr goto junkput; 216260852Sdarrenr } 216360852Sdarrenr bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data, 216460852Sdarrenr aps->aps_psiz); 216560852Sdarrenr } else { 216660852Sdarrenr aps->aps_psiz = 0; 216760852Sdarrenr aps->aps_data = NULL; 216860852Sdarrenr } 216960852Sdarrenr } 217060852Sdarrenr 217160852Sdarrenr /* 217260852Sdarrenr * If there was a filtering rule associated with this entry then 217360852Sdarrenr * build up a new one. 217460852Sdarrenr */ 2175145522Sdarrenr fr = nat->nat_fr; 217660852Sdarrenr if (fr != NULL) { 2177145522Sdarrenr if ((nat->nat_flags & SI_NEWFR) != 0) { 217860852Sdarrenr KMALLOC(fr, frentry_t *); 217960852Sdarrenr nat->nat_fr = fr; 218060852Sdarrenr if (fr == NULL) { 2181255332Scy IPFERROR(60046); 218260852Sdarrenr error = ENOMEM; 218360852Sdarrenr goto junkput; 218460852Sdarrenr } 2185145522Sdarrenr ipnn->ipn_nat.nat_fr = fr; 2186145522Sdarrenr fr->fr_ref = 1; 2187255332Scy (void) ipf_outobj(softc, data, ipnn, IPFOBJ_NATSAVE); 2188145522Sdarrenr bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr)); 2189161356Sguido 2190161356Sguido fr->fr_ref = 1; 2191161356Sguido fr->fr_dsize = 0; 2192161356Sguido fr->fr_data = NULL; 2193161356Sguido fr->fr_type = FR_T_NONE; 2194161356Sguido 2195145522Sdarrenr MUTEX_NUKE(&fr->fr_lock); 2196145522Sdarrenr MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock"); 219760852Sdarrenr } else { 2198153876Sguido if (getlock) { 2199255332Scy READ_ENTER(&softc->ipf_nat); 2200153876Sguido } 2201255332Scy for (n = softn->ipf_nat_instances; n; n = n->nat_next) 220260852Sdarrenr if (n->nat_fr == fr) 220360852Sdarrenr break; 2204145522Sdarrenr 2205145522Sdarrenr if (n != NULL) { 2206145522Sdarrenr MUTEX_ENTER(&fr->fr_lock); 2207145522Sdarrenr fr->fr_ref++; 2208145522Sdarrenr MUTEX_EXIT(&fr->fr_lock); 2209145522Sdarrenr } 2210153876Sguido if (getlock) { 2211255332Scy RWLOCK_EXIT(&softc->ipf_nat); 2212153876Sguido } 2213145522Sdarrenr 2214255332Scy if (n == NULL) { 2215255332Scy IPFERROR(60047); 221660852Sdarrenr error = ESRCH; 221760852Sdarrenr goto junkput; 221860852Sdarrenr } 221960852Sdarrenr } 222060852Sdarrenr } 222160852Sdarrenr 2222145522Sdarrenr if (ipnn != &ipn) { 2223145522Sdarrenr KFREES(ipnn, ipn.ipn_dsize); 2224145522Sdarrenr ipnn = NULL; 2225145522Sdarrenr } 2226145522Sdarrenr 2227145522Sdarrenr if (getlock) { 2228255332Scy WRITE_ENTER(&softc->ipf_nat); 2229145522Sdarrenr } 2230255332Scy 2231255332Scy if (fin.fin_v == 4) 2232255332Scy error = ipf_nat_finalise(&fin, nat); 2233255332Scy#ifdef USE_INET6 2234255332Scy else 2235255332Scy error = ipf_nat6_finalise(&fin, nat); 2236255332Scy#endif 2237255332Scy 2238145522Sdarrenr if (getlock) { 2239255332Scy RWLOCK_EXIT(&softc->ipf_nat); 2240145522Sdarrenr } 2241145522Sdarrenr 2242145522Sdarrenr if (error == 0) 2243145522Sdarrenr return 0; 2244145522Sdarrenr 2245255332Scy IPFERROR(60048); 2246145522Sdarrenr error = ENOMEM; 2247145522Sdarrenr 224860852Sdarrenrjunkput: 2249255332Scy if (fr != NULL) { 2250255332Scy (void) ipf_derefrule(softc, &fr); 2251255332Scy } 2252145522Sdarrenr 2253145522Sdarrenr if ((ipnn != NULL) && (ipnn != &ipn)) { 2254145522Sdarrenr KFREES(ipnn, ipn.ipn_dsize); 2255145522Sdarrenr } 2256145522Sdarrenr if (nat != NULL) { 2257145522Sdarrenr if (aps != NULL) { 2258145522Sdarrenr if (aps->aps_data != NULL) { 2259145522Sdarrenr KFREES(aps->aps_data, aps->aps_psiz); 2260145522Sdarrenr } 2261145522Sdarrenr KFREE(aps); 2262145522Sdarrenr } 2263145522Sdarrenr if (in != NULL) { 2264145522Sdarrenr if (in->in_apr) 2265255332Scy ipf_proxy_deref(in->in_apr); 2266255332Scy KFREES(in, in->in_size); 2267145522Sdarrenr } 2268145522Sdarrenr KFREE(nat); 2269145522Sdarrenr } 227060852Sdarrenr return error; 227160852Sdarrenr} 227260852Sdarrenr 227360852Sdarrenr 2274145522Sdarrenr/* ------------------------------------------------------------------------ */ 2275255332Scy/* Function: ipf_nat_delete */ 2276145522Sdarrenr/* Returns: Nil */ 2277255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 2278255332Scy/* nat(I) - pointer to NAT structure to delete */ 2279145522Sdarrenr/* logtype(I) - type of LOG record to create before deleting */ 2280145522Sdarrenr/* Write Lock: ipf_nat */ 2281145522Sdarrenr/* */ 2282145522Sdarrenr/* Delete a nat entry from the various lists and table. If NAT logging is */ 2283145522Sdarrenr/* enabled then generate a NAT log record for this event. */ 2284145522Sdarrenr/* ------------------------------------------------------------------------ */ 2285255332Scyvoid 2286255332Scyipf_nat_delete(softc, nat, logtype) 2287255332Scy ipf_main_softc_t *softc; 2288255332Scy struct nat *nat; 2289255332Scy int logtype; 229053642Sguido{ 2291255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 2292255332Scy int madeorphan = 0, bkt, removed = 0; 2293255332Scy nat_stat_side_t *nss; 229453642Sguido struct ipnat *ipn; 229553642Sguido 2296255332Scy if (logtype != 0 && softn->ipf_nat_logging != 0) 2297255332Scy ipf_nat_log(softc, softn, nat, logtype); 229853642Sguido 2299145522Sdarrenr /* 2300145522Sdarrenr * Take it as a general indication that all the pointers are set if 2301145522Sdarrenr * nat_pnext is set. 2302145522Sdarrenr */ 2303145522Sdarrenr if (nat->nat_pnext != NULL) { 2304172776Sdarrenr removed = 1; 2305172776Sdarrenr 2306255332Scy bkt = nat->nat_hv[0] % softn->ipf_nat_table_sz; 2307255332Scy nss = &softn->ipf_nat_stats.ns_side[0]; 2308338171Scy if (nss->ns_bucketlen[bkt] > 0) 2309338171Scy nss->ns_bucketlen[bkt]--; 2310255332Scy if (nss->ns_bucketlen[bkt] == 0) { 2311255332Scy nss->ns_inuse--; 2312255332Scy } 2313145522Sdarrenr 2314255332Scy bkt = nat->nat_hv[1] % softn->ipf_nat_table_sz; 2315255332Scy nss = &softn->ipf_nat_stats.ns_side[1]; 2316338171Scy if (nss->ns_bucketlen[bkt] > 0) 2317338171Scy nss->ns_bucketlen[bkt]--; 2318255332Scy if (nss->ns_bucketlen[bkt] == 0) { 2319255332Scy nss->ns_inuse--; 2320255332Scy } 2321255332Scy 2322145522Sdarrenr *nat->nat_pnext = nat->nat_next; 2323145522Sdarrenr if (nat->nat_next != NULL) { 2324145522Sdarrenr nat->nat_next->nat_pnext = nat->nat_pnext; 2325145522Sdarrenr nat->nat_next = NULL; 2326145522Sdarrenr } 2327145522Sdarrenr nat->nat_pnext = NULL; 2328145522Sdarrenr 2329145522Sdarrenr *nat->nat_phnext[0] = nat->nat_hnext[0]; 2330145522Sdarrenr if (nat->nat_hnext[0] != NULL) { 2331145522Sdarrenr nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 2332145522Sdarrenr nat->nat_hnext[0] = NULL; 2333145522Sdarrenr } 2334145522Sdarrenr nat->nat_phnext[0] = NULL; 2335145522Sdarrenr 2336145522Sdarrenr *nat->nat_phnext[1] = nat->nat_hnext[1]; 2337145522Sdarrenr if (nat->nat_hnext[1] != NULL) { 2338145522Sdarrenr nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 2339145522Sdarrenr nat->nat_hnext[1] = NULL; 2340145522Sdarrenr } 2341145522Sdarrenr nat->nat_phnext[1] = NULL; 2342145522Sdarrenr 2343255332Scy if ((nat->nat_flags & SI_WILDP) != 0) { 2344255332Scy ATOMIC_DEC32(softn->ipf_nat_stats.ns_wilds); 2345255332Scy } 2346255332Scy madeorphan = 1; 234753642Sguido } 234860852Sdarrenr 2349145522Sdarrenr if (nat->nat_me != NULL) { 2350145522Sdarrenr *nat->nat_me = NULL; 2351145522Sdarrenr nat->nat_me = NULL; 2352255332Scy nat->nat_ref--; 2353255332Scy ASSERT(nat->nat_ref >= 0); 2354145522Sdarrenr } 235560852Sdarrenr 2356255332Scy if (nat->nat_tqe.tqe_ifq != NULL) { 2357255332Scy /* 2358255332Scy * No call to ipf_freetimeoutqueue() is made here, they are 2359255332Scy * garbage collected in ipf_nat_expire(). 2360255332Scy */ 2361255332Scy (void) ipf_deletequeueentry(&nat->nat_tqe); 2362255332Scy } 2363145522Sdarrenr 2364255332Scy if (nat->nat_sync) { 2365255332Scy ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); 2366255332Scy nat->nat_sync = NULL; 2367255332Scy } 2368255332Scy 2369170268Sdarrenr if (logtype == NL_EXPIRE) 2370255332Scy softn->ipf_nat_stats.ns_expire++; 2371170268Sdarrenr 2372172776Sdarrenr MUTEX_ENTER(&nat->nat_lock); 2373172776Sdarrenr /* 2374172776Sdarrenr * NL_DESTROY should only be passed in when we've got nat_ref >= 2. 2375172776Sdarrenr * This happens when a nat'd packet is blocked and we want to throw 2376172776Sdarrenr * away the NAT session. 2377172776Sdarrenr */ 2378172776Sdarrenr if (logtype == NL_DESTROY) { 2379172776Sdarrenr if (nat->nat_ref > 2) { 2380172776Sdarrenr nat->nat_ref -= 2; 2381172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 2382172776Sdarrenr if (removed) 2383255332Scy softn->ipf_nat_stats.ns_orphans++; 2384172776Sdarrenr return; 2385172776Sdarrenr } 2386172776Sdarrenr } else if (nat->nat_ref > 1) { 2387172776Sdarrenr nat->nat_ref--; 2388172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 2389255332Scy if (madeorphan == 1) 2390255332Scy softn->ipf_nat_stats.ns_orphans++; 2391145522Sdarrenr return; 2392145522Sdarrenr } 2393255332Scy ASSERT(nat->nat_ref >= 0); 2394172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 2395170268Sdarrenr 2396255332Scy nat->nat_ref = 0; 2397255332Scy 2398255332Scy if (madeorphan == 0) 2399255332Scy softn->ipf_nat_stats.ns_orphans--; 2400255332Scy 2401161356Sguido /* 2402255332Scy * At this point, nat_ref can be either 0 or -1 2403161356Sguido */ 2404255332Scy softn->ipf_nat_stats.ns_proto[nat->nat_pr[0]]--; 2405145522Sdarrenr 2406255332Scy if (nat->nat_fr != NULL) { 2407255332Scy (void) ipf_derefrule(softc, &nat->nat_fr); 2408255332Scy } 2409145522Sdarrenr 2410255332Scy if (nat->nat_hm != NULL) { 2411255332Scy ipf_nat_hostmapdel(softc, &nat->nat_hm); 2412255332Scy } 2413145522Sdarrenr 241453642Sguido /* 241553642Sguido * If there is an active reference from the nat entry to its parent 241653642Sguido * rule, decrement the rule's reference count and free it too if no 241753642Sguido * longer being used. 241853642Sguido */ 2419145522Sdarrenr ipn = nat->nat_ptr; 2420255332Scy nat->nat_ptr = NULL; 2421255332Scy 242253642Sguido if (ipn != NULL) { 2423255332Scy ipn->in_space++; 2424255332Scy ipf_nat_rule_deref(softc, &ipn); 242553642Sguido } 242653642Sguido 2427255332Scy if (nat->nat_aps != NULL) { 2428255332Scy ipf_proxy_free(softc, nat->nat_aps); 2429255332Scy nat->nat_aps = NULL; 2430255332Scy } 2431255332Scy 2432145522Sdarrenr MUTEX_DESTROY(&nat->nat_lock); 2433145522Sdarrenr 2434255332Scy softn->ipf_nat_stats.ns_active--; 2435145522Sdarrenr 243653642Sguido /* 243753642Sguido * If there's a fragment table entry too for this nat entry, then 2438145522Sdarrenr * dereference that as well. This is after nat_lock is released 2439145522Sdarrenr * because of Tru64. 244053642Sguido */ 2441255332Scy ipf_frag_natforget(softc, (void *)nat); 2442145522Sdarrenr 2443145522Sdarrenr KFREE(nat); 244453642Sguido} 244553642Sguido 244653642Sguido 2447145522Sdarrenr/* ------------------------------------------------------------------------ */ 2448255332Scy/* Function: ipf_nat_flushtable */ 2449145522Sdarrenr/* Returns: int - number of NAT rules deleted */ 2450255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 2451255332Scy/* softn(I) - pointer to NAT context structure */ 2452255332Scy/* Write Lock: ipf_nat */ 2453145522Sdarrenr/* */ 2454145522Sdarrenr/* Deletes all currently active NAT sessions. In deleting each NAT entry a */ 2455255332Scy/* log record should be emitted in ipf_nat_delete() if NAT logging is */ 2456255332Scy/* enabled. */ 2457145522Sdarrenr/* ------------------------------------------------------------------------ */ 245853642Sguido/* 245953642Sguido * nat_flushtable - clear the NAT table of all mapping entries. 246053642Sguido */ 2461255332Scystatic int 2462255332Scyipf_nat_flushtable(softc, softn) 2463255332Scy ipf_main_softc_t *softc; 2464255332Scy ipf_nat_softc_t *softn; 246553642Sguido{ 2466145522Sdarrenr nat_t *nat; 2467145522Sdarrenr int j = 0; 246867614Sdarrenr 246953642Sguido /* 247053642Sguido * ALL NAT mappings deleted, so lets just make the deletions 247153642Sguido * quicker. 247253642Sguido */ 2473255332Scy if (softn->ipf_nat_table[0] != NULL) 2474255332Scy bzero((char *)softn->ipf_nat_table[0], 2475255332Scy sizeof(softn->ipf_nat_table[0]) * 2476255332Scy softn->ipf_nat_table_sz); 2477255332Scy if (softn->ipf_nat_table[1] != NULL) 2478255332Scy bzero((char *)softn->ipf_nat_table[1], 2479255332Scy sizeof(softn->ipf_nat_table[1]) * 2480255332Scy softn->ipf_nat_table_sz); 248153642Sguido 2482255332Scy while ((nat = softn->ipf_nat_instances) != NULL) { 2483255332Scy ipf_nat_delete(softc, nat, NL_FLUSH); 248453642Sguido j++; 248553642Sguido } 2486145522Sdarrenr 248753642Sguido return j; 248853642Sguido} 248953642Sguido 249053642Sguido 2491145522Sdarrenr/* ------------------------------------------------------------------------ */ 2492255332Scy/* Function: ipf_nat_clearlist */ 2493145522Sdarrenr/* Returns: int - number of NAT/RDR rules deleted */ 2494255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 2495255332Scy/* softn(I) - pointer to NAT context structure */ 2496145522Sdarrenr/* */ 2497145522Sdarrenr/* Delete all rules in the current list of rules. There is nothing elegant */ 2498145522Sdarrenr/* about this cleanup: simply free all entries on the list of rules and */ 2499145522Sdarrenr/* clear out the tables used for hashed NAT rule lookups. */ 2500145522Sdarrenr/* ------------------------------------------------------------------------ */ 2501255332Scystatic int 2502255332Scyipf_nat_clearlist(softc, softn) 2503255332Scy ipf_main_softc_t *softc; 2504255332Scy ipf_nat_softc_t *softn; 250553642Sguido{ 2506255332Scy ipnat_t *n; 250753642Sguido int i = 0; 250853642Sguido 2509255332Scy if (softn->ipf_nat_map_rules != NULL) { 2510255332Scy bzero((char *)softn->ipf_nat_map_rules, 2511255332Scy sizeof(*softn->ipf_nat_map_rules) * 2512255332Scy softn->ipf_nat_maprules_sz); 2513255332Scy } 2514255332Scy if (softn->ipf_nat_rdr_rules != NULL) { 2515255332Scy bzero((char *)softn->ipf_nat_rdr_rules, 2516255332Scy sizeof(*softn->ipf_nat_rdr_rules) * 2517255332Scy softn->ipf_nat_rdrrules_sz); 2518255332Scy } 251953642Sguido 2520255332Scy while ((n = softn->ipf_nat_list) != NULL) { 2521255332Scy ipf_nat_delrule(softc, softn, n, 0); 252253642Sguido i++; 252353642Sguido } 2524255332Scy#if SOLARIS && !defined(INSTANCES) 2525145522Sdarrenr pfil_delayed_copy = 1; 2526145522Sdarrenr#endif 252753642Sguido return i; 252853642Sguido} 252953642Sguido 253053642Sguido 2531145522Sdarrenr/* ------------------------------------------------------------------------ */ 2532255332Scy/* Function: ipf_nat_delrule */ 2533255332Scy/* Returns: Nil */ 2534255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 2535255332Scy/* softn(I) - pointer to NAT context structure */ 2536255332Scy/* np(I) - pointer to NAT rule to delete */ 2537255332Scy/* purge(I) - 1 == allow purge, 0 == prevent purge */ 2538255332Scy/* Locks: WRITE(ipf_nat) */ 2539255332Scy/* */ 2540255332Scy/* Preventing "purge" from occuring is allowed because when all of the NAT */ 2541255332Scy/* rules are being removed, allowing the "purge" to walk through the list */ 2542255332Scy/* of NAT sessions, possibly multiple times, would be a large performance */ 2543255332Scy/* hit, on the order of O(N^2). */ 2544255332Scy/* ------------------------------------------------------------------------ */ 2545255332Scystatic void 2546255332Scyipf_nat_delrule(softc, softn, np, purge) 2547255332Scy ipf_main_softc_t *softc; 2548255332Scy ipf_nat_softc_t *softn; 2549255332Scy ipnat_t *np; 2550255332Scy int purge; 2551255332Scy{ 2552255332Scy 2553255332Scy if (np->in_pnext != NULL) { 2554255332Scy *np->in_pnext = np->in_next; 2555255332Scy if (np->in_next != NULL) 2556255332Scy np->in_next->in_pnext = np->in_pnext; 2557255332Scy if (softn->ipf_nat_list_tail == &np->in_next) 2558255332Scy softn->ipf_nat_list_tail = np->in_pnext; 2559255332Scy } 2560255332Scy 2561255332Scy if ((purge == 1) && ((np->in_flags & IPN_PURGE) != 0)) { 2562255332Scy nat_t *next; 2563255332Scy nat_t *nat; 2564255332Scy 2565255332Scy for (next = softn->ipf_nat_instances; (nat = next) != NULL;) { 2566255332Scy next = nat->nat_next; 2567255332Scy if (nat->nat_ptr == np) 2568255332Scy ipf_nat_delete(softc, nat, NL_PURGE); 2569255332Scy } 2570255332Scy } 2571255332Scy 2572255332Scy if ((np->in_flags & IPN_DELETE) == 0) { 2573255332Scy if (np->in_redir & NAT_REDIRECT) { 2574255332Scy switch (np->in_v[0]) 2575255332Scy { 2576255332Scy case 4 : 2577255332Scy ipf_nat_delrdr(softn, np); 2578255332Scy break; 2579255332Scy#ifdef USE_INET6 2580255332Scy case 6 : 2581255332Scy ipf_nat6_delrdr(softn, np); 2582255332Scy break; 2583255332Scy#endif 2584255332Scy } 2585255332Scy } 2586255332Scy if (np->in_redir & (NAT_MAPBLK|NAT_MAP)) { 2587255332Scy switch (np->in_v[0]) 2588255332Scy { 2589255332Scy case 4 : 2590255332Scy ipf_nat_delmap(softn, np); 2591255332Scy break; 2592255332Scy#ifdef USE_INET6 2593255332Scy case 6 : 2594255332Scy ipf_nat6_delmap(softn, np); 2595255332Scy break; 2596255332Scy#endif 2597255332Scy } 2598255332Scy } 2599255332Scy } 2600255332Scy 2601255332Scy np->in_flags |= IPN_DELETE; 2602255332Scy ipf_nat_rule_deref(softc, &np); 2603255332Scy} 2604255332Scy 2605255332Scy 2606255332Scy/* ------------------------------------------------------------------------ */ 2607255332Scy/* Function: ipf_nat_newmap */ 2608145522Sdarrenr/* Returns: int - -1 == error, 0 == success */ 2609145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2610145522Sdarrenr/* nat(I) - pointer to NAT entry */ 2611145522Sdarrenr/* ni(I) - pointer to structure with misc. information needed */ 2612145522Sdarrenr/* to create new NAT entry. */ 2613145522Sdarrenr/* */ 2614145522Sdarrenr/* Given an empty NAT structure, populate it with new information about a */ 2615145522Sdarrenr/* new NAT session, as defined by the matching NAT rule. */ 2616145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 2617145522Sdarrenr/* to the new IP address for the translation. */ 2618145522Sdarrenr/* ------------------------------------------------------------------------ */ 2619255332Scystatic int 2620255332Scyipf_nat_newmap(fin, nat, ni) 2621255332Scy fr_info_t *fin; 2622255332Scy nat_t *nat; 2623255332Scy natinfo_t *ni; 2624145522Sdarrenr{ 2625255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 2626255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 2627145522Sdarrenr u_short st_port, dport, sport, port, sp, dp; 2628145522Sdarrenr struct in_addr in, inb; 2629145522Sdarrenr hostmap_t *hm; 2630145522Sdarrenr u_32_t flags; 2631145522Sdarrenr u_32_t st_ip; 2632145522Sdarrenr ipnat_t *np; 2633145522Sdarrenr nat_t *natl; 2634145522Sdarrenr int l; 2635145522Sdarrenr 2636145522Sdarrenr /* 2637145522Sdarrenr * If it's an outbound packet which doesn't match any existing 2638145522Sdarrenr * record, then create a new port 2639145522Sdarrenr */ 2640145522Sdarrenr l = 0; 2641145522Sdarrenr hm = NULL; 2642145522Sdarrenr np = ni->nai_np; 2643255332Scy st_ip = np->in_snip; 2644255332Scy st_port = np->in_spnext; 2645255332Scy flags = nat->nat_flags; 2646145522Sdarrenr 2647255332Scy if (flags & IPN_ICMPQUERY) { 2648255332Scy sport = fin->fin_data[1]; 2649255332Scy dport = 0; 2650255332Scy } else { 2651255332Scy sport = htons(fin->fin_data[0]); 2652255332Scy dport = htons(fin->fin_data[1]); 2653255332Scy } 2654255332Scy 2655145522Sdarrenr /* 2656145522Sdarrenr * Do a loop until we either run out of entries to try or we find 2657145522Sdarrenr * a NAT mapping that isn't currently being used. This is done 2658145522Sdarrenr * because the change to the source is not (usually) being fixed. 2659145522Sdarrenr */ 2660145522Sdarrenr do { 2661145522Sdarrenr port = 0; 2662255332Scy in.s_addr = htonl(np->in_snip); 2663145522Sdarrenr if (l == 0) { 2664145522Sdarrenr /* 2665145522Sdarrenr * Check to see if there is an existing NAT 2666145522Sdarrenr * setup for this IP address pair. 2667145522Sdarrenr */ 2668255332Scy hm = ipf_nat_hostmap(softn, np, fin->fin_src, 2669255332Scy fin->fin_dst, in, 0); 2670145522Sdarrenr if (hm != NULL) 2671255332Scy in.s_addr = hm->hm_nsrcip.s_addr; 2672145522Sdarrenr } else if ((l == 1) && (hm != NULL)) { 2673255332Scy ipf_nat_hostmapdel(softc, &hm); 2674145522Sdarrenr } 2675145522Sdarrenr in.s_addr = ntohl(in.s_addr); 2676145522Sdarrenr 2677145522Sdarrenr nat->nat_hm = hm; 2678145522Sdarrenr 2679255332Scy if ((np->in_nsrcmsk == 0xffffffff) && (np->in_spnext == 0)) { 2680255332Scy if (l > 0) { 2681255332Scy NBUMPSIDEX(1, ns_exhausted, ns_exhausted_1); 2682338170Scy DT4(ns_exhausted_1, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); 2683145522Sdarrenr return -1; 2684255332Scy } 2685145522Sdarrenr } 2686145522Sdarrenr 2687145522Sdarrenr if (np->in_redir == NAT_BIMAP && 2688255332Scy np->in_osrcmsk == np->in_nsrcmsk) { 2689145522Sdarrenr /* 2690145522Sdarrenr * map the address block in a 1:1 fashion 2691145522Sdarrenr */ 2692255332Scy in.s_addr = np->in_nsrcaddr; 2693255332Scy in.s_addr |= fin->fin_saddr & ~np->in_osrcmsk; 2694145522Sdarrenr in.s_addr = ntohl(in.s_addr); 2695145522Sdarrenr 2696145522Sdarrenr } else if (np->in_redir & NAT_MAPBLK) { 2697145522Sdarrenr if ((l >= np->in_ppip) || ((l > 0) && 2698255332Scy !(flags & IPN_TCPUDP))) { 2699255332Scy NBUMPSIDEX(1, ns_exhausted, ns_exhausted_2); 2700338170Scy DT4(ns_exhausted_2, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); 2701145522Sdarrenr return -1; 2702255332Scy } 2703145522Sdarrenr /* 2704145522Sdarrenr * map-block - Calculate destination address. 2705145522Sdarrenr */ 2706145522Sdarrenr in.s_addr = ntohl(fin->fin_saddr); 2707255332Scy in.s_addr &= ntohl(~np->in_osrcmsk); 2708145522Sdarrenr inb.s_addr = in.s_addr; 2709145522Sdarrenr in.s_addr /= np->in_ippip; 2710255332Scy in.s_addr &= ntohl(~np->in_nsrcmsk); 2711255332Scy in.s_addr += ntohl(np->in_nsrcaddr); 2712145522Sdarrenr /* 2713145522Sdarrenr * Calculate destination port. 2714145522Sdarrenr */ 2715145522Sdarrenr if ((flags & IPN_TCPUDP) && 2716145522Sdarrenr (np->in_ppip != 0)) { 2717145522Sdarrenr port = ntohs(sport) + l; 2718145522Sdarrenr port %= np->in_ppip; 2719145522Sdarrenr port += np->in_ppip * 2720145522Sdarrenr (inb.s_addr % np->in_ippip); 2721145522Sdarrenr port += MAPBLK_MINPORT; 2722145522Sdarrenr port = htons(port); 2723145522Sdarrenr } 2724145522Sdarrenr 2725255332Scy } else if ((np->in_nsrcaddr == 0) && 2726255332Scy (np->in_nsrcmsk == 0xffffffff)) { 2727255332Scy i6addr_t in6; 2728255332Scy 2729145522Sdarrenr /* 2730145522Sdarrenr * 0/32 - use the interface's IP address. 2731145522Sdarrenr */ 2732145522Sdarrenr if ((l > 0) || 2733255332Scy ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp, 2734255332Scy &in6, NULL) == -1) { 2735255332Scy NBUMPSIDEX(1, ns_new_ifpaddr, ns_new_ifpaddr_1); 2736338170Scy DT4(ns_new_ifpaddr_1, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); 2737145522Sdarrenr return -1; 2738255332Scy } 2739255332Scy in.s_addr = ntohl(in6.in4.s_addr); 2740145522Sdarrenr 2741255332Scy } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) { 2742145522Sdarrenr /* 2743145522Sdarrenr * 0/0 - use the original source address/port. 2744145522Sdarrenr */ 2745255332Scy if (l > 0) { 2746255332Scy NBUMPSIDEX(1, ns_exhausted, ns_exhausted_3); 2747338170Scy DT4(ns_exhausted_3, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); 2748145522Sdarrenr return -1; 2749255332Scy } 2750145522Sdarrenr in.s_addr = ntohl(fin->fin_saddr); 2751145522Sdarrenr 2752255332Scy } else if ((np->in_nsrcmsk != 0xffffffff) && 2753255332Scy (np->in_spnext == 0) && ((l > 0) || (hm == NULL))) 2754255332Scy np->in_snip++; 2755145522Sdarrenr 2756145522Sdarrenr natl = NULL; 2757145522Sdarrenr 2758145522Sdarrenr if ((flags & IPN_TCPUDP) && 2759145522Sdarrenr ((np->in_redir & NAT_MAPBLK) == 0) && 2760145522Sdarrenr (np->in_flags & IPN_AUTOPORTMAP)) { 2761145522Sdarrenr /* 2762145522Sdarrenr * "ports auto" (without map-block) 2763145522Sdarrenr */ 2764145522Sdarrenr if ((l > 0) && (l % np->in_ppip == 0)) { 2765255332Scy if ((l > np->in_ppip) && 2766255332Scy np->in_nsrcmsk != 0xffffffff) 2767255332Scy np->in_snip++; 2768145522Sdarrenr } 2769145522Sdarrenr if (np->in_ppip != 0) { 2770145522Sdarrenr port = ntohs(sport); 2771145522Sdarrenr port += (l % np->in_ppip); 2772145522Sdarrenr port %= np->in_ppip; 2773145522Sdarrenr port += np->in_ppip * 2774145522Sdarrenr (ntohl(fin->fin_saddr) % 2775145522Sdarrenr np->in_ippip); 2776145522Sdarrenr port += MAPBLK_MINPORT; 2777145522Sdarrenr port = htons(port); 2778145522Sdarrenr } 2779145522Sdarrenr 2780145522Sdarrenr } else if (((np->in_redir & NAT_MAPBLK) == 0) && 2781255332Scy (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) { 2782145522Sdarrenr /* 2783145522Sdarrenr * Standard port translation. Select next port. 2784145522Sdarrenr */ 2785180778Sdarrenr if (np->in_flags & IPN_SEQUENTIAL) { 2786255332Scy port = np->in_spnext; 2787180778Sdarrenr } else { 2788255332Scy port = ipf_random() % (np->in_spmax - 2789255332Scy np->in_spmin + 1); 2790255332Scy port += np->in_spmin; 2791180778Sdarrenr } 2792180832Sdarrenr port = htons(port); 2793255332Scy np->in_spnext++; 2794145522Sdarrenr 2795255332Scy if (np->in_spnext > np->in_spmax) { 2796255332Scy np->in_spnext = np->in_spmin; 2797255332Scy if (np->in_nsrcmsk != 0xffffffff) 2798255332Scy np->in_snip++; 2799145522Sdarrenr } 2800145522Sdarrenr } 2801145522Sdarrenr 2802255332Scy if (np->in_flags & IPN_SIPRANGE) { 2803255332Scy if (np->in_snip > ntohl(np->in_nsrcmsk)) 2804255332Scy np->in_snip = ntohl(np->in_nsrcaddr); 2805145522Sdarrenr } else { 2806255332Scy if ((np->in_nsrcmsk != 0xffffffff) && 2807255332Scy ((np->in_snip + 1) & ntohl(np->in_nsrcmsk)) > 2808255332Scy ntohl(np->in_nsrcaddr)) 2809255332Scy np->in_snip = ntohl(np->in_nsrcaddr) + 1; 2810145522Sdarrenr } 2811145522Sdarrenr 2812145522Sdarrenr if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) 2813145522Sdarrenr port = sport; 2814145522Sdarrenr 2815145522Sdarrenr /* 2816145522Sdarrenr * Here we do a lookup of the connection as seen from 2817145522Sdarrenr * the outside. If an IP# pair already exists, try 2818145522Sdarrenr * again. So if you have A->B becomes C->B, you can 2819145522Sdarrenr * also have D->E become C->E but not D->B causing 2820145522Sdarrenr * another C->B. Also take protocol and ports into 2821145522Sdarrenr * account when determining whether a pre-existing 2822145522Sdarrenr * NAT setup will cause an external conflict where 2823145522Sdarrenr * this is appropriate. 2824145522Sdarrenr */ 2825145522Sdarrenr inb.s_addr = htonl(in.s_addr); 2826145522Sdarrenr sp = fin->fin_data[0]; 2827145522Sdarrenr dp = fin->fin_data[1]; 2828145522Sdarrenr fin->fin_data[0] = fin->fin_data[1]; 2829255332Scy fin->fin_data[1] = ntohs(port); 2830255332Scy natl = ipf_nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 2831255332Scy (u_int)fin->fin_p, fin->fin_dst, inb); 2832145522Sdarrenr fin->fin_data[0] = sp; 2833145522Sdarrenr fin->fin_data[1] = dp; 2834145522Sdarrenr 2835145522Sdarrenr /* 2836145522Sdarrenr * Has the search wrapped around and come back to the 2837145522Sdarrenr * start ? 2838145522Sdarrenr */ 2839145522Sdarrenr if ((natl != NULL) && 2840255332Scy (np->in_spnext != 0) && (st_port == np->in_spnext) && 2841255332Scy (np->in_snip != 0) && (st_ip == np->in_snip)) { 2842255332Scy NBUMPSIDED(1, ns_wrap); 2843338170Scy DT4(ns_wrap, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); 2844145522Sdarrenr return -1; 2845255332Scy } 2846145522Sdarrenr l++; 2847145522Sdarrenr } while (natl != NULL); 2848145522Sdarrenr 2849145522Sdarrenr /* Setup the NAT table */ 2850255332Scy nat->nat_osrcip = fin->fin_src; 2851255332Scy nat->nat_nsrcaddr = htonl(in.s_addr); 2852255332Scy nat->nat_odstip = fin->fin_dst; 2853255332Scy nat->nat_ndstip = fin->fin_dst; 2854145522Sdarrenr if (nat->nat_hm == NULL) 2855255332Scy nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src, 2856255332Scy fin->fin_dst, nat->nat_nsrcip, 2857255332Scy 0); 2858145522Sdarrenr 2859145522Sdarrenr if (flags & IPN_TCPUDP) { 2860255332Scy nat->nat_osport = sport; 2861255332Scy nat->nat_nsport = port; /* sport */ 2862255332Scy nat->nat_odport = dport; 2863255332Scy nat->nat_ndport = dport; 2864145522Sdarrenr ((tcphdr_t *)fin->fin_dp)->th_sport = port; 2865145522Sdarrenr } else if (flags & IPN_ICMPQUERY) { 2866255332Scy nat->nat_oicmpid = fin->fin_data[1]; 2867145522Sdarrenr ((icmphdr_t *)fin->fin_dp)->icmp_id = port; 2868255332Scy nat->nat_nicmpid = port; 2869145522Sdarrenr } 2870145522Sdarrenr return 0; 2871145522Sdarrenr} 2872145522Sdarrenr 2873145522Sdarrenr 2874145522Sdarrenr/* ------------------------------------------------------------------------ */ 2875255332Scy/* Function: ipf_nat_newrdr */ 2876145522Sdarrenr/* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ 2877145522Sdarrenr/* allow rule to be moved if IPN_ROUNDR is set. */ 2878145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2879145522Sdarrenr/* nat(I) - pointer to NAT entry */ 2880145522Sdarrenr/* ni(I) - pointer to structure with misc. information needed */ 2881145522Sdarrenr/* to create new NAT entry. */ 2882145522Sdarrenr/* */ 2883145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 2884145522Sdarrenr/* to the new IP address for the translation. */ 2885145522Sdarrenr/* ------------------------------------------------------------------------ */ 2886255332Scystatic int 2887255332Scyipf_nat_newrdr(fin, nat, ni) 2888255332Scy fr_info_t *fin; 2889255332Scy nat_t *nat; 2890255332Scy natinfo_t *ni; 2891145522Sdarrenr{ 2892255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 2893255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 2894145522Sdarrenr u_short nport, dport, sport; 2895170268Sdarrenr struct in_addr in, inb; 2896170268Sdarrenr u_short sp, dp; 2897145522Sdarrenr hostmap_t *hm; 2898145522Sdarrenr u_32_t flags; 2899145522Sdarrenr ipnat_t *np; 2900170268Sdarrenr nat_t *natl; 2901145522Sdarrenr int move; 2902145522Sdarrenr 2903145522Sdarrenr move = 1; 2904145522Sdarrenr hm = NULL; 2905145522Sdarrenr in.s_addr = 0; 2906145522Sdarrenr np = ni->nai_np; 2907255332Scy flags = nat->nat_flags; 2908145522Sdarrenr 2909255332Scy if (flags & IPN_ICMPQUERY) { 2910255332Scy dport = fin->fin_data[1]; 2911255332Scy sport = 0; 2912255332Scy } else { 2913255332Scy sport = htons(fin->fin_data[0]); 2914255332Scy dport = htons(fin->fin_data[1]); 2915255332Scy } 2916255332Scy 2917255332Scy /* TRACE sport, dport */ 2918255332Scy 2919255332Scy 2920145522Sdarrenr /* 2921145522Sdarrenr * If the matching rule has IPN_STICKY set, then we want to have the 2922145522Sdarrenr * same rule kick in as before. Why would this happen? If you have 2923145522Sdarrenr * a collection of rdr rules with "round-robin sticky", the current 2924145522Sdarrenr * packet might match a different one to the previous connection but 2925145522Sdarrenr * we want the same destination to be used. 2926145522Sdarrenr */ 2927153876Sguido if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) && 2928153876Sguido ((np->in_flags & IPN_STICKY) != 0)) { 2929255332Scy hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, fin->fin_dst, 2930255332Scy in, (u_32_t)dport); 2931145522Sdarrenr if (hm != NULL) { 2932255332Scy in.s_addr = ntohl(hm->hm_ndstip.s_addr); 2933145522Sdarrenr np = hm->hm_ipnat; 2934145522Sdarrenr ni->nai_np = np; 2935145522Sdarrenr move = 0; 2936255332Scy ipf_nat_hostmapdel(softc, &hm); 2937145522Sdarrenr } 2938145522Sdarrenr } 2939145522Sdarrenr 2940145522Sdarrenr /* 2941145522Sdarrenr * Otherwise, it's an inbound packet. Most likely, we don't 2942145522Sdarrenr * want to rewrite source ports and source addresses. Instead, 2943145522Sdarrenr * we want to rewrite to a fixed internal address and fixed 2944145522Sdarrenr * internal port. 2945145522Sdarrenr */ 2946145522Sdarrenr if (np->in_flags & IPN_SPLIT) { 2947255332Scy in.s_addr = np->in_dnip; 2948272998Scy inb.s_addr = htonl(in.s_addr); 2949145522Sdarrenr 2950145522Sdarrenr if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { 2951255332Scy hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, 2952272998Scy fin->fin_dst, inb, (u_32_t)dport); 2953145522Sdarrenr if (hm != NULL) { 2954255332Scy in.s_addr = hm->hm_ndstip.s_addr; 2955145522Sdarrenr move = 0; 2956145522Sdarrenr } 2957145522Sdarrenr } 2958145522Sdarrenr 2959145522Sdarrenr if (hm == NULL || hm->hm_ref == 1) { 2960255332Scy if (np->in_ndstaddr == htonl(in.s_addr)) { 2961255332Scy np->in_dnip = ntohl(np->in_ndstmsk); 2962145522Sdarrenr move = 0; 2963145522Sdarrenr } else { 2964255332Scy np->in_dnip = ntohl(np->in_ndstaddr); 2965145522Sdarrenr } 2966145522Sdarrenr } 2967255332Scy if (hm != NULL) 2968255332Scy ipf_nat_hostmapdel(softc, &hm); 2969145522Sdarrenr 2970255332Scy } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) { 2971255332Scy i6addr_t in6; 2972255332Scy 2973145522Sdarrenr /* 2974145522Sdarrenr * 0/32 - use the interface's IP address. 2975145522Sdarrenr */ 2976255332Scy if (ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp, 2977255332Scy &in6, NULL) == -1) { 2978255332Scy NBUMPSIDEX(0, ns_new_ifpaddr, ns_new_ifpaddr_2); 2979338170Scy DT3(ns_new_ifpaddr_2, fr_info_t *, fin, nat_t *, nat, natinfo_t, ni); 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); 3126338170Scy DT2(ns_table_max, nat_stat_t *, nsp, ipf_nat_softc_t *, softn); 3127130886Sdarrenr return NULL; 3128130886Sdarrenr } 3129130886Sdarrenr 3130145522Sdarrenr move = 1; 3131145522Sdarrenr nflags = np->in_flags & flags; 3132145522Sdarrenr nflags &= NAT_FROMRULE; 313353642Sguido 3134145522Sdarrenr ni.nai_np = np; 3135170268Sdarrenr ni.nai_dport = 0; 3136170268Sdarrenr ni.nai_sport = 0; 3137145522Sdarrenr 313853642Sguido /* Give me a new nat */ 313953642Sguido KMALLOC(nat, nat_t *); 314060852Sdarrenr if (nat == NULL) { 3141338170Scy DT(ns_memfail); 3142255332Scy NBUMPSIDED(fin->fin_out, ns_memfail); 3143130886Sdarrenr /* 3144130886Sdarrenr * Try to automatically tune the max # of entries in the 3145130886Sdarrenr * table allowed to be less than what will cause kmem_alloc() 3146130886Sdarrenr * to fail and try to eliminate panics due to out of memory 3147130886Sdarrenr * conditions arising. 3148130886Sdarrenr */ 3149255332Scy if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) && 3150255332Scy (nsp->ns_active > 100)) { 3151255332Scy softn->ipf_nat_table_max = nsp->ns_active - 100; 3152255332Scy printf("table_max reduced to %d\n", 3153255332Scy softn->ipf_nat_table_max); 3154130886Sdarrenr } 315553642Sguido return NULL; 315660852Sdarrenr } 315753642Sguido 3158255332Scy if (flags & IPN_ICMPQUERY) { 3159145522Sdarrenr /* 3160145522Sdarrenr * In the ICMP query NAT code, we translate the ICMP id fields 3161145522Sdarrenr * to make them unique. This is indepedent of the ICMP type 3162145522Sdarrenr * (e.g. in the unlikely event that a host sends an echo and 3163145522Sdarrenr * an tstamp request with the same id, both packets will have 3164145522Sdarrenr * their ip address/id field changed in the same way). 3165145522Sdarrenr */ 3166145522Sdarrenr /* The icmp_id field is used by the sender to identify the 3167145522Sdarrenr * process making the icmp request. (the receiver justs 3168145522Sdarrenr * copies it back in its response). So, it closely matches 3169145522Sdarrenr * the concept of source port. We overlay sport, so we can 3170145522Sdarrenr * maximally reuse the existing code. 3171145522Sdarrenr */ 3172255332Scy ni.nai_sport = fin->fin_data[1]; 3173255332Scy ni.nai_dport = 0; 3174145522Sdarrenr } 3175145522Sdarrenr 317653642Sguido bzero((char *)nat, sizeof(*nat)); 317753642Sguido nat->nat_flags = flags; 3178170268Sdarrenr nat->nat_redir = np->in_redir; 3179255332Scy nat->nat_dir = direction; 3180255332Scy nat->nat_pr[0] = fin->fin_p; 3181255332Scy nat->nat_pr[1] = fin->fin_p; 3182145522Sdarrenr 318353642Sguido /* 3184255332Scy * Search the current table for a match and create a new mapping 3185255332Scy * if there is none found. 318653642Sguido */ 3187255332Scy if (np->in_redir & NAT_DIVERTUDP) { 3188255332Scy move = ipf_nat_newdivert(fin, nat, &ni); 3189255332Scy 3190255332Scy } else if (np->in_redir & NAT_REWRITE) { 3191255332Scy move = ipf_nat_newrewrite(fin, nat, &ni); 3192255332Scy 3193255332Scy } else if (direction == NAT_OUTBOUND) { 319453642Sguido /* 3195145522Sdarrenr * We can now arrange to call this for the same connection 3196145522Sdarrenr * because ipf_nat_new doesn't protect the code path into 3197145522Sdarrenr * this function. 319853642Sguido */ 3199255332Scy natl = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p, 3200145522Sdarrenr fin->fin_src, fin->fin_dst); 3201145522Sdarrenr if (natl != NULL) { 3202161356Sguido KFREE(nat); 3203145522Sdarrenr nat = natl; 3204145522Sdarrenr goto done; 3205145522Sdarrenr } 320653642Sguido 3207255332Scy move = ipf_nat_newmap(fin, nat, &ni); 320853642Sguido } else { 320953642Sguido /* 3210255332Scy * NAT_INBOUND is used for redirects rules 321153642Sguido */ 3212255332Scy natl = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p, 3213255332Scy fin->fin_src, fin->fin_dst); 3214145522Sdarrenr if (natl != NULL) { 3215161356Sguido KFREE(nat); 3216145522Sdarrenr nat = natl; 3217145522Sdarrenr goto done; 321860852Sdarrenr } 321953642Sguido 3220255332Scy move = ipf_nat_newrdr(fin, nat, &ni); 3221145522Sdarrenr } 3222255332Scy if (move == -1) 3223255332Scy goto badnat; 322453642Sguido 3225255332Scy np = ni.nai_np; 3226255332Scy 3227255332Scy nat->nat_mssclamp = np->in_mssclamp; 3228255332Scy nat->nat_me = natsave; 3229255332Scy nat->nat_fr = fin->fin_fr; 3230255332Scy nat->nat_rev = fin->fin_rev; 3231255332Scy nat->nat_ptr = np; 3232255332Scy nat->nat_dlocal = np->in_dlocal; 3233255332Scy 3234255332Scy if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) { 3235255332Scy if (ipf_proxy_new(fin, nat) == -1) { 3236255332Scy NBUMPSIDED(fin->fin_out, ns_appr_fail); 3237338170Scy DT3(ns_appr_fail, fr_info_t *, fin, nat_t *, nat, ipnat_t *, np); 3238255332Scy goto badnat; 323953642Sguido } 324053642Sguido } 324153642Sguido 3242255332Scy nat->nat_ifps[0] = np->in_ifps[0]; 3243255332Scy if (np->in_ifps[0] != NULL) { 3244255332Scy COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]); 3245145522Sdarrenr } 3246145522Sdarrenr 3247255332Scy nat->nat_ifps[1] = np->in_ifps[1]; 3248255332Scy if (np->in_ifps[1] != NULL) { 3249255332Scy COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]); 3250255332Scy } 325153642Sguido 3252255332Scy if (ipf_nat_finalise(fin, nat) == -1) { 3253255332Scy goto badnat; 3254255332Scy } 325553642Sguido 3256255332Scy np->in_use++; 325753642Sguido 3258255332Scy if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { 3259255332Scy if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) { 3260255332Scy ipf_nat_delrdr(softn, np); 3261255332Scy ipf_nat_addrdr(softn, np); 3262255332Scy } else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) { 3263255332Scy ipf_nat_delmap(softn, np); 3264255332Scy ipf_nat_addmap(softn, np); 3265145522Sdarrenr } 3266145522Sdarrenr } 326753642Sguido 3268145522Sdarrenr if (flags & SI_WILDP) 3269255332Scy nsp->ns_wilds++; 3270255332Scy nsp->ns_proto[nat->nat_pr[0]]++; 3271255332Scy 3272145522Sdarrenr goto done; 3273145522Sdarrenrbadnat: 3274338169Scy DT3(ns_badnatnew, fr_info_t *, fin, nat_t *, nat, ipnat_t *, np); 3275255332Scy NBUMPSIDE(fin->fin_out, ns_badnatnew); 3276145522Sdarrenr if ((hm = nat->nat_hm) != NULL) 3277255332Scy ipf_nat_hostmapdel(softc, &hm); 3278145522Sdarrenr KFREE(nat); 3279145522Sdarrenr nat = NULL; 3280145522Sdarrenrdone: 3281255332Scy if (nat != NULL && np != NULL) 3282255332Scy np->in_hits++; 3283255332Scy if (natsave != NULL) 3284255332Scy *natsave = nat; 3285145522Sdarrenr return nat; 3286145522Sdarrenr} 328760852Sdarrenr 328860852Sdarrenr 3289145522Sdarrenr/* ------------------------------------------------------------------------ */ 3290255332Scy/* Function: ipf_nat_finalise */ 3291145522Sdarrenr/* Returns: int - 0 == sucess, -1 == failure */ 3292145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3293145522Sdarrenr/* nat(I) - pointer to NAT entry */ 3294145522Sdarrenr/* Write Lock: ipf_nat */ 3295145522Sdarrenr/* */ 3296145522Sdarrenr/* This is the tail end of constructing a new NAT entry and is the same */ 3297145522Sdarrenr/* for both IPv4 and IPv6. */ 3298145522Sdarrenr/* ------------------------------------------------------------------------ */ 3299145522Sdarrenr/*ARGSUSED*/ 3300255332Scystatic int 3301255332Scyipf_nat_finalise(fin, nat) 3302255332Scy fr_info_t *fin; 3303255332Scy nat_t *nat; 3304145522Sdarrenr{ 3305255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3306255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 3307255332Scy u_32_t sum1, sum2, sumd; 3308145522Sdarrenr frentry_t *fr; 3309255332Scy u_32_t flags; 3310255332Scy#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC) 3311255332Scy qpktinfo_t *qpi = fin->fin_qpi; 3312255332Scy#endif 3313145522Sdarrenr 3314255332Scy flags = nat->nat_flags; 3315145522Sdarrenr 3316255332Scy switch (nat->nat_pr[0]) 3317255332Scy { 3318255332Scy case IPPROTO_ICMP : 3319255332Scy sum1 = LONG_SUM(ntohs(nat->nat_oicmpid)); 3320255332Scy sum2 = LONG_SUM(ntohs(nat->nat_nicmpid)); 3321255332Scy CALC_SUMD(sum1, sum2, sumd); 3322255332Scy nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 3323255332Scy 3324255332Scy break; 3325255332Scy 3326255332Scy default : 3327255332Scy sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr) + \ 3328255332Scy ntohs(nat->nat_osport)); 3329255332Scy sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr) + \ 3330255332Scy ntohs(nat->nat_nsport)); 3331255332Scy CALC_SUMD(sum1, sum2, sumd); 3332255332Scy nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 3333255332Scy 3334255332Scy sum1 = LONG_SUM(ntohl(nat->nat_odstaddr) + \ 3335255332Scy ntohs(nat->nat_odport)); 3336255332Scy sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr) + \ 3337255332Scy ntohs(nat->nat_ndport)); 3338255332Scy CALC_SUMD(sum1, sum2, sumd); 3339255332Scy nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); 3340255332Scy break; 3341161356Sguido } 3342255332Scy 3343255332Scy /* 3344255332Scy * Compute the partial checksum, just in case. 3345255332Scy * This is only ever placed into outbound packets so care needs 3346255332Scy * to be taken over which pair of addresses are used. 3347255332Scy */ 3348255332Scy if (nat->nat_dir == NAT_OUTBOUND) { 3349255332Scy sum1 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); 3350255332Scy sum1 += LONG_SUM(ntohl(nat->nat_ndstaddr)); 3351255332Scy } else { 3352255332Scy sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr)); 3353255332Scy sum1 += LONG_SUM(ntohl(nat->nat_odstaddr)); 3354161356Sguido } 3355255332Scy sum1 += nat->nat_pr[1]; 3356255332Scy nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16); 3357145522Sdarrenr 3358255332Scy sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr)); 3359255332Scy sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); 3360255332Scy CALC_SUMD(sum1, sum2, sumd); 3361255332Scy nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); 336292685Sdarrenr 3363255332Scy sum1 = LONG_SUM(ntohl(nat->nat_odstaddr)); 3364255332Scy sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr)); 3365255332Scy CALC_SUMD(sum1, sum2, sumd); 3366255332Scy nat->nat_ipsumd += (sumd & 0xffff) + (sumd >> 16); 336792685Sdarrenr 3368255332Scy nat->nat_v[0] = 4; 3369255332Scy nat->nat_v[1] = 4; 3370255332Scy 3371255332Scy if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { 3372255332Scy nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); 3373255332Scy } 3374255332Scy 3375255332Scy if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { 3376255332Scy nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); 3377255332Scy } 3378255332Scy 3379255332Scy if ((nat->nat_flags & SI_CLONE) == 0) 3380255332Scy nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat); 3381255332Scy 3382255332Scy if (ipf_nat_insert(softc, softn, nat) == 0) { 3383255332Scy if (softn->ipf_nat_logging) 3384255332Scy ipf_nat_log(softc, softn, nat, NL_NEW); 3385255332Scy fr = nat->nat_fr; 3386145522Sdarrenr if (fr != NULL) { 3387145522Sdarrenr MUTEX_ENTER(&fr->fr_lock); 3388145522Sdarrenr fr->fr_ref++; 3389145522Sdarrenr MUTEX_EXIT(&fr->fr_lock); 3390145522Sdarrenr } 3391145522Sdarrenr return 0; 3392145522Sdarrenr } 339392685Sdarrenr 3394255332Scy NBUMPSIDED(fin->fin_out, ns_unfinalised); 3395338170Scy DT2(ns_unfinalised, fr_info_t *, fin, nat_t *, nat); 3396145522Sdarrenr /* 3397145522Sdarrenr * nat_insert failed, so cleanup time... 3398145522Sdarrenr */ 3399255332Scy if (nat->nat_sync != NULL) 3400255332Scy ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); 3401145522Sdarrenr return -1; 340253642Sguido} 340353642Sguido 340453642Sguido 3405145522Sdarrenr/* ------------------------------------------------------------------------ */ 3406255332Scy/* Function: ipf_nat_insert */ 3407255332Scy/* Returns: int - 0 == sucess, -1 == failure */ 3408255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 3409255332Scy/* softn(I) - pointer to NAT context structure */ 3410255332Scy/* nat(I) - pointer to NAT structure */ 3411255332Scy/* Write Lock: ipf_nat */ 3412145522Sdarrenr/* */ 3413145522Sdarrenr/* Insert a NAT entry into the hash tables for searching and add it to the */ 3414145522Sdarrenr/* list of active NAT entries. Adjust global counters when complete. */ 3415145522Sdarrenr/* ------------------------------------------------------------------------ */ 3416255332Scyint 3417255332Scyipf_nat_insert(softc, softn, nat) 3418255332Scy ipf_main_softc_t *softc; 3419255332Scy ipf_nat_softc_t *softn; 3420255332Scy nat_t *nat; 342160852Sdarrenr{ 3422255332Scy u_int hv0, hv1; 3423255332Scy u_int sp, dp; 3424255332Scy ipnat_t *in; 342560852Sdarrenr 3426145522Sdarrenr /* 3427145522Sdarrenr * Try and return an error as early as possible, so calculate the hash 3428145522Sdarrenr * entry numbers first and then proceed. 3429145522Sdarrenr */ 3430145522Sdarrenr if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { 3431255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 3432255332Scy sp = nat->nat_osport; 3433255332Scy dp = nat->nat_odport; 3434255332Scy } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { 3435255332Scy sp = 0; 3436255332Scy dp = nat->nat_oicmpid; 3437255332Scy } else { 3438255332Scy sp = 0; 3439255332Scy dp = 0; 3440255332Scy } 3441255332Scy hv0 = NAT_HASH_FN(nat->nat_osrcaddr, sp, 0xffffffff); 3442255332Scy hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0 + dp, 0xffffffff); 3443255332Scy /* 3444255332Scy * TRACE nat_osrcaddr, nat_osport, nat_odstaddr, 3445255332Scy * nat_odport, hv0 3446255332Scy */ 3447255332Scy 3448255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 3449255332Scy sp = nat->nat_nsport; 3450255332Scy dp = nat->nat_ndport; 3451255332Scy } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { 3452255332Scy sp = 0; 3453255332Scy dp = nat->nat_nicmpid; 3454255332Scy } else { 3455255332Scy sp = 0; 3456255332Scy dp = 0; 3457255332Scy } 3458255332Scy hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, sp, 0xffffffff); 3459255332Scy hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1 + dp, 0xffffffff); 3460255332Scy /* 3461255332Scy * TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, 3462255332Scy * nat_ndport, hv1 3463255332Scy */ 346480482Sdarrenr } else { 3465255332Scy hv0 = NAT_HASH_FN(nat->nat_osrcaddr, 0, 0xffffffff); 3466255332Scy hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0, 0xffffffff); 3467255332Scy /* TRACE nat_osrcaddr, nat_odstaddr, hv0 */ 346880482Sdarrenr 3469255332Scy hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, 0, 0xffffffff); 3470255332Scy hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1, 0xffffffff); 3471255332Scy /* TRACE nat_nsrcaddr, nat_ndstaddr, hv1 */ 3472145522Sdarrenr } 3473145522Sdarrenr 3474255332Scy nat->nat_hv[0] = hv0; 3475255332Scy nat->nat_hv[1] = hv1; 3476145522Sdarrenr 3477145522Sdarrenr MUTEX_INIT(&nat->nat_lock, "nat entry lock"); 3478145522Sdarrenr 3479255332Scy in = nat->nat_ptr; 3480255332Scy nat->nat_ref = nat->nat_me ? 2 : 1; 3481145522Sdarrenr 3482145522Sdarrenr nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; 3483255332Scy nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0], 4); 3484145522Sdarrenr 3485161356Sguido if (nat->nat_ifnames[1][0] != '\0') { 3486145522Sdarrenr nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 3487255332Scy nat->nat_ifps[1] = ipf_resolvenic(softc, 3488255332Scy nat->nat_ifnames[1], 4); 3489255332Scy } else if (in->in_ifnames[1] != -1) { 3490255332Scy char *name; 3491255332Scy 3492255332Scy name = in->in_names + in->in_ifnames[1]; 3493255332Scy if (name[1] != '\0' && name[0] != '-' && name[0] != '*') { 3494255332Scy (void) strncpy(nat->nat_ifnames[1], 3495255332Scy nat->nat_ifnames[0], LIFNAMSIZ); 3496255332Scy nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 3497255332Scy nat->nat_ifps[1] = nat->nat_ifps[0]; 3498255332Scy } 3499145522Sdarrenr } 3500255332Scy if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { 3501255332Scy nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); 3502255332Scy } 3503255332Scy if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { 3504255332Scy nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); 3505255332Scy } 3506145522Sdarrenr 3507255332Scy return ipf_nat_hashtab_add(softc, softn, nat); 3508255332Scy} 3509145522Sdarrenr 3510255332Scy 3511255332Scy/* ------------------------------------------------------------------------ */ 3512255332Scy/* Function: ipf_nat_hashtab_add */ 3513255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 3514255332Scy/* softn(I) - pointer to NAT context structure */ 3515255332Scy/* nat(I) - pointer to NAT structure */ 3516255332Scy/* */ 3517255332Scy/* Handle the insertion of a NAT entry into the table/list. */ 3518255332Scy/* ------------------------------------------------------------------------ */ 3519255332Scyint 3520255332Scyipf_nat_hashtab_add(softc, softn, nat) 3521255332Scy ipf_main_softc_t *softc; 3522255332Scy ipf_nat_softc_t *softn; 3523255332Scy nat_t *nat; 3524255332Scy{ 3525255332Scy nat_t **natp; 3526255332Scy u_int hv0; 3527255332Scy u_int hv1; 3528255332Scy 3529255332Scy hv0 = nat->nat_hv[0] % softn->ipf_nat_table_sz; 3530255332Scy hv1 = nat->nat_hv[1] % softn->ipf_nat_table_sz; 3531255332Scy 3532255332Scy if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) { 3533255332Scy u_int swap; 3534255332Scy 3535255332Scy swap = hv0; 3536255332Scy hv0 = hv1; 3537255332Scy hv1 = swap; 3538255332Scy } 3539255332Scy 3540255332Scy if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0] >= 3541255332Scy softn->ipf_nat_maxbucket) { 3542255332Scy DT1(ns_bucket_max_0, int, 3543255332Scy softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]); 3544255332Scy NBUMPSIDE(0, ns_bucket_max); 3545255332Scy return -1; 3546255332Scy } 3547255332Scy 3548255332Scy if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1] >= 3549255332Scy softn->ipf_nat_maxbucket) { 3550255332Scy DT1(ns_bucket_max_1, int, 3551255332Scy softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]); 3552255332Scy NBUMPSIDE(1, ns_bucket_max); 3553255332Scy return -1; 3554255332Scy } 3555255332Scy 3556255332Scy /* 3557255332Scy * The ordering of operations in the list and hash table insertion 3558255332Scy * is very important. The last operation for each task should be 3559255332Scy * to update the top of the list, after all the "nexts" have been 3560255332Scy * done so that walking the list while it is being done does not 3561255332Scy * find strange pointers. 3562255332Scy * 3563255332Scy * Global list of NAT instances 3564255332Scy */ 3565255332Scy nat->nat_next = softn->ipf_nat_instances; 3566255332Scy nat->nat_pnext = &softn->ipf_nat_instances; 3567255332Scy if (softn->ipf_nat_instances) 3568255332Scy softn->ipf_nat_instances->nat_pnext = &nat->nat_next; 3569255332Scy softn->ipf_nat_instances = nat; 3570255332Scy 3571255332Scy /* 3572255332Scy * Inbound hash table. 3573255332Scy */ 3574255332Scy natp = &softn->ipf_nat_table[0][hv0]; 357567614Sdarrenr nat->nat_phnext[0] = natp; 357660852Sdarrenr nat->nat_hnext[0] = *natp; 3577255332Scy if (*natp) { 3578255332Scy (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 3579255332Scy } else { 3580255332Scy NBUMPSIDE(0, ns_inuse); 3581255332Scy } 358260852Sdarrenr *natp = nat; 3583255332Scy NBUMPSIDE(0, ns_bucketlen[hv0]); 358467614Sdarrenr 3585255332Scy /* 3586255332Scy * Outbound hash table. 3587255332Scy */ 3588255332Scy natp = &softn->ipf_nat_table[1][hv1]; 3589255332Scy nat->nat_phnext[1] = natp; 3590255332Scy nat->nat_hnext[1] = *natp; 359167614Sdarrenr if (*natp) 359267614Sdarrenr (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 3593255332Scy else { 3594255332Scy NBUMPSIDE(1, ns_inuse); 3595255332Scy } 359660852Sdarrenr *natp = nat; 3597255332Scy NBUMPSIDE(1, ns_bucketlen[hv1]); 359860852Sdarrenr 3599255332Scy ipf_nat_setqueue(softc, softn, nat); 3600145522Sdarrenr 3601255332Scy if (nat->nat_dir & NAT_OUTBOUND) { 3602255332Scy NBUMPSIDE(1, ns_added); 3603255332Scy } else { 3604255332Scy NBUMPSIDE(0, ns_added); 3605255332Scy } 3606255332Scy softn->ipf_nat_stats.ns_active++; 3607145522Sdarrenr return 0; 360860852Sdarrenr} 360960852Sdarrenr 361060852Sdarrenr 3611145522Sdarrenr/* ------------------------------------------------------------------------ */ 3612255332Scy/* Function: ipf_nat_icmperrorlookup */ 3613145522Sdarrenr/* Returns: nat_t* - point to matching NAT structure */ 3614145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3615145522Sdarrenr/* dir(I) - direction of packet (in/out) */ 3616145522Sdarrenr/* */ 3617145522Sdarrenr/* Check if the ICMP error message is related to an existing TCP, UDP or */ 3618145522Sdarrenr/* ICMP query nat entry. It is assumed that the packet is already of the */ 3619145522Sdarrenr/* the required length. */ 3620145522Sdarrenr/* ------------------------------------------------------------------------ */ 3621255332Scynat_t * 3622255332Scyipf_nat_icmperrorlookup(fin, dir) 3623255332Scy fr_info_t *fin; 3624255332Scy int dir; 362553642Sguido{ 3626255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3627255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 3628145522Sdarrenr int flags = 0, type, minlen; 3629145522Sdarrenr icmphdr_t *icmp, *orgicmp; 3630255332Scy nat_stat_side_t *nside; 363153642Sguido tcphdr_t *tcp = NULL; 3632145522Sdarrenr u_short data[2]; 3633145522Sdarrenr nat_t *nat; 363453642Sguido ip_t *oip; 3635145522Sdarrenr u_int p; 363653642Sguido 3637145522Sdarrenr icmp = fin->fin_dp; 3638145522Sdarrenr type = icmp->icmp_type; 3639255332Scy nside = &softn->ipf_nat_stats.ns_side[fin->fin_out]; 364053642Sguido /* 364153642Sguido * Does it at least have the return (basic) IP header ? 364253642Sguido * Only a basic IP header (no options) should be with an ICMP error 3643145522Sdarrenr * header. Also, if it's not an error type, then return. 364453642Sguido */ 3645255332Scy if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) { 3646255332Scy ATOMIC_INCL(nside->ns_icmp_basic); 364753642Sguido return NULL; 3648255332Scy } 3649145522Sdarrenr 365053642Sguido /* 3651145522Sdarrenr * Check packet size 365253642Sguido */ 365353642Sguido oip = (ip_t *)((char *)fin->fin_dp + 8); 3654145522Sdarrenr minlen = IP_HL(oip) << 2; 3655145522Sdarrenr if ((minlen < sizeof(ip_t)) || 3656255332Scy (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) { 3657255332Scy ATOMIC_INCL(nside->ns_icmp_size); 365853642Sguido return NULL; 3659255332Scy } 3660255332Scy 366164580Sdarrenr /* 366264580Sdarrenr * Is the buffer big enough for all of it ? It's the size of the IP 366364580Sdarrenr * header claimed in the encapsulated part which is of concern. It 366464580Sdarrenr * may be too big to be in this buffer but not so big that it's 366564580Sdarrenr * outside the ICMP packet, leading to TCP deref's causing problems. 366664580Sdarrenr * This is possible because we don't know how big oip_hl is when we 3667255332Scy * do the pullup early in ipf_check() and thus can't gaurantee it is 366864580Sdarrenr * all here now. 366964580Sdarrenr */ 3670255332Scy#ifdef ipf_nat_KERNEL 367164580Sdarrenr { 367264580Sdarrenr mb_t *m; 367364580Sdarrenr 3674145522Sdarrenr m = fin->fin_m; 3675145522Sdarrenr# if defined(MENTAT) 3676255332Scy if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > 3677255332Scy (char *)m->b_wptr) { 3678255332Scy ATOMIC_INCL(nside->ns_icmp_mbuf); 367964580Sdarrenr return NULL; 3680255332Scy } 368164580Sdarrenr# else 368264580Sdarrenr if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > 3683255332Scy (char *)fin->fin_ip + M_LEN(m)) { 3684255332Scy ATOMIC_INCL(nside->ns_icmp_mbuf); 368564580Sdarrenr return NULL; 3686255332Scy } 368764580Sdarrenr# endif 368864580Sdarrenr } 368964580Sdarrenr#endif 369064580Sdarrenr 3691255332Scy if (fin->fin_daddr != oip->ip_src.s_addr) { 3692255332Scy ATOMIC_INCL(nside->ns_icmp_address); 3693145522Sdarrenr return NULL; 3694255332Scy } 3695145522Sdarrenr 3696145522Sdarrenr p = oip->ip_p; 3697145522Sdarrenr if (p == IPPROTO_TCP) 369853642Sguido flags = IPN_TCP; 3699145522Sdarrenr else if (p == IPPROTO_UDP) 370053642Sguido flags = IPN_UDP; 3701145522Sdarrenr else if (p == IPPROTO_ICMP) { 3702145522Sdarrenr orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 3703145522Sdarrenr 3704145522Sdarrenr /* see if this is related to an ICMP query */ 3705255332Scy if (ipf_nat_icmpquerytype(orgicmp->icmp_type)) { 3706145522Sdarrenr data[0] = fin->fin_data[0]; 3707145522Sdarrenr data[1] = fin->fin_data[1]; 3708145522Sdarrenr fin->fin_data[0] = 0; 3709145522Sdarrenr fin->fin_data[1] = orgicmp->icmp_id; 3710145522Sdarrenr 3711145522Sdarrenr flags = IPN_ICMPERR|IPN_ICMPQUERY; 3712145522Sdarrenr /* 3713145522Sdarrenr * NOTE : dir refers to the direction of the original 3714145522Sdarrenr * ip packet. By definition the icmp error 3715145522Sdarrenr * message flows in the opposite direction. 3716145522Sdarrenr */ 3717145522Sdarrenr if (dir == NAT_INBOUND) 3718255332Scy nat = ipf_nat_inlookup(fin, flags, p, 3719255332Scy oip->ip_dst, 3720255332Scy oip->ip_src); 3721145522Sdarrenr else 3722255332Scy nat = ipf_nat_outlookup(fin, flags, p, 3723255332Scy oip->ip_dst, 3724255332Scy oip->ip_src); 3725145522Sdarrenr fin->fin_data[0] = data[0]; 3726145522Sdarrenr fin->fin_data[1] = data[1]; 3727145522Sdarrenr return nat; 3728145522Sdarrenr } 3729145522Sdarrenr } 3730255332Scy 373153642Sguido if (flags & IPN_TCPUDP) { 373264580Sdarrenr minlen += 8; /* + 64bits of data to get ports */ 3733255332Scy /* TRACE (fin,minlen) */ 3734255332Scy if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) { 3735255332Scy ATOMIC_INCL(nside->ns_icmp_short); 373664580Sdarrenr return NULL; 3737255332Scy } 373892685Sdarrenr 373992685Sdarrenr data[0] = fin->fin_data[0]; 374092685Sdarrenr data[1] = fin->fin_data[1]; 3741145522Sdarrenr tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 374292685Sdarrenr fin->fin_data[0] = ntohs(tcp->th_dport); 374392685Sdarrenr fin->fin_data[1] = ntohs(tcp->th_sport); 374492685Sdarrenr 374592685Sdarrenr if (dir == NAT_INBOUND) { 3746255332Scy nat = ipf_nat_inlookup(fin, flags, p, oip->ip_dst, 3747255332Scy oip->ip_src); 374892685Sdarrenr } else { 3749255332Scy nat = ipf_nat_outlookup(fin, flags, p, oip->ip_dst, 3750145522Sdarrenr oip->ip_src); 375192685Sdarrenr } 375292685Sdarrenr fin->fin_data[0] = data[0]; 375392685Sdarrenr fin->fin_data[1] = data[1]; 375492685Sdarrenr return nat; 375553642Sguido } 375660852Sdarrenr if (dir == NAT_INBOUND) 3757255332Scy nat = ipf_nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src); 375860852Sdarrenr else 3759255332Scy nat = ipf_nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src); 3760255332Scy 3761255332Scy return nat; 376253642Sguido} 376353642Sguido 376453642Sguido 3765145522Sdarrenr/* ------------------------------------------------------------------------ */ 3766255332Scy/* Function: ipf_nat_icmperror */ 3767145522Sdarrenr/* Returns: nat_t* - point to matching NAT structure */ 3768145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3769145522Sdarrenr/* nflags(I) - NAT flags for this packet */ 3770145522Sdarrenr/* dir(I) - direction of packet (in/out) */ 3771145522Sdarrenr/* */ 3772145522Sdarrenr/* Fix up an ICMP packet which is an error message for an existing NAT */ 3773145522Sdarrenr/* session. This will correct both packet header data and checksums. */ 3774145522Sdarrenr/* */ 3775145522Sdarrenr/* This should *ONLY* be used for incoming ICMP error packets to make sure */ 3776145522Sdarrenr/* a NAT'd ICMP packet gets correctly recognised. */ 3777145522Sdarrenr/* ------------------------------------------------------------------------ */ 3778255332Scynat_t * 3779255332Scyipf_nat_icmperror(fin, nflags, dir) 3780255332Scy fr_info_t *fin; 3781255332Scy u_int *nflags; 3782255332Scy int dir; 378353642Sguido{ 3784255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3785255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 3786145522Sdarrenr u_32_t sum1, sum2, sumd, sumd2; 3787255332Scy struct in_addr a1, a2, a3, a4; 3788170268Sdarrenr int flags, dlen, odst; 3789145522Sdarrenr icmphdr_t *icmp; 3790145522Sdarrenr u_short *csump; 379195418Sdarrenr tcphdr_t *tcp; 379253642Sguido nat_t *nat; 379353642Sguido ip_t *oip; 3794145522Sdarrenr void *dp; 379553642Sguido 3796255332Scy if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { 3797255332Scy NBUMPSIDED(fin->fin_out, ns_icmp_short); 379863523Sdarrenr return NULL; 3799255332Scy } 3800255332Scy 380167614Sdarrenr /* 3802255332Scy * ipf_nat_icmperrorlookup() will return NULL for `defective' packets. 380367614Sdarrenr */ 3804255332Scy if ((fin->fin_v != 4) || !(nat = ipf_nat_icmperrorlookup(fin, dir))) { 3805255332Scy NBUMPSIDED(fin->fin_out, ns_icmp_notfound); 380653642Sguido return NULL; 3807255332Scy } 380892685Sdarrenr 3809145522Sdarrenr tcp = NULL; 3810145522Sdarrenr csump = NULL; 381192685Sdarrenr flags = 0; 3812130886Sdarrenr sumd2 = 0; 381353642Sguido *nflags = IPN_ICMPERR; 3814145522Sdarrenr icmp = fin->fin_dp; 381553642Sguido oip = (ip_t *)&icmp->icmp_ip; 3816145522Sdarrenr dp = (((char *)oip) + (IP_HL(oip) << 2)); 3817145522Sdarrenr if (oip->ip_p == IPPROTO_TCP) { 3818145522Sdarrenr tcp = (tcphdr_t *)dp; 3819145522Sdarrenr csump = (u_short *)&tcp->th_sum; 382053642Sguido flags = IPN_TCP; 3821145522Sdarrenr } else if (oip->ip_p == IPPROTO_UDP) { 3822145522Sdarrenr udphdr_t *udp; 3823145522Sdarrenr 3824145522Sdarrenr udp = (udphdr_t *)dp; 3825145522Sdarrenr tcp = (tcphdr_t *)dp; 3826145522Sdarrenr csump = (u_short *)&udp->uh_sum; 382753642Sguido flags = IPN_UDP; 3828145522Sdarrenr } else if (oip->ip_p == IPPROTO_ICMP) 3829145522Sdarrenr flags = IPN_ICMPQUERY; 3830145522Sdarrenr dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip); 383195418Sdarrenr 383295418Sdarrenr /* 383353642Sguido * Need to adjust ICMP header to include the real IP#'s and 383453642Sguido * port #'s. Only apply a checksum change relative to the 3835255332Scy * IP address change as it will be modified again in ipf_nat_checkout 383653642Sguido * for both address and port. Two checksum changes are 383753642Sguido * necessary for the two header address changes. Be careful 383853642Sguido * to only modify the checksum once for the port # and twice 383953642Sguido * for the IP#. 384053642Sguido */ 384160852Sdarrenr 384267614Sdarrenr /* 384367614Sdarrenr * Step 1 384467614Sdarrenr * Fix the IP addresses in the offending IP packet. You also need 3845170268Sdarrenr * to adjust the IP header checksum of that offending IP packet. 384667614Sdarrenr * 3847145522Sdarrenr * Normally, you would expect that the ICMP checksum of the 3848130886Sdarrenr * ICMP error message needs to be adjusted as well for the 3849130886Sdarrenr * IP address change in oip. 3850145522Sdarrenr * However, this is a NOP, because the ICMP checksum is 3851130886Sdarrenr * calculated over the complete ICMP packet, which includes the 3852145522Sdarrenr * changed oip IP addresses and oip->ip_sum. However, these 3853130886Sdarrenr * two changes cancel each other out (if the delta for 3854145522Sdarrenr * the IP address is x, then the delta for ip_sum is minus x), 3855130886Sdarrenr * so no change in the icmp_cksum is necessary. 3856130886Sdarrenr * 3857170268Sdarrenr * Inbound ICMP 3858170268Sdarrenr * ------------ 3859170268Sdarrenr * MAP rule, SRC=a,DST=b -> SRC=c,DST=b 3860170268Sdarrenr * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b) 3861255332Scy * - OIP_SRC(c)=nat_newsrcip, OIP_DST(b)=nat_newdstip 3862255332Scy *=> OIP_SRC(c)=nat_oldsrcip, OIP_DST(b)=nat_olddstip 3863170268Sdarrenr * 3864170268Sdarrenr * RDR rule, SRC=a,DST=b -> SRC=a,DST=c 3865170268Sdarrenr * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a) 3866255332Scy * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip 3867255332Scy *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip 3868170268Sdarrenr * 3869255332Scy * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d 3870255332Scy * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d) 3871255332Scy * - OIP_SRC(c)=nat_newsrcip, OIP_DST(d)=nat_newdstip 3872255332Scy *=> OIP_SRC(c)=nat_oldsrcip, OIP_DST(d)=nat_olddstip 3873255332Scy * 3874255332Scy * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d 3875255332Scy * - response to outgoing packet (d,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) 3876255332Scy * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip 3877255332Scy *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip 3878255332Scy * 3879170268Sdarrenr * Outbound ICMP 3880170268Sdarrenr * ------------- 3881170268Sdarrenr * MAP rule, SRC=a,DST=b -> SRC=c,DST=b 3882170268Sdarrenr * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) 3883255332Scy * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip 3884255332Scy *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip 3885170268Sdarrenr * 3886170268Sdarrenr * RDR rule, SRC=a,DST=b -> SRC=a,DST=c 3887170268Sdarrenr * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c) 3888255332Scy * - OIP_SRC(a)=nat_newsrcip, OIP_DST(c)=nat_newdstip 3889255332Scy *=> OIP_SRC(a)=nat_oldsrcip, OIP_DST(c)=nat_olddstip 3890170268Sdarrenr * 3891255332Scy * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d 3892255332Scy * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d) 3893255332Scy * - OIP_SRC(c)=nat_olddstip, OIP_DST(d)=nat_oldsrcip 3894255332Scy *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip 3895255332Scy * 3896255332Scy * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d 3897255332Scy * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a) 3898255332Scy * - OIP_SRC(b)=nat_newsrcip, OIP_DST(a)=nat_newdstip 3899255332Scy *=> OIP_SRC(a)=nat_oldsrcip, OIP_DST(c)=nat_olddstip 3900130886Sdarrenr */ 3901255332Scy 3902255332Scy if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) || 3903255332Scy ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) { 3904255332Scy a1.s_addr = ntohl(nat->nat_osrcaddr); 3905255332Scy a4.s_addr = ntohl(oip->ip_src.s_addr); 3906255332Scy a3.s_addr = ntohl(nat->nat_odstaddr); 3907255332Scy a2.s_addr = ntohl(oip->ip_dst.s_addr); 3908170268Sdarrenr oip->ip_src.s_addr = htonl(a1.s_addr); 3909255332Scy oip->ip_dst.s_addr = htonl(a3.s_addr); 3910255332Scy odst = 1; 3911170268Sdarrenr } else { 3912255332Scy a1.s_addr = ntohl(nat->nat_ndstaddr); 3913170268Sdarrenr a2.s_addr = ntohl(oip->ip_dst.s_addr); 3914255332Scy a3.s_addr = ntohl(nat->nat_nsrcaddr); 3915255332Scy a4.s_addr = ntohl(oip->ip_src.s_addr); 3916255332Scy oip->ip_dst.s_addr = htonl(a3.s_addr); 3917255332Scy oip->ip_src.s_addr = htonl(a1.s_addr); 3918255332Scy odst = 0; 3919170268Sdarrenr } 3920255332Scy sum1 = 0; 3921255332Scy sum2 = 0; 3922255332Scy sumd = 0; 3923255332Scy CALC_SUMD(a2.s_addr, a3.s_addr, sum1); 3924255332Scy CALC_SUMD(a4.s_addr, a1.s_addr, sum2); 3925255332Scy sumd = sum2 + sum1; 3926255332Scy if (sumd != 0) 3927255332Scy ipf_fix_datacksum(&oip->ip_sum, sumd); 3928130886Sdarrenr 3929170268Sdarrenr sumd2 = sumd; 3930170268Sdarrenr sum1 = 0; 3931170268Sdarrenr sum2 = 0; 3932170268Sdarrenr 3933130886Sdarrenr /* 3934170268Sdarrenr * Fix UDP pseudo header checksum to compensate for the 3935170268Sdarrenr * IP address change. 3936130886Sdarrenr */ 3937130886Sdarrenr if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) { 3938255332Scy u_32_t sum3, sum4, sumt; 3939255332Scy 394064580Sdarrenr /* 394167614Sdarrenr * Step 2 : 394267614Sdarrenr * For offending TCP/UDP IP packets, translate the ports as 394367614Sdarrenr * well, based on the NAT specification. Of course such 3944170268Sdarrenr * a change may be reflected in the ICMP checksum as well. 394567614Sdarrenr * 394667614Sdarrenr * Since the port fields are part of the TCP/UDP checksum 394767614Sdarrenr * of the offending IP packet, you need to adjust that checksum 3948255332Scy * as well... except that the change in the port numbers should 3949170268Sdarrenr * be offset by the checksum change. However, the TCP/UDP 3950170268Sdarrenr * checksum will also need to change if there has been an 3951170268Sdarrenr * IP address change. 395267614Sdarrenr */ 3953170268Sdarrenr if (odst == 1) { 3954255332Scy sum1 = ntohs(nat->nat_osport); 3955255332Scy sum4 = ntohs(tcp->th_sport); 3956255332Scy sum3 = ntohs(nat->nat_odport); 3957255332Scy sum2 = ntohs(tcp->th_dport); 3958145522Sdarrenr 3959170268Sdarrenr tcp->th_sport = htons(sum1); 3960255332Scy tcp->th_dport = htons(sum3); 3961170268Sdarrenr } else { 3962255332Scy sum1 = ntohs(nat->nat_ndport); 3963145522Sdarrenr sum2 = ntohs(tcp->th_dport); 3964255332Scy sum3 = ntohs(nat->nat_nsport); 3965255332Scy sum4 = ntohs(tcp->th_sport); 3966170268Sdarrenr 3967255332Scy tcp->th_dport = htons(sum3); 3968255332Scy tcp->th_sport = htons(sum1); 3969145522Sdarrenr } 3970255332Scy CALC_SUMD(sum4, sum1, sumt); 3971255332Scy sumd += sumt; 3972255332Scy CALC_SUMD(sum2, sum3, sumt); 3973255332Scy sumd += sumt; 397467614Sdarrenr 3975170268Sdarrenr if (sumd != 0 || sumd2 != 0) { 3976145522Sdarrenr /* 3977170268Sdarrenr * At this point, sumd is the delta to apply to the 3978170268Sdarrenr * TCP/UDP header, given the changes in both the IP 3979170268Sdarrenr * address and the ports and sumd2 is the delta to 3980170268Sdarrenr * apply to the ICMP header, given the IP address 3981170268Sdarrenr * change delta that may need to be applied to the 3982170268Sdarrenr * TCP/UDP checksum instead. 3983145522Sdarrenr * 3984170268Sdarrenr * If we will both the IP and TCP/UDP checksums 3985170268Sdarrenr * then the ICMP checksum changes by the address 3986170268Sdarrenr * delta applied to the TCP/UDP checksum. If we 3987170268Sdarrenr * do not change the TCP/UDP checksum them we 3988170268Sdarrenr * apply the delta in ports to the ICMP checksum. 3989145522Sdarrenr */ 3990161356Sguido if (oip->ip_p == IPPROTO_UDP) { 3991161356Sguido if ((dlen >= 8) && (*csump != 0)) { 3992255332Scy ipf_fix_datacksum(csump, sumd); 3993161356Sguido } else { 3994255332Scy CALC_SUMD(sum1, sum4, sumd2); 3995255332Scy CALC_SUMD(sum3, sum2, sumt); 3996255332Scy sumd2 += sumt; 3997161356Sguido } 3998170268Sdarrenr } else if (oip->ip_p == IPPROTO_TCP) { 3999145522Sdarrenr if (dlen >= 18) { 4000255332Scy ipf_fix_datacksum(csump, sumd); 4001145522Sdarrenr } else { 4002255332Scy CALC_SUMD(sum1, sum4, sumd2); 4003255332Scy CALC_SUMD(sum3, sum2, sumt); 4004255332Scy sumd2 += sumt; 400567614Sdarrenr } 400653642Sguido } 4007170268Sdarrenr if (sumd2 != 0) { 4008170268Sdarrenr sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 4009170268Sdarrenr sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 4010170268Sdarrenr sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 4011255332Scy ipf_fix_incksum(0, &icmp->icmp_cksum, sumd2, 0); 4012130886Sdarrenr } 401353642Sguido } 4014145522Sdarrenr } else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) { 4015145522Sdarrenr icmphdr_t *orgicmp; 4016145522Sdarrenr 4017145522Sdarrenr /* 4018145522Sdarrenr * XXX - what if this is bogus hl and we go off the end ? 4019255332Scy * In this case, ipf_nat_icmperrorlookup() will have 4020255332Scy * returned NULL. 4021145522Sdarrenr */ 4022145522Sdarrenr orgicmp = (icmphdr_t *)dp; 4023145522Sdarrenr 4024170268Sdarrenr if (odst == 1) { 4025255332Scy if (orgicmp->icmp_id != nat->nat_osport) { 4026145522Sdarrenr 4027145522Sdarrenr /* 4028145522Sdarrenr * Fix ICMP checksum (of the offening ICMP 4029145522Sdarrenr * query packet) to compensate the change 4030145522Sdarrenr * in the ICMP id of the offending ICMP 4031145522Sdarrenr * packet. 4032145522Sdarrenr * 4033145522Sdarrenr * Since you modify orgicmp->icmp_id with 4034145522Sdarrenr * a delta (say x) and you compensate that 4035145522Sdarrenr * in origicmp->icmp_cksum with a delta 4036145522Sdarrenr * minus x, you don't have to adjust the 4037145522Sdarrenr * overall icmp->icmp_cksum 4038145522Sdarrenr */ 4039145522Sdarrenr sum1 = ntohs(orgicmp->icmp_id); 4040255332Scy sum2 = ntohs(nat->nat_oicmpid); 4041145522Sdarrenr CALC_SUMD(sum1, sum2, sumd); 4042255332Scy orgicmp->icmp_id = nat->nat_oicmpid; 4043255332Scy ipf_fix_datacksum(&orgicmp->icmp_cksum, sumd); 4044145522Sdarrenr } 4045145522Sdarrenr } /* nat_dir == NAT_INBOUND is impossible for icmp queries */ 404653642Sguido } 404753642Sguido return nat; 404853642Sguido} 404953642Sguido 405053642Sguido 405153642Sguido/* 4052255332Scy * MAP-IN MAP-OUT RDR-IN RDR-OUT 4053255332Scy * osrc X == src == src X 4054255332Scy * odst X == dst == dst X 4055255332Scy * nsrc == dst X X == dst 4056255332Scy * ndst == src X X == src 4057255332Scy * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND 4058255332Scy */ 4059255332Scy/* 4060145522Sdarrenr * NB: these lookups don't lock access to the list, it assumed that it has 4061145522Sdarrenr * already been done! 406253642Sguido */ 4063145522Sdarrenr/* ------------------------------------------------------------------------ */ 4064255332Scy/* Function: ipf_nat_inlookup */ 4065145522Sdarrenr/* Returns: nat_t* - NULL == no match, */ 4066145522Sdarrenr/* else pointer to matching NAT entry */ 4067145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4068145522Sdarrenr/* flags(I) - NAT flags for this packet */ 4069145522Sdarrenr/* p(I) - protocol for this packet */ 4070145522Sdarrenr/* src(I) - source IP address */ 4071145522Sdarrenr/* mapdst(I) - destination IP address */ 4072145522Sdarrenr/* */ 4073145522Sdarrenr/* Lookup a nat entry based on the mapped destination ip address/port and */ 4074145522Sdarrenr/* real source address/port. We use this lookup when receiving a packet, */ 4075145522Sdarrenr/* we're looking for a table entry, based on the destination address. */ 4076145522Sdarrenr/* */ 4077145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 4078145522Sdarrenr/* */ 4079255332Scy/* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ 4080145522Sdarrenr/* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 4081145522Sdarrenr/* */ 4082145522Sdarrenr/* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 4083145522Sdarrenr/* the packet is of said protocol */ 4084145522Sdarrenr/* ------------------------------------------------------------------------ */ 4085255332Scynat_t * 4086255332Scyipf_nat_inlookup(fin, flags, p, src, mapdst) 4087255332Scy fr_info_t *fin; 4088255332Scy u_int flags, p; 4089255332Scy struct in_addr src , mapdst; 409053642Sguido{ 4091255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 4092255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 4093145522Sdarrenr u_short sport, dport; 4094145522Sdarrenr grehdr_t *gre; 409592685Sdarrenr ipnat_t *ipn; 4096145522Sdarrenr u_int sflags; 4097145522Sdarrenr nat_t *nat; 4098145522Sdarrenr int nflags; 4099145522Sdarrenr u_32_t dst; 410092685Sdarrenr void *ifp; 4101255332Scy u_int hv, rhv; 410253642Sguido 4103161356Sguido ifp = fin->fin_ifp; 4104145522Sdarrenr gre = NULL; 410567614Sdarrenr dst = mapdst.s_addr; 4106145522Sdarrenr sflags = flags & NAT_TCPUDPICMP; 4107145522Sdarrenr 4108145522Sdarrenr switch (p) 4109145522Sdarrenr { 4110145522Sdarrenr case IPPROTO_TCP : 4111145522Sdarrenr case IPPROTO_UDP : 411292685Sdarrenr sport = htons(fin->fin_data[0]); 411392685Sdarrenr dport = htons(fin->fin_data[1]); 4114145522Sdarrenr break; 4115145522Sdarrenr case IPPROTO_ICMP : 4116323231Scy sport = 0; 4117323231Scy dport = fin->fin_data[1]; 4118145522Sdarrenr break; 4119145522Sdarrenr default : 4120255332Scy sport = 0; 4121255332Scy dport = 0; 4122145522Sdarrenr break; 412392685Sdarrenr } 412453642Sguido 4125145522Sdarrenr 4126145522Sdarrenr if ((flags & SI_WILDP) != 0) 4127145522Sdarrenr goto find_in_wild_ports; 4128145522Sdarrenr 4129255332Scy rhv = NAT_HASH_FN(dst, dport, 0xffffffff); 4130255332Scy rhv = NAT_HASH_FN(src.s_addr, rhv + sport, 0xffffffff); 4131255332Scy hv = rhv % softn->ipf_nat_table_sz; 4132255332Scy nat = softn->ipf_nat_table[1][hv]; 4133255332Scy /* TRACE dst, dport, src, sport, hv, nat */ 4134255332Scy 413553642Sguido for (; nat; nat = nat->nat_hnext[1]) { 4136161356Sguido if (nat->nat_ifps[0] != NULL) { 4137161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 4138161356Sguido continue; 4139255332Scy } 4140161356Sguido 4141255332Scy if (nat->nat_pr[0] != p) 4142255332Scy continue; 4143145522Sdarrenr 4144255332Scy switch (nat->nat_dir) 4145255332Scy { 4146255332Scy case NAT_INBOUND : 4147255332Scy case NAT_DIVERTIN : 4148255332Scy if (nat->nat_v[0] != 4) 4149255332Scy continue; 4150255332Scy if (nat->nat_osrcaddr != src.s_addr || 4151255332Scy nat->nat_odstaddr != dst) 4152255332Scy continue; 4153255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 4154255332Scy if (nat->nat_osport != sport) 4155145522Sdarrenr continue; 4156255332Scy if (nat->nat_odport != dport) 4157255332Scy continue; 4158255332Scy 4159255332Scy } else if (p == IPPROTO_ICMP) { 4160255332Scy if (nat->nat_osport != dport) { 4161255332Scy continue; 4162145522Sdarrenr } 4163255332Scy } 4164255332Scy break; 4165255332Scy case NAT_DIVERTOUT : 4166255332Scy if (nat->nat_dlocal) 4167255332Scy continue; 4168255332Scy case NAT_OUTBOUND : 4169255332Scy if (nat->nat_v[1] != 4) 4170255332Scy continue; 4171255332Scy if (nat->nat_dlocal) 4172255332Scy continue; 4173255332Scy if (nat->nat_dlocal) 4174255332Scy continue; 4175255332Scy if (nat->nat_ndstaddr != src.s_addr || 4176255332Scy nat->nat_nsrcaddr != dst) 4177255332Scy continue; 4178255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 4179255332Scy if (nat->nat_ndport != sport) 418092685Sdarrenr continue; 4181255332Scy if (nat->nat_nsport != dport) 418292685Sdarrenr continue; 4183255332Scy 4184255332Scy } else if (p == IPPROTO_ICMP) { 4185255332Scy if (nat->nat_osport != dport) { 4186255332Scy continue; 4187255332Scy } 418892685Sdarrenr } 4189255332Scy break; 4190255332Scy } 419192685Sdarrenr 4192255332Scy 4193255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 419492685Sdarrenr ipn = nat->nat_ptr; 419592685Sdarrenr if ((ipn != NULL) && (nat->nat_aps != NULL)) 4196255332Scy if (ipf_proxy_match(fin, nat) != 0) 419792685Sdarrenr continue; 419892685Sdarrenr } 4199255332Scy if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { 4200255332Scy nat->nat_ifps[0] = ifp; 4201255332Scy nat->nat_mtu[0] = GETIFMTU_4(ifp); 4202255332Scy } 4203255332Scy return nat; 420453642Sguido } 4205145522Sdarrenr 4206145522Sdarrenr /* 4207145522Sdarrenr * So if we didn't find it but there are wildcard members in the hash 4208145522Sdarrenr * table, go back and look for them. We do this search and update here 4209145522Sdarrenr * because it is modifying the NAT table and we want to do this only 4210145522Sdarrenr * for the first packet that matches. The exception, of course, is 4211145522Sdarrenr * for "dummy" (FI_IGNORE) lookups. 4212145522Sdarrenr */ 4213145522Sdarrenrfind_in_wild_ports: 4214255332Scy if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { 4215255332Scy NBUMPSIDEX(0, ns_lookup_miss, ns_lookup_miss_0); 421667614Sdarrenr return NULL; 4217255332Scy } 4218255332Scy if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { 4219255332Scy NBUMPSIDEX(0, ns_lookup_nowild, ns_lookup_nowild_0); 4220145522Sdarrenr return NULL; 4221255332Scy } 4222145522Sdarrenr 4223255332Scy RWLOCK_EXIT(&softc->ipf_nat); 4224145522Sdarrenr 422580482Sdarrenr hv = NAT_HASH_FN(dst, 0, 0xffffffff); 4226255332Scy hv = NAT_HASH_FN(src.s_addr, hv, softn->ipf_nat_table_sz); 4227255332Scy WRITE_ENTER(&softc->ipf_nat); 4228145522Sdarrenr 4229255332Scy nat = softn->ipf_nat_table[1][hv]; 4230255332Scy /* TRACE dst, src, hv, nat */ 423167614Sdarrenr for (; nat; nat = nat->nat_hnext[1]) { 4232161356Sguido if (nat->nat_ifps[0] != NULL) { 4233161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 4234161356Sguido continue; 4235255332Scy } 4236145522Sdarrenr 4237255332Scy if (nat->nat_pr[0] != fin->fin_p) 423867614Sdarrenr continue; 4239145522Sdarrenr 4240255332Scy switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)) 4241255332Scy { 4242255332Scy case NAT_INBOUND : 4243255332Scy if (nat->nat_v[0] != 4) 4244255332Scy continue; 4245255332Scy if (nat->nat_osrcaddr != src.s_addr || 4246255332Scy nat->nat_odstaddr != dst) 4247255332Scy continue; 4248255332Scy break; 4249255332Scy case NAT_OUTBOUND : 4250255332Scy if (nat->nat_v[1] != 4) 4251255332Scy continue; 4252255332Scy if (nat->nat_ndstaddr != src.s_addr || 4253255332Scy nat->nat_nsrcaddr != dst) 4254255332Scy continue; 4255255332Scy break; 4256255332Scy } 4257255332Scy 4258145522Sdarrenr nflags = nat->nat_flags; 4259145522Sdarrenr if (!(nflags & (NAT_TCPUDP|SI_WILDP))) 4260145522Sdarrenr continue; 4261145522Sdarrenr 4262255332Scy if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags, 4263255332Scy NAT_INBOUND) == 1) { 4264145522Sdarrenr if ((fin->fin_flx & FI_IGNORE) != 0) 4265145522Sdarrenr break; 4266145522Sdarrenr if ((nflags & SI_CLONE) != 0) { 4267255332Scy nat = ipf_nat_clone(fin, nat); 4268145522Sdarrenr if (nat == NULL) 4269145522Sdarrenr break; 4270145522Sdarrenr } else { 4271255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 4272255332Scy softn->ipf_nat_stats.ns_wilds--; 4273255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 4274145522Sdarrenr } 4275255332Scy 4276255332Scy if (nat->nat_dir == NAT_INBOUND) { 4277255332Scy if (nat->nat_osport == 0) { 4278255332Scy nat->nat_osport = sport; 4279255332Scy nat->nat_nsport = sport; 4280255332Scy } 4281255332Scy if (nat->nat_odport == 0) { 4282255332Scy nat->nat_odport = dport; 4283255332Scy nat->nat_ndport = dport; 4284255332Scy } 4285255332Scy } else if (nat->nat_dir == NAT_OUTBOUND) { 4286255332Scy if (nat->nat_osport == 0) { 4287255332Scy nat->nat_osport = dport; 4288255332Scy nat->nat_nsport = dport; 4289255332Scy } 4290255332Scy if (nat->nat_odport == 0) { 4291255332Scy nat->nat_odport = sport; 4292255332Scy nat->nat_ndport = sport; 4293255332Scy } 4294255332Scy } 4295255332Scy if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { 4296255332Scy nat->nat_ifps[0] = ifp; 4297255332Scy nat->nat_mtu[0] = GETIFMTU_4(ifp); 4298255332Scy } 4299145522Sdarrenr nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 4300255332Scy ipf_nat_tabmove(softn, nat); 430167614Sdarrenr break; 430267614Sdarrenr } 430367614Sdarrenr } 4304145522Sdarrenr 4305255332Scy MUTEX_DOWNGRADE(&softc->ipf_nat); 4306145522Sdarrenr 4307255332Scy if (nat == NULL) { 4308255332Scy NBUMPSIDE(0, ns_lookup_miss); 4309255332Scy } 431067614Sdarrenr return nat; 431153642Sguido} 431253642Sguido 431353642Sguido 4314145522Sdarrenr/* ------------------------------------------------------------------------ */ 4315255332Scy/* Function: ipf_nat_tabmove */ 4316145522Sdarrenr/* Returns: Nil */ 4317255332Scy/* Parameters: softn(I) - pointer to NAT context structure */ 4318255332Scy/* nat(I) - pointer to NAT structure */ 4319145522Sdarrenr/* Write Lock: ipf_nat */ 4320145522Sdarrenr/* */ 4321145522Sdarrenr/* This function is only called for TCP/UDP NAT table entries where the */ 4322145522Sdarrenr/* original was placed in the table without hashing on the ports and we now */ 4323145522Sdarrenr/* want to include hashing on port numbers. */ 4324145522Sdarrenr/* ------------------------------------------------------------------------ */ 4325255332Scystatic void 4326255332Scyipf_nat_tabmove(softn, nat) 4327255332Scy ipf_nat_softc_t *softn; 4328255332Scy nat_t *nat; 432967614Sdarrenr{ 4330255332Scy u_int hv0, hv1, rhv0, rhv1; 4331255332Scy natstat_t *nsp; 433267614Sdarrenr nat_t **natp; 433367614Sdarrenr 4334145522Sdarrenr if (nat->nat_flags & SI_CLONE) 4335145522Sdarrenr return; 433672006Sdarrenr 4337255332Scy nsp = &softn->ipf_nat_stats; 433867614Sdarrenr /* 433967614Sdarrenr * Remove the NAT entry from the old location 434067614Sdarrenr */ 434167614Sdarrenr if (nat->nat_hnext[0]) 434267614Sdarrenr nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 434367614Sdarrenr *nat->nat_phnext[0] = nat->nat_hnext[0]; 4344255332Scy nsp->ns_side[0].ns_bucketlen[nat->nat_hv[0] % 4345255332Scy softn->ipf_nat_table_sz]--; 434667614Sdarrenr 434767614Sdarrenr if (nat->nat_hnext[1]) 434867853Sdarrenr nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 434967614Sdarrenr *nat->nat_phnext[1] = nat->nat_hnext[1]; 4350255332Scy nsp->ns_side[1].ns_bucketlen[nat->nat_hv[1] % 4351255332Scy softn->ipf_nat_table_sz]--; 435267614Sdarrenr 435367853Sdarrenr /* 435467853Sdarrenr * Add into the NAT table in the new position 435567853Sdarrenr */ 4356255332Scy rhv0 = NAT_HASH_FN(nat->nat_osrcaddr, nat->nat_osport, 0xffffffff); 4357255332Scy rhv0 = NAT_HASH_FN(nat->nat_odstaddr, rhv0 + nat->nat_odport, 4358255332Scy 0xffffffff); 4359255332Scy rhv1 = NAT_HASH_FN(nat->nat_nsrcaddr, nat->nat_nsport, 0xffffffff); 4360255332Scy rhv1 = NAT_HASH_FN(nat->nat_ndstaddr, rhv1 + nat->nat_ndport, 4361255332Scy 0xffffffff); 4362255332Scy 4363255332Scy hv0 = rhv0 % softn->ipf_nat_table_sz; 4364255332Scy hv1 = rhv1 % softn->ipf_nat_table_sz; 4365255332Scy 4366255332Scy if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) { 4367255332Scy u_int swap; 4368255332Scy 4369255332Scy swap = hv0; 4370255332Scy hv0 = hv1; 4371255332Scy hv1 = swap; 4372255332Scy } 4373255332Scy 4374255332Scy /* TRACE nat_osrcaddr, nat_osport, nat_odstaddr, nat_odport, hv0 */ 4375255332Scy /* TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, nat_ndport, hv1 */ 4376255332Scy 4377255332Scy nat->nat_hv[0] = rhv0; 4378255332Scy natp = &softn->ipf_nat_table[0][hv0]; 437967614Sdarrenr if (*natp) 438067614Sdarrenr (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 438167614Sdarrenr nat->nat_phnext[0] = natp; 438267614Sdarrenr nat->nat_hnext[0] = *natp; 438367614Sdarrenr *natp = nat; 4384255332Scy nsp->ns_side[0].ns_bucketlen[hv0]++; 438567614Sdarrenr 4386255332Scy nat->nat_hv[1] = rhv1; 4387255332Scy natp = &softn->ipf_nat_table[1][hv1]; 438867614Sdarrenr if (*natp) 438967614Sdarrenr (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 439067614Sdarrenr nat->nat_phnext[1] = natp; 439167614Sdarrenr nat->nat_hnext[1] = *natp; 439267614Sdarrenr *natp = nat; 4393255332Scy nsp->ns_side[1].ns_bucketlen[hv1]++; 439467614Sdarrenr} 439567614Sdarrenr 439667614Sdarrenr 4397145522Sdarrenr/* ------------------------------------------------------------------------ */ 4398255332Scy/* Function: ipf_nat_outlookup */ 4399145522Sdarrenr/* Returns: nat_t* - NULL == no match, */ 4400145522Sdarrenr/* else pointer to matching NAT entry */ 4401145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4402145522Sdarrenr/* flags(I) - NAT flags for this packet */ 4403145522Sdarrenr/* p(I) - protocol for this packet */ 4404145522Sdarrenr/* src(I) - source IP address */ 4405145522Sdarrenr/* dst(I) - destination IP address */ 4406255332Scy/* rw(I) - 1 == write lock on held, 0 == read lock. */ 4407145522Sdarrenr/* */ 4408145522Sdarrenr/* Lookup a nat entry based on the source 'real' ip address/port and */ 4409145522Sdarrenr/* destination address/port. We use this lookup when sending a packet out, */ 4410145522Sdarrenr/* we're looking for a table entry, based on the source address. */ 4411145522Sdarrenr/* */ 4412145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 4413145522Sdarrenr/* */ 4414255332Scy/* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ 4415145522Sdarrenr/* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 4416145522Sdarrenr/* */ 4417145522Sdarrenr/* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 4418145522Sdarrenr/* the packet is of said protocol */ 4419145522Sdarrenr/* ------------------------------------------------------------------------ */ 4420255332Scynat_t * 4421255332Scyipf_nat_outlookup(fin, flags, p, src, dst) 4422255332Scy fr_info_t *fin; 4423255332Scy u_int flags, p; 4424255332Scy struct in_addr src , dst; 442553642Sguido{ 4426255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 4427255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 4428145522Sdarrenr u_short sport, dport; 4429145522Sdarrenr u_int sflags; 443092685Sdarrenr ipnat_t *ipn; 4431145522Sdarrenr nat_t *nat; 443292685Sdarrenr void *ifp; 443353642Sguido u_int hv; 443453642Sguido 443592685Sdarrenr ifp = fin->fin_ifp; 4436145522Sdarrenr sflags = flags & IPN_TCPUDPICMP; 4437145522Sdarrenr 4438145522Sdarrenr switch (p) 4439145522Sdarrenr { 4440145522Sdarrenr case IPPROTO_TCP : 4441145522Sdarrenr case IPPROTO_UDP : 4442145522Sdarrenr sport = htons(fin->fin_data[0]); 4443145522Sdarrenr dport = htons(fin->fin_data[1]); 4444145522Sdarrenr break; 4445145522Sdarrenr case IPPROTO_ICMP : 4446323231Scy sport = 0; 4447323231Scy dport = fin->fin_data[1]; 4448145522Sdarrenr break; 4449145522Sdarrenr default : 4450323231Scy sport = 0; 4451323231Scy dport = 0; 4452145522Sdarrenr break; 445392685Sdarrenr } 445453642Sguido 4455145522Sdarrenr if ((flags & SI_WILDP) != 0) 4456145522Sdarrenr goto find_out_wild_ports; 4457145522Sdarrenr 4458255332Scy hv = NAT_HASH_FN(src.s_addr, sport, 0xffffffff); 4459255332Scy hv = NAT_HASH_FN(dst.s_addr, hv + dport, softn->ipf_nat_table_sz); 4460255332Scy nat = softn->ipf_nat_table[0][hv]; 4461255332Scy 4462255332Scy /* TRACE src, sport, dst, dport, hv, nat */ 4463255332Scy 446453642Sguido for (; nat; nat = nat->nat_hnext[0]) { 4465161356Sguido if (nat->nat_ifps[1] != NULL) { 4466161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 4467161356Sguido continue; 4468255332Scy } 4469161356Sguido 4470255332Scy if (nat->nat_pr[1] != p) 4471255332Scy continue; 447253642Sguido 4473255332Scy switch (nat->nat_dir) 4474255332Scy { 4475255332Scy case NAT_INBOUND : 4476255332Scy case NAT_DIVERTIN : 4477255332Scy if (nat->nat_v[1] != 4) 4478255332Scy continue; 4479255332Scy if (nat->nat_ndstaddr != src.s_addr || 4480255332Scy nat->nat_nsrcaddr != dst.s_addr) 4481255332Scy continue; 4482255332Scy 4483255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 4484255332Scy if (nat->nat_ndport != sport) 4485145522Sdarrenr continue; 4486255332Scy if (nat->nat_nsport != dport) 448792685Sdarrenr continue; 4488255332Scy 4489255332Scy } else if (p == IPPROTO_ICMP) { 4490255332Scy if (nat->nat_osport != dport) { 449192685Sdarrenr continue; 4492255332Scy } 449392685Sdarrenr } 4494255332Scy break; 4495255332Scy case NAT_OUTBOUND : 4496255332Scy case NAT_DIVERTOUT : 4497255332Scy if (nat->nat_v[0] != 4) 4498255332Scy continue; 4499255332Scy if (nat->nat_osrcaddr != src.s_addr || 4500255332Scy nat->nat_odstaddr != dst.s_addr) 4501255332Scy continue; 450292685Sdarrenr 4503255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 4504255332Scy if (nat->nat_odport != dport) 450592685Sdarrenr continue; 4506255332Scy if (nat->nat_osport != sport) 4507255332Scy continue; 4508255332Scy 4509255332Scy } else if (p == IPPROTO_ICMP) { 4510255332Scy if (nat->nat_osport != dport) { 4511255332Scy continue; 4512255332Scy } 4513255332Scy } 4514255332Scy break; 451592685Sdarrenr } 4516255332Scy 4517255332Scy ipn = nat->nat_ptr; 4518255332Scy if ((ipn != NULL) && (nat->nat_aps != NULL)) 4519255332Scy if (ipf_proxy_match(fin, nat) != 0) 4520255332Scy continue; 4521255332Scy 4522255332Scy if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { 4523255332Scy nat->nat_ifps[1] = ifp; 4524255332Scy nat->nat_mtu[1] = GETIFMTU_4(ifp); 4525255332Scy } 4526255332Scy return nat; 452753642Sguido } 4528145522Sdarrenr 4529145522Sdarrenr /* 4530145522Sdarrenr * So if we didn't find it but there are wildcard members in the hash 4531145522Sdarrenr * table, go back and look for them. We do this search and update here 4532145522Sdarrenr * because it is modifying the NAT table and we want to do this only 4533145522Sdarrenr * for the first packet that matches. The exception, of course, is 4534145522Sdarrenr * for "dummy" (FI_IGNORE) lookups. 4535145522Sdarrenr */ 4536145522Sdarrenrfind_out_wild_ports: 4537255332Scy if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { 4538255332Scy NBUMPSIDEX(1, ns_lookup_miss, ns_lookup_miss_1); 453967614Sdarrenr return NULL; 4540255332Scy } 4541255332Scy if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { 4542255332Scy NBUMPSIDEX(1, ns_lookup_nowild, ns_lookup_nowild_1); 4543145522Sdarrenr return NULL; 4544255332Scy } 454592685Sdarrenr 4546255332Scy RWLOCK_EXIT(&softc->ipf_nat); 4547145522Sdarrenr 4548255332Scy hv = NAT_HASH_FN(src.s_addr, 0, 0xffffffff); 4549255332Scy hv = NAT_HASH_FN(dst.s_addr, hv, softn->ipf_nat_table_sz); 4550145522Sdarrenr 4551255332Scy WRITE_ENTER(&softc->ipf_nat); 4552145522Sdarrenr 4553255332Scy nat = softn->ipf_nat_table[0][hv]; 455467614Sdarrenr for (; nat; nat = nat->nat_hnext[0]) { 4555161356Sguido if (nat->nat_ifps[1] != NULL) { 4556161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 4557161356Sguido continue; 4558255332Scy } 4559145522Sdarrenr 4560255332Scy if (nat->nat_pr[1] != fin->fin_p) 456167614Sdarrenr continue; 4562145522Sdarrenr 4563255332Scy switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)) 4564255332Scy { 4565255332Scy case NAT_INBOUND : 4566255332Scy if (nat->nat_v[1] != 4) 4567255332Scy continue; 4568255332Scy if (nat->nat_ndstaddr != src.s_addr || 4569255332Scy nat->nat_nsrcaddr != dst.s_addr) 4570255332Scy continue; 4571255332Scy break; 4572255332Scy case NAT_OUTBOUND : 4573255332Scy if (nat->nat_v[0] != 4) 4574255332Scy continue; 4575255332Scy if (nat->nat_osrcaddr != src.s_addr || 4576255332Scy nat->nat_odstaddr != dst.s_addr) 4577255332Scy continue; 4578255332Scy break; 4579255332Scy } 4580255332Scy 4581255332Scy if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP))) 4582145522Sdarrenr continue; 4583145522Sdarrenr 4584255332Scy if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags, 4585255332Scy NAT_OUTBOUND) == 1) { 4586145522Sdarrenr if ((fin->fin_flx & FI_IGNORE) != 0) 4587145522Sdarrenr break; 4588255332Scy if ((nat->nat_flags & SI_CLONE) != 0) { 4589255332Scy nat = ipf_nat_clone(fin, nat); 4590145522Sdarrenr if (nat == NULL) 4591145522Sdarrenr break; 4592145522Sdarrenr } else { 4593255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 4594255332Scy softn->ipf_nat_stats.ns_wilds--; 4595255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 4596145522Sdarrenr } 4597255332Scy 4598255332Scy if (nat->nat_dir == NAT_OUTBOUND) { 4599255332Scy if (nat->nat_osport == 0) { 4600255332Scy nat->nat_osport = sport; 4601255332Scy nat->nat_nsport = sport; 4602255332Scy } 4603255332Scy if (nat->nat_odport == 0) { 4604255332Scy nat->nat_odport = dport; 4605255332Scy nat->nat_ndport = dport; 4606255332Scy } 4607255332Scy } else if (nat->nat_dir == NAT_INBOUND) { 4608255332Scy if (nat->nat_osport == 0) { 4609255332Scy nat->nat_osport = dport; 4610255332Scy nat->nat_nsport = dport; 4611255332Scy } 4612255332Scy if (nat->nat_odport == 0) { 4613255332Scy nat->nat_odport = sport; 4614255332Scy nat->nat_ndport = sport; 4615255332Scy } 4616255332Scy } 4617255332Scy if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { 4618255332Scy nat->nat_ifps[1] = ifp; 4619255332Scy nat->nat_mtu[1] = GETIFMTU_4(ifp); 4620255332Scy } 4621145522Sdarrenr nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 4622255332Scy ipf_nat_tabmove(softn, nat); 462367614Sdarrenr break; 462467614Sdarrenr } 462567614Sdarrenr } 4626145522Sdarrenr 4627255332Scy MUTEX_DOWNGRADE(&softc->ipf_nat); 4628145522Sdarrenr 4629255332Scy if (nat == NULL) { 4630255332Scy NBUMPSIDE(1, ns_lookup_miss); 4631255332Scy } 463267614Sdarrenr return nat; 463353642Sguido} 463453642Sguido 463553642Sguido 4636145522Sdarrenr/* ------------------------------------------------------------------------ */ 4637255332Scy/* Function: ipf_nat_lookupredir */ 4638145522Sdarrenr/* Returns: nat_t* - NULL == no match, */ 4639145522Sdarrenr/* else pointer to matching NAT entry */ 4640145522Sdarrenr/* Parameters: np(I) - pointer to description of packet to find NAT table */ 4641145522Sdarrenr/* entry for. */ 4642145522Sdarrenr/* */ 4643145522Sdarrenr/* Lookup the NAT tables to search for a matching redirect */ 4644161356Sguido/* The contents of natlookup_t should imitate those found in a packet that */ 4645161356Sguido/* would be translated - ie a packet coming in for RDR or going out for MAP.*/ 4646161356Sguido/* We can do the lookup in one of two ways, imitating an inbound or */ 4647161356Sguido/* outbound packet. By default we assume outbound, unless IPN_IN is set. */ 4648161356Sguido/* For IN, the fields are set as follows: */ 4649161356Sguido/* nl_real* = source information */ 4650161356Sguido/* nl_out* = destination information (translated) */ 4651161356Sguido/* For an out packet, the fields are set like this: */ 4652161356Sguido/* nl_in* = source information (untranslated) */ 4653161356Sguido/* nl_out* = destination information (translated) */ 4654145522Sdarrenr/* ------------------------------------------------------------------------ */ 4655255332Scynat_t * 4656255332Scyipf_nat_lookupredir(np) 4657255332Scy natlookup_t *np; 465853642Sguido{ 4659145522Sdarrenr fr_info_t fi; 466053642Sguido nat_t *nat; 466153642Sguido 466292685Sdarrenr bzero((char *)&fi, sizeof(fi)); 4663145522Sdarrenr if (np->nl_flags & IPN_IN) { 4664145522Sdarrenr fi.fin_data[0] = ntohs(np->nl_realport); 4665145522Sdarrenr fi.fin_data[1] = ntohs(np->nl_outport); 4666145522Sdarrenr } else { 4667145522Sdarrenr fi.fin_data[0] = ntohs(np->nl_inport); 4668145522Sdarrenr fi.fin_data[1] = ntohs(np->nl_outport); 4669145522Sdarrenr } 4670145522Sdarrenr if (np->nl_flags & IPN_TCP) 4671145522Sdarrenr fi.fin_p = IPPROTO_TCP; 4672145522Sdarrenr else if (np->nl_flags & IPN_UDP) 4673145522Sdarrenr fi.fin_p = IPPROTO_UDP; 4674145522Sdarrenr else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) 4675145522Sdarrenr fi.fin_p = IPPROTO_ICMP; 467692685Sdarrenr 467753642Sguido /* 4678145522Sdarrenr * We can do two sorts of lookups: 4679145522Sdarrenr * - IPN_IN: we have the `real' and `out' address, look for `in'. 4680145522Sdarrenr * - default: we have the `in' and `out' address, look for `real'. 468153642Sguido */ 4682145522Sdarrenr if (np->nl_flags & IPN_IN) { 4683255332Scy if ((nat = ipf_nat_inlookup(&fi, np->nl_flags, fi.fin_p, 4684255332Scy np->nl_realip, np->nl_outip))) { 4685255332Scy np->nl_inip = nat->nat_odstip; 4686255332Scy np->nl_inport = nat->nat_odport; 4687145522Sdarrenr } 4688145522Sdarrenr } else { 4689145522Sdarrenr /* 4690145522Sdarrenr * If nl_inip is non null, this is a lookup based on the real 4691145522Sdarrenr * ip address. Else, we use the fake. 4692145522Sdarrenr */ 4693255332Scy if ((nat = ipf_nat_outlookup(&fi, np->nl_flags, fi.fin_p, 4694145522Sdarrenr np->nl_inip, np->nl_outip))) { 4695145522Sdarrenr 4696145522Sdarrenr if ((np->nl_flags & IPN_FINDFORWARD) != 0) { 4697145522Sdarrenr fr_info_t fin; 4698145522Sdarrenr bzero((char *)&fin, sizeof(fin)); 4699255332Scy fin.fin_p = nat->nat_pr[0]; 4700255332Scy fin.fin_data[0] = ntohs(nat->nat_ndport); 4701255332Scy fin.fin_data[1] = ntohs(nat->nat_nsport); 4702255332Scy if (ipf_nat_inlookup(&fin, np->nl_flags, 4703255332Scy fin.fin_p, nat->nat_ndstip, 4704255332Scy nat->nat_nsrcip) != NULL) { 4705145522Sdarrenr np->nl_flags &= ~IPN_FINDFORWARD; 4706145522Sdarrenr } 4707145522Sdarrenr } 4708145522Sdarrenr 4709315079Scy np->nl_realip = nat->nat_odstip; 4710315079Scy np->nl_realport = nat->nat_odport; 4711145522Sdarrenr } 4712145522Sdarrenr } 4713145522Sdarrenr 471453642Sguido return nat; 471553642Sguido} 471653642Sguido 471753642Sguido 4718145522Sdarrenr/* ------------------------------------------------------------------------ */ 4719255332Scy/* Function: ipf_nat_match */ 4720145522Sdarrenr/* Returns: int - 0 == no match, 1 == match */ 4721145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4722145522Sdarrenr/* np(I) - pointer to NAT rule */ 4723145522Sdarrenr/* */ 4724145522Sdarrenr/* Pull the matching of a packet against a NAT rule out of that complex */ 4725255332Scy/* loop inside ipf_nat_checkin() and lay it out properly in its own function. */ 4726145522Sdarrenr/* ------------------------------------------------------------------------ */ 4727255332Scystatic int 4728255332Scyipf_nat_match(fin, np) 4729255332Scy fr_info_t *fin; 4730255332Scy ipnat_t *np; 473160852Sdarrenr{ 4732255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 473360852Sdarrenr frtuc_t *ft; 4734255332Scy int match; 473560852Sdarrenr 4736255332Scy match = 0; 4737255332Scy switch (np->in_osrcatype) 4738255332Scy { 4739255332Scy case FRI_NORMAL : 4740255332Scy match = ((fin->fin_saddr & np->in_osrcmsk) != np->in_osrcaddr); 4741255332Scy break; 4742255332Scy case FRI_LOOKUP : 4743255332Scy match = (*np->in_osrcfunc)(softc, np->in_osrcptr, 4744255332Scy 4, &fin->fin_saddr, fin->fin_plen); 4745255332Scy break; 4746255332Scy } 4747255332Scy match ^= ((np->in_flags & IPN_NOTSRC) != 0); 4748255332Scy if (match) 474960852Sdarrenr return 0; 475060852Sdarrenr 4751255332Scy match = 0; 4752255332Scy switch (np->in_odstatype) 4753255332Scy { 4754255332Scy case FRI_NORMAL : 4755255332Scy match = ((fin->fin_daddr & np->in_odstmsk) != np->in_odstaddr); 4756255332Scy break; 4757255332Scy case FRI_LOOKUP : 4758255332Scy match = (*np->in_odstfunc)(softc, np->in_odstptr, 4759255332Scy 4, &fin->fin_daddr, fin->fin_plen); 4760255332Scy break; 4761255332Scy } 4762255332Scy 4763255332Scy match ^= ((np->in_flags & IPN_NOTDST) != 0); 4764255332Scy if (match) 476560852Sdarrenr return 0; 4766145522Sdarrenr 476760852Sdarrenr ft = &np->in_tuc; 4768145522Sdarrenr if (!(fin->fin_flx & FI_TCPUDP) || 4769145522Sdarrenr (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { 477060852Sdarrenr if (ft->ftu_scmp || ft->ftu_dcmp) 477160852Sdarrenr return 0; 477260852Sdarrenr return 1; 477360852Sdarrenr } 477460852Sdarrenr 4775255332Scy return ipf_tcpudpchk(&fin->fin_fi, ft); 477660852Sdarrenr} 477760852Sdarrenr 477860852Sdarrenr 4779145522Sdarrenr/* ------------------------------------------------------------------------ */ 4780255332Scy/* Function: ipf_nat_update */ 4781145522Sdarrenr/* Returns: Nil */ 4782255332Scy/* Parameters: fin(I) - pointer to packet information */ 4783255332Scy/* nat(I) - pointer to NAT structure */ 4784145522Sdarrenr/* */ 4785145522Sdarrenr/* Updates the lifetime of a NAT table entry for non-TCP packets. Must be */ 4786255332Scy/* called with fin_rev updated - i.e. after calling ipf_nat_proto(). */ 4787255332Scy/* */ 4788255332Scy/* This *MUST* be called after ipf_nat_proto() as it expects fin_rev to */ 4789255332Scy/* already be set. */ 4790145522Sdarrenr/* ------------------------------------------------------------------------ */ 4791255332Scyvoid 4792255332Scyipf_nat_update(fin, nat) 4793255332Scy fr_info_t *fin; 4794255332Scy nat_t *nat; 479553642Sguido{ 4796255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 4797255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 4798145522Sdarrenr ipftq_t *ifq, *ifq2; 4799145522Sdarrenr ipftqent_t *tqe; 4800255332Scy ipnat_t *np = nat->nat_ptr; 4801145522Sdarrenr 4802145522Sdarrenr tqe = &nat->nat_tqe; 4803145522Sdarrenr ifq = tqe->tqe_ifq; 4804145522Sdarrenr 4805145522Sdarrenr /* 4806145522Sdarrenr * We allow over-riding of NAT timeouts from NAT rules, even for 4807145522Sdarrenr * TCP, however, if it is TCP and there is no rule timeout set, 4808145522Sdarrenr * then do not update the timeout here. 4809145522Sdarrenr */ 4810255332Scy if (np != NULL) { 4811255332Scy np->in_bytes[fin->fin_rev] += fin->fin_plen; 4812145522Sdarrenr ifq2 = np->in_tqehead[fin->fin_rev]; 4813255332Scy } else { 4814145522Sdarrenr ifq2 = NULL; 4815255332Scy } 4816145522Sdarrenr 4817255332Scy if (nat->nat_pr[0] == IPPROTO_TCP && ifq2 == NULL) { 4818255332Scy (void) ipf_tcp_age(&nat->nat_tqe, fin, softn->ipf_nat_tcptq, 4819255332Scy 0, 2); 4820145522Sdarrenr } else { 4821145522Sdarrenr if (ifq2 == NULL) { 4822255332Scy if (nat->nat_pr[0] == IPPROTO_UDP) 4823255332Scy ifq2 = fin->fin_rev ? &softn->ipf_nat_udpacktq : 4824255332Scy &softn->ipf_nat_udptq; 4825255332Scy else if (nat->nat_pr[0] == IPPROTO_ICMP || 4826255332Scy nat->nat_pr[0] == IPPROTO_ICMPV6) 4827255332Scy ifq2 = fin->fin_rev ? &softn->ipf_nat_icmpacktq: 4828255332Scy &softn->ipf_nat_icmptq; 4829145522Sdarrenr else 4830255332Scy ifq2 = &softn->ipf_nat_iptq; 4831145522Sdarrenr } 4832145522Sdarrenr 4833255332Scy ipf_movequeue(softc->ipf_ticks, tqe, ifq, ifq2); 4834145522Sdarrenr } 4835145522Sdarrenr} 4836145522Sdarrenr 4837145522Sdarrenr 4838145522Sdarrenr/* ------------------------------------------------------------------------ */ 4839255332Scy/* Function: ipf_nat_checkout */ 4840145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 4841145522Sdarrenr/* 0 == no packet translation occurred, */ 4842145522Sdarrenr/* 1 == packet was successfully translated. */ 4843145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4844145522Sdarrenr/* passp(I) - pointer to filtering result flags */ 4845145522Sdarrenr/* */ 4846145522Sdarrenr/* Check to see if an outcoming packet should be changed. ICMP packets are */ 4847145522Sdarrenr/* first checked to see if they match an existing entry (if an error), */ 4848145522Sdarrenr/* otherwise a search of the current NAT table is made. If neither results */ 4849145522Sdarrenr/* in a match then a search for a matching NAT rule is made. Create a new */ 4850145522Sdarrenr/* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 4851145522Sdarrenr/* packet header(s) as required. */ 4852145522Sdarrenr/* ------------------------------------------------------------------------ */ 4853255332Scyint 4854255332Scyipf_nat_checkout(fin, passp) 4855255332Scy fr_info_t *fin; 4856255332Scy u_32_t *passp; 4857145522Sdarrenr{ 4858255332Scy ipnat_t *np = NULL, *npnext; 4859145522Sdarrenr struct ifnet *ifp, *sifp; 4860255332Scy ipf_main_softc_t *softc; 4861255332Scy ipf_nat_softc_t *softn; 4862145522Sdarrenr icmphdr_t *icmp = NULL; 486353642Sguido tcphdr_t *tcp = NULL; 4864145522Sdarrenr int rval, natfailed; 4865145522Sdarrenr u_int nflags = 0; 4866145522Sdarrenr u_32_t ipa, iph; 4867145522Sdarrenr int natadd = 1; 486853642Sguido frentry_t *fr; 486953642Sguido nat_t *nat; 487053642Sguido 4871255332Scy if (fin->fin_v == 6) { 4872255332Scy#ifdef USE_INET6 4873255332Scy return ipf_nat6_checkout(fin, passp); 4874255332Scy#else 487553642Sguido return 0; 4876255332Scy#endif 4877255332Scy } 487853642Sguido 4879255332Scy softc = fin->fin_main_soft; 4880255332Scy softn = softc->ipf_nat_soft; 4881255332Scy 4882255332Scy if (softn->ipf_nat_lock != 0) 4883255332Scy return 0; 4884255332Scy if (softn->ipf_nat_stats.ns_rules == 0 && 4885255332Scy softn->ipf_nat_instances == NULL) 4886255332Scy return 0; 4887255332Scy 4888145522Sdarrenr natfailed = 0; 4889145522Sdarrenr fr = fin->fin_fr; 4890145522Sdarrenr sifp = fin->fin_ifp; 4891170268Sdarrenr if (fr != NULL) { 4892255332Scy ifp = fr->fr_tifs[fin->fin_rev].fd_ptr; 4893170268Sdarrenr if ((ifp != NULL) && (ifp != (void *)-1)) 4894170268Sdarrenr fin->fin_ifp = ifp; 4895170268Sdarrenr } 489692685Sdarrenr ifp = fin->fin_ifp; 489753642Sguido 4898145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 4899145522Sdarrenr switch (fin->fin_p) 4900145522Sdarrenr { 4901145522Sdarrenr case IPPROTO_TCP : 490253642Sguido nflags = IPN_TCP; 4903145522Sdarrenr break; 4904145522Sdarrenr case IPPROTO_UDP : 490553642Sguido nflags = IPN_UDP; 4906145522Sdarrenr break; 4907145522Sdarrenr case IPPROTO_ICMP : 4908145522Sdarrenr icmp = fin->fin_dp; 4909145522Sdarrenr 4910145522Sdarrenr /* 4911145522Sdarrenr * This is an incoming packet, so the destination is 4912145522Sdarrenr * the icmp_id and the source port equals 0 4913145522Sdarrenr */ 4914255332Scy if ((fin->fin_flx & FI_ICMPQUERY) != 0) 4915145522Sdarrenr nflags = IPN_ICMPQUERY; 4916145522Sdarrenr break; 4917145522Sdarrenr default : 4918145522Sdarrenr break; 491953642Sguido } 4920255332Scy 4921145522Sdarrenr if ((nflags & IPN_TCPUDP)) 4922145522Sdarrenr tcp = fin->fin_dp; 492353642Sguido } 492453642Sguido 492592685Sdarrenr ipa = fin->fin_saddr; 492653642Sguido 4927255332Scy READ_ENTER(&softc->ipf_nat); 492860852Sdarrenr 4929255332Scy if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && 4930255332Scy (nat = ipf_nat_icmperror(fin, &nflags, NAT_OUTBOUND))) 4931145522Sdarrenr /*EMPTY*/; 4932255332Scy else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) 493353642Sguido natadd = 0; 4934255332Scy else if ((nat = ipf_nat_outlookup(fin, nflags|NAT_SEARCH, 4935255332Scy (u_int)fin->fin_p, fin->fin_src, 4936255332Scy fin->fin_dst))) { 493753642Sguido nflags = nat->nat_flags; 4938255332Scy } else if (fin->fin_off == 0) { 4939255332Scy u_32_t hv, msk, nmsk = 0; 494092685Sdarrenr 494153642Sguido /* 494253642Sguido * If there is no current entry in the nat table for this IP#, 494353642Sguido * create one for it (if there is a matching rule). 494453642Sguido */ 494553642Sguidomaskloop: 4946255332Scy msk = softn->ipf_nat_map_active_masks[nmsk]; 4947255332Scy iph = ipa & msk; 4948255332Scy hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_maprules_sz); 4949255332Scyretry_roundrobin: 4950255332Scy for (np = softn->ipf_nat_map_rules[hv]; np; np = npnext) { 4951255332Scy npnext = np->in_mnext; 4952161356Sguido if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) 495360852Sdarrenr continue; 4954255332Scy if (np->in_v[0] != 4) 495560852Sdarrenr continue; 4956255332Scy if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p)) 4957145522Sdarrenr continue; 4958255332Scy if ((np->in_flags & IPN_RF) && 4959255332Scy !(np->in_flags & nflags)) 4960145522Sdarrenr continue; 496160852Sdarrenr if (np->in_flags & IPN_FILTER) { 4962255332Scy switch (ipf_nat_match(fin, np)) 4963255332Scy { 4964255332Scy case 0 : 496560852Sdarrenr continue; 4966255332Scy case -1 : 4967337948Scy rval = -3; 4968255332Scy goto outmatchfail; 4969255332Scy case 1 : 4970255332Scy default : 4971255332Scy break; 4972255332Scy } 4973255332Scy } else if ((ipa & np->in_osrcmsk) != np->in_osrcaddr) 497460852Sdarrenr continue; 4975145522Sdarrenr 4976145522Sdarrenr if ((fr != NULL) && 4977255332Scy !ipf_matchtag(&np->in_tag, &fr->fr_nattag)) 497892685Sdarrenr continue; 4979145522Sdarrenr 4980255332Scy if (np->in_plabel != -1) { 4981145522Sdarrenr if (((np->in_flags & IPN_FILTER) == 0) && 4982255332Scy (np->in_odport != fin->fin_data[1])) 4983145522Sdarrenr continue; 4984255332Scy if (ipf_proxy_ok(fin, tcp, np) == 0) 4985145522Sdarrenr continue; 4986145522Sdarrenr } 4987145522Sdarrenr 4988255332Scy if (np->in_flags & IPN_NO) { 498992685Sdarrenr np->in_hits++; 499092685Sdarrenr break; 4991145522Sdarrenr } 4992255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 4993255332Scy /* 4994255332Scy * If we've matched a round-robin rule but it has 4995255332Scy * moved in the list since we got it, start over as 4996255332Scy * this is now no longer correct. 4997255332Scy */ 4998255332Scy if (npnext != np->in_mnext) { 4999255332Scy if ((np->in_flags & IPN_ROUNDR) != 0) { 5000255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 5001255332Scy goto retry_roundrobin; 5002255332Scy } 5003255332Scy npnext = np->in_mnext; 5004145522Sdarrenr } 5005255332Scy 5006255332Scy nat = ipf_nat_add(fin, np, NULL, nflags, NAT_OUTBOUND); 5007255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 5008255332Scy if (nat != NULL) { 5009255332Scy natfailed = 0; 5010255332Scy break; 5011255332Scy } 5012337948Scy natfailed = -2; 501353642Sguido } 5014255332Scy if ((np == NULL) && (nmsk < softn->ipf_nat_map_max)) { 5015255332Scy nmsk++; 5016255332Scy goto maskloop; 5017255332Scy } 501853642Sguido } 501953642Sguido 5020145522Sdarrenr if (nat != NULL) { 5021255332Scy rval = ipf_nat_out(fin, nat, natadd, nflags); 5022145522Sdarrenr if (rval == 1) { 5023145522Sdarrenr MUTEX_ENTER(&nat->nat_lock); 5024255332Scy ipf_nat_update(fin, nat); 5025255332Scy nat->nat_bytes[1] += fin->fin_plen; 5026255332Scy nat->nat_pkts[1]++; 5027255332Scy fin->fin_pktnum = nat->nat_pkts[1]; 5028145522Sdarrenr MUTEX_EXIT(&nat->nat_lock); 5029145522Sdarrenr } 5030145522Sdarrenr } else 5031145522Sdarrenr rval = natfailed; 5032255332Scyoutmatchfail: 5033255332Scy RWLOCK_EXIT(&softc->ipf_nat); 5034145522Sdarrenr 5035255332Scy switch (rval) 5036255332Scy { 5037337948Scy case -3 : 5038337948Scy /* ipf_nat_match() failure */ 5039337948Scy /* FALLTHROUGH */ 5040337948Scy case -2 : 5041337948Scy /* retry_roundrobin loop failure */ 5042337948Scy /* FALLTHROUGH */ 5043255332Scy case -1 : 5044337948Scy /* proxy failure detected by ipf_nat_out() */ 5045255332Scy if (passp != NULL) { 5046337948Scy DT2(frb_natv4out, fr_info_t *, fin, int, rval); 5047255332Scy NBUMPSIDED(1, ns_drop); 5048145522Sdarrenr *passp = FR_BLOCK; 5049255332Scy fin->fin_reason = FRB_NATV4; 5050255332Scy } 5051145522Sdarrenr fin->fin_flx |= FI_BADNAT; 5052255332Scy NBUMPSIDED(1, ns_badnat); 5053337948Scy rval = -1; /* We only return -1 on error. */ 5054255332Scy break; 5055255332Scy case 0 : 5056255332Scy NBUMPSIDE(1, ns_ignored); 5057255332Scy break; 5058255332Scy case 1 : 5059255332Scy NBUMPSIDE(1, ns_translated); 5060255332Scy break; 5061145522Sdarrenr } 5062145522Sdarrenr fin->fin_ifp = sifp; 5063145522Sdarrenr return rval; 5064145522Sdarrenr} 5065145522Sdarrenr 5066145522Sdarrenr/* ------------------------------------------------------------------------ */ 5067255332Scy/* Function: ipf_nat_out */ 5068145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 5069145522Sdarrenr/* 1 == packet was successfully translated. */ 5070145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 5071145522Sdarrenr/* nat(I) - pointer to NAT structure */ 5072145522Sdarrenr/* natadd(I) - flag indicating if it is safe to add frag cache */ 5073145522Sdarrenr/* nflags(I) - NAT flags set for this packet */ 5074145522Sdarrenr/* */ 5075145522Sdarrenr/* Translate a packet coming "out" on an interface. */ 5076145522Sdarrenr/* ------------------------------------------------------------------------ */ 5077255332Scyint 5078255332Scyipf_nat_out(fin, nat, natadd, nflags) 5079255332Scy fr_info_t *fin; 5080255332Scy nat_t *nat; 5081255332Scy int natadd; 5082255332Scy u_32_t nflags; 5083145522Sdarrenr{ 5084255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 5085255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 5086145522Sdarrenr icmphdr_t *icmp; 5087145522Sdarrenr tcphdr_t *tcp; 5088145522Sdarrenr ipnat_t *np; 5089255332Scy int skip; 5090145522Sdarrenr int i; 5091145522Sdarrenr 5092145522Sdarrenr tcp = NULL; 5093145522Sdarrenr icmp = NULL; 5094145522Sdarrenr np = nat->nat_ptr; 5095145522Sdarrenr 5096145522Sdarrenr if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL)) 5097255332Scy (void) ipf_frag_natnew(softc, fin, 0, nat); 5098145522Sdarrenr 509972006Sdarrenr /* 5100145522Sdarrenr * Fix up checksums, not by recalculating them, but 5101145522Sdarrenr * simply computing adjustments. 5102145522Sdarrenr * This is only done for STREAMS based IP implementations where the 5103145522Sdarrenr * checksum has already been calculated by IP. In all other cases, 5104145522Sdarrenr * IPFilter is called before the checksum needs calculating so there 5105145522Sdarrenr * is no call to modify whatever is in the header now. 510672006Sdarrenr */ 5107255332Scy if (nflags == IPN_ICMPERR) { 5108255332Scy u_32_t s1, s2, sumd, msumd; 510963523Sdarrenr 5110255332Scy s1 = LONG_SUM(ntohl(fin->fin_saddr)); 5111255332Scy if (nat->nat_dir == NAT_OUTBOUND) { 5112255332Scy s2 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); 5113255332Scy } else { 5114255332Scy s2 = LONG_SUM(ntohl(nat->nat_odstaddr)); 511563523Sdarrenr } 5116255332Scy CALC_SUMD(s1, s2, sumd); 5117255332Scy msumd = sumd; 5118255332Scy 5119255332Scy s1 = LONG_SUM(ntohl(fin->fin_daddr)); 5120255332Scy if (nat->nat_dir == NAT_OUTBOUND) { 5121255332Scy s2 = LONG_SUM(ntohl(nat->nat_ndstaddr)); 5122255332Scy } else { 5123255332Scy s2 = LONG_SUM(ntohl(nat->nat_osrcaddr)); 5124255332Scy } 5125255332Scy CALC_SUMD(s1, s2, sumd); 5126255332Scy msumd += sumd; 5127255332Scy 5128255332Scy ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, msumd, 0); 5129255332Scy } 5130153876Sguido#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 5131292811Scy defined(linux) || defined(BRIDGE_IPF) || defined(__FreeBSD__) 5132255332Scy else { 5133255332Scy /* 5134255332Scy * Strictly speaking, this isn't necessary on BSD 5135255332Scy * kernels because they do checksum calculation after 5136255332Scy * this code has run BUT if ipfilter is being used 5137255332Scy * to do NAT as a bridge, that code doesn't exist. 5138255332Scy */ 5139255332Scy switch (nat->nat_dir) 5140255332Scy { 5141255332Scy case NAT_OUTBOUND : 5142255332Scy ipf_fix_outcksum(fin->fin_cksum & FI_CK_L4PART, 5143255332Scy &fin->fin_ip->ip_sum, 5144255332Scy nat->nat_ipsumd, 0); 5145255332Scy break; 5146255332Scy 5147255332Scy case NAT_INBOUND : 5148255332Scy ipf_fix_incksum(fin->fin_cksum & FI_CK_L4PART, 5149255332Scy &fin->fin_ip->ip_sum, 5150255332Scy nat->nat_ipsumd, 0); 5151255332Scy break; 5152255332Scy 5153255332Scy default : 5154255332Scy break; 515563523Sdarrenr } 5156255332Scy } 515753642Sguido#endif 5158255332Scy 5159255332Scy /* 5160255332Scy * Address assignment is after the checksum modification because 5161255332Scy * we are using the address in the packet for determining the 5162255332Scy * correct checksum offset (the ICMP error could be coming from 5163255332Scy * anyone...) 5164255332Scy */ 5165255332Scy switch (nat->nat_dir) 5166255332Scy { 5167255332Scy case NAT_OUTBOUND : 5168255332Scy fin->fin_ip->ip_src = nat->nat_nsrcip; 5169255332Scy fin->fin_saddr = nat->nat_nsrcaddr; 5170255332Scy fin->fin_ip->ip_dst = nat->nat_ndstip; 5171255332Scy fin->fin_daddr = nat->nat_ndstaddr; 5172255332Scy break; 5173255332Scy 5174255332Scy case NAT_INBOUND : 5175255332Scy fin->fin_ip->ip_src = nat->nat_odstip; 5176255332Scy fin->fin_saddr = nat->nat_ndstaddr; 5177255332Scy fin->fin_ip->ip_dst = nat->nat_osrcip; 5178255332Scy fin->fin_daddr = nat->nat_nsrcaddr; 5179255332Scy break; 5180255332Scy 5181255332Scy case NAT_DIVERTIN : 5182255332Scy { 5183255332Scy mb_t *m; 5184255332Scy 5185255332Scy skip = ipf_nat_decap(fin, nat); 5186255332Scy if (skip <= 0) { 5187255332Scy NBUMPSIDED(1, ns_decap_fail); 5188255332Scy return -1; 5189255332Scy } 5190255332Scy 5191255332Scy m = fin->fin_m; 5192255332Scy 5193255332Scy#if defined(MENTAT) && defined(_KERNEL) 5194255332Scy m->b_rptr += skip; 5195255332Scy#else 5196255332Scy m->m_data += skip; 5197255332Scy m->m_len -= skip; 5198255332Scy 5199255332Scy# ifdef M_PKTHDR 5200255332Scy if (m->m_flags & M_PKTHDR) 5201255332Scy m->m_pkthdr.len -= skip; 5202255332Scy# endif 5203255332Scy#endif 5204255332Scy 5205255332Scy MUTEX_ENTER(&nat->nat_lock); 5206255332Scy ipf_nat_update(fin, nat); 5207255332Scy MUTEX_EXIT(&nat->nat_lock); 5208255332Scy fin->fin_flx |= FI_NATED; 5209255332Scy if (np != NULL && np->in_tag.ipt_num[0] != 0) 5210255332Scy fin->fin_nattag = &np->in_tag; 5211255332Scy return 1; 5212255332Scy /* NOTREACHED */ 5213255332Scy } 5214255332Scy 5215255332Scy case NAT_DIVERTOUT : 5216255332Scy { 5217255332Scy u_32_t s1, s2, sumd; 5218255332Scy udphdr_t *uh; 5219255332Scy ip_t *ip; 5220255332Scy mb_t *m; 5221255332Scy 5222255332Scy m = M_DUP(np->in_divmp); 5223255332Scy if (m == NULL) { 5224255332Scy NBUMPSIDED(1, ns_divert_dup); 5225255332Scy return -1; 5226255332Scy } 5227255332Scy 5228255332Scy ip = MTOD(m, ip_t *); 5229255332Scy ip->ip_id = htons(ipf_nextipid(fin)); 5230255332Scy s2 = ntohs(ip->ip_id); 5231255332Scy 5232255332Scy s1 = ip->ip_len; 5233255332Scy ip->ip_len = ntohs(ip->ip_len); 5234255332Scy ip->ip_len += fin->fin_plen; 5235255332Scy ip->ip_len = htons(ip->ip_len); 5236255332Scy s2 += ntohs(ip->ip_len); 5237255332Scy CALC_SUMD(s1, s2, sumd); 5238255332Scy 5239255332Scy uh = (udphdr_t *)(ip + 1); 5240255332Scy uh->uh_ulen += fin->fin_plen; 5241255332Scy uh->uh_ulen = htons(uh->uh_ulen); 5242255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 5243292979Scy defined(linux) || defined(BRIDGE_IPF) || defined(__FreeBSD__) 5244255332Scy ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0); 5245255332Scy#endif 5246255332Scy 5247255332Scy PREP_MB_T(fin, m); 5248255332Scy 5249255332Scy fin->fin_src = ip->ip_src; 5250255332Scy fin->fin_dst = ip->ip_dst; 5251255332Scy fin->fin_ip = ip; 5252255332Scy fin->fin_plen += sizeof(ip_t) + 8; /* UDP + IPv4 hdr */ 5253255332Scy fin->fin_dlen += sizeof(ip_t) + 8; /* UDP + IPv4 hdr */ 5254255332Scy 5255255332Scy nflags &= ~IPN_TCPUDPICMP; 5256255332Scy 5257255332Scy break; 5258255332Scy } 5259255332Scy 5260255332Scy default : 5261255332Scy break; 5262145522Sdarrenr } 526353642Sguido 5264145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 5265255332Scy u_short *csump; 5266255332Scy 5267255332Scy if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) { 5268145522Sdarrenr tcp = fin->fin_dp; 526953642Sguido 5270255332Scy switch (nat->nat_dir) 5271255332Scy { 5272255332Scy case NAT_OUTBOUND : 5273255332Scy tcp->th_sport = nat->nat_nsport; 5274255332Scy fin->fin_data[0] = ntohs(nat->nat_nsport); 5275255332Scy tcp->th_dport = nat->nat_ndport; 5276255332Scy fin->fin_data[1] = ntohs(nat->nat_ndport); 5277255332Scy break; 5278255332Scy 5279255332Scy case NAT_INBOUND : 5280255332Scy tcp->th_sport = nat->nat_odport; 5281255332Scy fin->fin_data[0] = ntohs(nat->nat_odport); 5282255332Scy tcp->th_dport = nat->nat_osport; 5283255332Scy fin->fin_data[1] = ntohs(nat->nat_osport); 5284255332Scy break; 5285255332Scy } 5286145522Sdarrenr } 528753642Sguido 5288255332Scy if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) { 5289145522Sdarrenr icmp = fin->fin_dp; 5290255332Scy icmp->icmp_id = nat->nat_nicmpid; 5291145522Sdarrenr } 5292110916Sdarrenr 5293255332Scy csump = ipf_nat_proto(fin, nat, nflags); 5294255332Scy 5295255332Scy /* 5296255332Scy * The above comments do not hold for layer 4 (or higher) 5297255332Scy * checksums... 5298255332Scy */ 5299255332Scy if (csump != NULL) { 5300255332Scy if (nat->nat_dir == NAT_OUTBOUND) 5301255332Scy ipf_fix_outcksum(fin->fin_cksum, csump, 5302255332Scy nat->nat_sumd[0], 5303255332Scy nat->nat_sumd[1] + 5304255332Scy fin->fin_dlen); 5305255332Scy else 5306255332Scy ipf_fix_incksum(fin->fin_cksum, csump, 5307255332Scy nat->nat_sumd[0], 5308255332Scy nat->nat_sumd[1] + 5309255332Scy fin->fin_dlen); 5310255332Scy } 5311145522Sdarrenr } 5312110916Sdarrenr 5313255332Scy ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); 5314145522Sdarrenr /* ------------------------------------------------------------- */ 5315255332Scy /* A few quick notes: */ 5316255332Scy /* Following are test conditions prior to calling the */ 5317255332Scy /* ipf_proxy_check routine. */ 5318255332Scy /* */ 5319255332Scy /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 5320255332Scy /* with a redirect rule, we attempt to match the packet's */ 5321255332Scy /* source port against in_dport, otherwise we'd compare the */ 5322255332Scy /* packet's destination. */ 5323145522Sdarrenr /* ------------------------------------------------------------- */ 5324145522Sdarrenr if ((np != NULL) && (np->in_apr != NULL)) { 5325255332Scy i = ipf_proxy_check(fin, nat); 5326255332Scy if (i == 0) { 532760852Sdarrenr i = 1; 5328255332Scy } else if (i == -1) { 5329255332Scy NBUMPSIDED(1, ns_ipf_proxy_fail); 5330255332Scy } 5331255332Scy } else { 5332145522Sdarrenr i = 1; 5333255332Scy } 5334145522Sdarrenr fin->fin_flx |= FI_NATED; 5335145522Sdarrenr return i; 533653642Sguido} 533753642Sguido 533853642Sguido 5339145522Sdarrenr/* ------------------------------------------------------------------------ */ 5340255332Scy/* Function: ipf_nat_checkin */ 5341145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 5342145522Sdarrenr/* 0 == no packet translation occurred, */ 5343145522Sdarrenr/* 1 == packet was successfully translated. */ 5344145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 5345145522Sdarrenr/* passp(I) - pointer to filtering result flags */ 5346145522Sdarrenr/* */ 5347145522Sdarrenr/* Check to see if an incoming packet should be changed. ICMP packets are */ 5348145522Sdarrenr/* first checked to see if they match an existing entry (if an error), */ 5349145522Sdarrenr/* otherwise a search of the current NAT table is made. If neither results */ 5350145522Sdarrenr/* in a match then a search for a matching NAT rule is made. Create a new */ 5351145522Sdarrenr/* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 5352145522Sdarrenr/* packet header(s) as required. */ 5353145522Sdarrenr/* ------------------------------------------------------------------------ */ 5354255332Scyint 5355255332Scyipf_nat_checkin(fin, passp) 5356255332Scy fr_info_t *fin; 5357255332Scy u_32_t *passp; 535853642Sguido{ 5359255332Scy ipf_main_softc_t *softc; 5360255332Scy ipf_nat_softc_t *softn; 5361145522Sdarrenr u_int nflags, natadd; 5362255332Scy ipnat_t *np, *npnext; 5363145522Sdarrenr int rval, natfailed; 5364145522Sdarrenr struct ifnet *ifp; 5365145522Sdarrenr struct in_addr in; 5366145522Sdarrenr icmphdr_t *icmp; 5367145522Sdarrenr tcphdr_t *tcp; 5368145522Sdarrenr u_short dport; 536953642Sguido nat_t *nat; 537053642Sguido u_32_t iph; 537153642Sguido 5372255332Scy softc = fin->fin_main_soft; 5373255332Scy softn = softc->ipf_nat_soft; 5374255332Scy 5375255332Scy if (softn->ipf_nat_lock != 0) 537653642Sguido return 0; 5377255332Scy if (softn->ipf_nat_stats.ns_rules == 0 && 5378255332Scy softn->ipf_nat_instances == NULL) 5379255332Scy return 0; 538053642Sguido 5381145522Sdarrenr tcp = NULL; 5382145522Sdarrenr icmp = NULL; 5383145522Sdarrenr dport = 0; 5384145522Sdarrenr natadd = 1; 5385145522Sdarrenr nflags = 0; 5386145522Sdarrenr natfailed = 0; 5387145522Sdarrenr ifp = fin->fin_ifp; 5388145522Sdarrenr 5389145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 5390145522Sdarrenr switch (fin->fin_p) 5391145522Sdarrenr { 5392145522Sdarrenr case IPPROTO_TCP : 539353642Sguido nflags = IPN_TCP; 5394145522Sdarrenr break; 5395145522Sdarrenr case IPPROTO_UDP : 539653642Sguido nflags = IPN_UDP; 5397145522Sdarrenr break; 5398145522Sdarrenr case IPPROTO_ICMP : 5399145522Sdarrenr icmp = fin->fin_dp; 5400145522Sdarrenr 5401145522Sdarrenr /* 5402145522Sdarrenr * This is an incoming packet, so the destination is 5403145522Sdarrenr * the icmp_id and the source port equals 0 5404145522Sdarrenr */ 5405255332Scy if ((fin->fin_flx & FI_ICMPQUERY) != 0) { 5406145522Sdarrenr nflags = IPN_ICMPQUERY; 5407255332Scy dport = icmp->icmp_id; 5408145522Sdarrenr } break; 5409145522Sdarrenr default : 5410145522Sdarrenr break; 5411145522Sdarrenr } 5412255332Scy 541353642Sguido if ((nflags & IPN_TCPUDP)) { 5414145522Sdarrenr tcp = fin->fin_dp; 5415255332Scy dport = fin->fin_data[1]; 541653642Sguido } 541753642Sguido } 541853642Sguido 541992685Sdarrenr in = fin->fin_dst; 542053642Sguido 5421255332Scy READ_ENTER(&softc->ipf_nat); 542253642Sguido 5423255332Scy if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && 5424255332Scy (nat = ipf_nat_icmperror(fin, &nflags, NAT_INBOUND))) 5425145522Sdarrenr /*EMPTY*/; 5426255332Scy else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) 542753642Sguido natadd = 0; 5428255332Scy else if ((nat = ipf_nat_inlookup(fin, nflags|NAT_SEARCH, 5429255332Scy (u_int)fin->fin_p, 5430255332Scy fin->fin_src, in))) { 543153642Sguido nflags = nat->nat_flags; 5432255332Scy } else if (fin->fin_off == 0) { 5433255332Scy u_32_t hv, msk, rmsk = 0; 5434145522Sdarrenr 543553642Sguido /* 543653642Sguido * If there is no current entry in the nat table for this IP#, 543753642Sguido * create one for it (if there is a matching rule). 543853642Sguido */ 543953642Sguidomaskloop: 5440255332Scy msk = softn->ipf_nat_rdr_active_masks[rmsk]; 5441255332Scy iph = in.s_addr & msk; 5442255332Scy hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_rdrrules_sz); 5443255332Scyretry_roundrobin: 5444255332Scy /* TRACE (iph,msk,rmsk,hv,softn->ipf_nat_rdrrules_sz) */ 5445255332Scy for (np = softn->ipf_nat_rdr_rules[hv]; np; np = npnext) { 5446255332Scy npnext = np->in_rnext; 5447145522Sdarrenr if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) 544860852Sdarrenr continue; 5449255332Scy if (np->in_v[0] != 4) 5450138947Sdarrenr continue; 5451255332Scy if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p)) 5452145522Sdarrenr continue; 5453145522Sdarrenr if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) 5454145522Sdarrenr continue; 545560852Sdarrenr if (np->in_flags & IPN_FILTER) { 5456255332Scy switch (ipf_nat_match(fin, np)) 5457255332Scy { 5458255332Scy case 0 : 545960852Sdarrenr continue; 5460255332Scy case -1 : 5461337948Scy rval = -3; 5462255332Scy goto inmatchfail; 5463255332Scy case 1 : 5464255332Scy default : 5465255332Scy break; 5466255332Scy } 5467145522Sdarrenr } else { 5468255332Scy if ((in.s_addr & np->in_odstmsk) != 5469255332Scy np->in_odstaddr) 5470145522Sdarrenr continue; 5471255332Scy if (np->in_odport && 5472255332Scy ((np->in_dtop < dport) || 5473255332Scy (dport < np->in_odport))) 5474145522Sdarrenr continue; 5475145522Sdarrenr } 5476145522Sdarrenr 5477255332Scy if (np->in_plabel != -1) { 5478255332Scy if (!ipf_proxy_ok(fin, tcp, np)) { 5479145522Sdarrenr continue; 548053642Sguido } 5481145522Sdarrenr } 5482145522Sdarrenr 5483255332Scy if (np->in_flags & IPN_NO) { 5484145522Sdarrenr np->in_hits++; 5485145522Sdarrenr break; 5486255332Scy } 548760852Sdarrenr 5488255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 5489255332Scy /* 5490255332Scy * If we've matched a round-robin rule but it has 5491255332Scy * moved in the list since we got it, start over as 5492255332Scy * this is now no longer correct. 5493255332Scy */ 5494255332Scy if (npnext != np->in_rnext) { 5495255332Scy if ((np->in_flags & IPN_ROUNDR) != 0) { 5496255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 5497255332Scy goto retry_roundrobin; 5498255332Scy } 5499255332Scy npnext = np->in_rnext; 5500145522Sdarrenr } 5501255332Scy 5502255332Scy nat = ipf_nat_add(fin, np, NULL, nflags, NAT_INBOUND); 5503255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 5504255332Scy if (nat != NULL) { 5505255332Scy natfailed = 0; 5506255332Scy break; 5507145522Sdarrenr } 5508337948Scy natfailed = -2; 550953642Sguido } 5510255332Scy if ((np == NULL) && (rmsk < softn->ipf_nat_rdr_max)) { 5511255332Scy rmsk++; 5512255332Scy goto maskloop; 5513255332Scy } 551453642Sguido } 5515255332Scy 5516145522Sdarrenr if (nat != NULL) { 5517255332Scy rval = ipf_nat_in(fin, nat, natadd, nflags); 5518145522Sdarrenr if (rval == 1) { 5519145522Sdarrenr MUTEX_ENTER(&nat->nat_lock); 5520255332Scy ipf_nat_update(fin, nat); 5521255332Scy nat->nat_bytes[0] += fin->fin_plen; 5522255332Scy nat->nat_pkts[0]++; 5523255332Scy fin->fin_pktnum = nat->nat_pkts[0]; 5524145522Sdarrenr MUTEX_EXIT(&nat->nat_lock); 5525145522Sdarrenr } 5526145522Sdarrenr } else 5527145522Sdarrenr rval = natfailed; 5528255332Scyinmatchfail: 5529255332Scy RWLOCK_EXIT(&softc->ipf_nat); 553072006Sdarrenr 5531255332Scy switch (rval) 5532255332Scy { 5533337948Scy case -3 : 5534337948Scy /* ipf_nat_match() failure */ 5535337948Scy /* FALLTHROUGH */ 5536337948Scy case -2 : 5537337948Scy /* retry_roundrobin loop failure */ 5538337948Scy /* FALLTHROUGH */ 5539255332Scy case -1 : 5540337948Scy /* proxy failure detected by ipf_nat_in() */ 5541255332Scy if (passp != NULL) { 5542337948Scy DT2(frb_natv4in, fr_info_t *, fin, int, rval); 5543255332Scy NBUMPSIDED(0, ns_drop); 5544145522Sdarrenr *passp = FR_BLOCK; 5545255332Scy fin->fin_reason = FRB_NATV4; 5546255332Scy } 5547145522Sdarrenr fin->fin_flx |= FI_BADNAT; 5548255332Scy NBUMPSIDED(0, ns_badnat); 5549337948Scy rval = -1; /* We only return -1 on error. */ 5550255332Scy break; 5551255332Scy case 0 : 5552255332Scy NBUMPSIDE(0, ns_ignored); 5553255332Scy break; 5554255332Scy case 1 : 5555255332Scy NBUMPSIDE(0, ns_translated); 5556255332Scy break; 5557145522Sdarrenr } 5558145522Sdarrenr return rval; 5559145522Sdarrenr} 5560145522Sdarrenr 5561145522Sdarrenr 5562145522Sdarrenr/* ------------------------------------------------------------------------ */ 5563255332Scy/* Function: ipf_nat_in */ 5564145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 5565145522Sdarrenr/* 1 == packet was successfully translated. */ 5566145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 5567145522Sdarrenr/* nat(I) - pointer to NAT structure */ 5568145522Sdarrenr/* natadd(I) - flag indicating if it is safe to add frag cache */ 5569145522Sdarrenr/* nflags(I) - NAT flags set for this packet */ 5570255332Scy/* Locks Held: ipf_nat(READ) */ 5571145522Sdarrenr/* */ 5572145522Sdarrenr/* Translate a packet coming "in" on an interface. */ 5573145522Sdarrenr/* ------------------------------------------------------------------------ */ 5574255332Scyint 5575255332Scyipf_nat_in(fin, nat, natadd, nflags) 5576255332Scy fr_info_t *fin; 5577255332Scy nat_t *nat; 5578255332Scy int natadd; 5579255332Scy u_32_t nflags; 5580145522Sdarrenr{ 5581255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 5582255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 5583255332Scy u_32_t sumd, ipsumd, sum1, sum2; 5584145522Sdarrenr icmphdr_t *icmp; 5585145522Sdarrenr tcphdr_t *tcp; 5586145522Sdarrenr ipnat_t *np; 5587255332Scy int skip; 5588145522Sdarrenr int i; 5589145522Sdarrenr 5590145522Sdarrenr tcp = NULL; 5591145522Sdarrenr np = nat->nat_ptr; 5592145522Sdarrenr fin->fin_fr = nat->nat_fr; 5593145522Sdarrenr 5594145522Sdarrenr if (np != NULL) { 5595145522Sdarrenr if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) 5596255332Scy (void) ipf_frag_natnew(softc, fin, 0, nat); 5597145522Sdarrenr 5598145522Sdarrenr /* ------------------------------------------------------------- */ 5599255332Scy /* A few quick notes: */ 5600255332Scy /* Following are test conditions prior to calling the */ 5601255332Scy /* ipf_proxy_check routine. */ 5602255332Scy /* */ 5603255332Scy /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 5604255332Scy /* with a map rule, we attempt to match the packet's */ 5605255332Scy /* source port against in_dport, otherwise we'd compare the */ 5606255332Scy /* packet's destination. */ 5607145522Sdarrenr /* ------------------------------------------------------------- */ 5608145522Sdarrenr if (np->in_apr != NULL) { 5609255332Scy i = ipf_proxy_check(fin, nat); 561060852Sdarrenr if (i == -1) { 5611255332Scy NBUMPSIDED(0, ns_ipf_proxy_fail); 5612145522Sdarrenr return -1; 561360852Sdarrenr } 561460852Sdarrenr } 5615145522Sdarrenr } 561653642Sguido 5617255332Scy ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); 5618145522Sdarrenr 5619255332Scy ipsumd = nat->nat_ipsumd; 5620145522Sdarrenr /* 5621145522Sdarrenr * Fix up checksums, not by recalculating them, but 5622145522Sdarrenr * simply computing adjustments. 5623145522Sdarrenr * Why only do this for some platforms on inbound packets ? 5624145522Sdarrenr * Because for those that it is done, IP processing is yet to happen 5625145522Sdarrenr * and so the IPv4 header checksum has not yet been evaluated. 5626145522Sdarrenr * Perhaps it should always be done for the benefit of things like 5627145522Sdarrenr * fast forwarding (so that it doesn't need to be recomputed) but with 5628145522Sdarrenr * header checksum offloading, perhaps it is a moot point. 5629145522Sdarrenr */ 5630255332Scy 5631255332Scy switch (nat->nat_dir) 5632255332Scy { 5633255332Scy case NAT_INBOUND : 5634255332Scy if ((fin->fin_flx & FI_ICMPERR) == 0) { 5635255332Scy fin->fin_ip->ip_src = nat->nat_nsrcip; 5636255332Scy fin->fin_saddr = nat->nat_nsrcaddr; 5637255332Scy } else { 5638255332Scy sum1 = nat->nat_osrcaddr; 5639255332Scy sum2 = nat->nat_nsrcaddr; 5640255332Scy CALC_SUMD(sum1, sum2, sumd); 5641255332Scy ipsumd -= sumd; 5642255332Scy } 5643255332Scy fin->fin_ip->ip_dst = nat->nat_ndstip; 5644255332Scy fin->fin_daddr = nat->nat_ndstaddr; 5645145522Sdarrenr#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 5646145522Sdarrenr defined(__osf__) || defined(linux) 5647255332Scy ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, ipsumd, 0); 5648145522Sdarrenr#endif 5649255332Scy break; 5650145522Sdarrenr 5651255332Scy case NAT_OUTBOUND : 5652255332Scy if ((fin->fin_flx & FI_ICMPERR) == 0) { 5653255332Scy fin->fin_ip->ip_src = nat->nat_odstip; 5654255332Scy fin->fin_saddr = nat->nat_odstaddr; 5655255332Scy } else { 5656255332Scy sum1 = nat->nat_odstaddr; 5657255332Scy sum2 = nat->nat_ndstaddr; 5658255332Scy CALC_SUMD(sum1, sum2, sumd); 5659255332Scy ipsumd -= sumd; 5660255332Scy } 5661255332Scy fin->fin_ip->ip_dst = nat->nat_osrcip; 5662255332Scy fin->fin_daddr = nat->nat_osrcaddr; 5663255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 5664255332Scy defined(__osf__) || defined(linux) 5665255332Scy ipf_fix_incksum(0, &fin->fin_ip->ip_sum, ipsumd, 0); 5666255332Scy#endif 5667255332Scy break; 5668255332Scy 5669255332Scy case NAT_DIVERTIN : 5670255332Scy { 5671255332Scy udphdr_t *uh; 5672255332Scy ip_t *ip; 5673255332Scy mb_t *m; 5674255332Scy 5675255332Scy m = M_DUP(np->in_divmp); 5676255332Scy if (m == NULL) { 5677255332Scy NBUMPSIDED(0, ns_divert_dup); 5678255332Scy return -1; 5679255332Scy } 5680255332Scy 5681255332Scy ip = MTOD(m, ip_t *); 5682255332Scy ip->ip_id = htons(ipf_nextipid(fin)); 5683255332Scy sum1 = ntohs(ip->ip_len); 5684255332Scy ip->ip_len = ntohs(ip->ip_len); 5685255332Scy ip->ip_len += fin->fin_plen; 5686255332Scy ip->ip_len = htons(ip->ip_len); 5687255332Scy 5688255332Scy uh = (udphdr_t *)(ip + 1); 5689255332Scy uh->uh_ulen += fin->fin_plen; 5690255332Scy uh->uh_ulen = htons(uh->uh_ulen); 5691255332Scy 5692255332Scy sum2 = ntohs(ip->ip_id) + ntohs(ip->ip_len); 5693255332Scy sum2 += ntohs(ip->ip_off) & IP_DF; 5694255332Scy CALC_SUMD(sum1, sum2, sumd); 5695255332Scy 5696255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 5697255332Scy defined(__osf__) || defined(linux) 5698255332Scy ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0); 5699255332Scy#endif 5700255332Scy PREP_MB_T(fin, m); 5701255332Scy 5702255332Scy fin->fin_ip = ip; 5703255332Scy fin->fin_plen += sizeof(ip_t) + 8; /* UDP + new IPv4 hdr */ 5704255332Scy fin->fin_dlen += sizeof(ip_t) + 8; /* UDP + old IPv4 hdr */ 5705255332Scy 5706255332Scy nflags &= ~IPN_TCPUDPICMP; 5707255332Scy 5708255332Scy break; 5709255332Scy } 5710255332Scy 5711255332Scy case NAT_DIVERTOUT : 5712255332Scy { 5713255332Scy mb_t *m; 5714255332Scy 5715255332Scy skip = ipf_nat_decap(fin, nat); 5716255332Scy if (skip <= 0) { 5717255332Scy NBUMPSIDED(0, ns_decap_fail); 5718255332Scy return -1; 5719255332Scy } 5720255332Scy 5721255332Scy m = fin->fin_m; 5722255332Scy 5723255332Scy#if defined(MENTAT) && defined(_KERNEL) 5724255332Scy m->b_rptr += skip; 5725255332Scy#else 5726255332Scy m->m_data += skip; 5727255332Scy m->m_len -= skip; 5728255332Scy 5729255332Scy# ifdef M_PKTHDR 5730255332Scy if (m->m_flags & M_PKTHDR) 5731255332Scy m->m_pkthdr.len -= skip; 5732255332Scy# endif 5733255332Scy#endif 5734255332Scy 5735255332Scy ipf_nat_update(fin, nat); 5736255332Scy nflags &= ~IPN_TCPUDPICMP; 5737255332Scy fin->fin_flx |= FI_NATED; 5738255332Scy if (np != NULL && np->in_tag.ipt_num[0] != 0) 5739255332Scy fin->fin_nattag = &np->in_tag; 5740255332Scy return 1; 5741255332Scy /* NOTREACHED */ 5742255332Scy } 5743255332Scy } 5744255332Scy if (nflags & IPN_TCPUDP) 5745255332Scy tcp = fin->fin_dp; 5746255332Scy 5747145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 5748255332Scy u_short *csump; 5749255332Scy 5750255332Scy if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) { 5751255332Scy switch (nat->nat_dir) 5752255332Scy { 5753255332Scy case NAT_INBOUND : 5754255332Scy tcp->th_sport = nat->nat_nsport; 5755255332Scy fin->fin_data[0] = ntohs(nat->nat_nsport); 5756255332Scy tcp->th_dport = nat->nat_ndport; 5757255332Scy fin->fin_data[1] = ntohs(nat->nat_ndport); 5758255332Scy break; 5759255332Scy 5760255332Scy case NAT_OUTBOUND : 5761255332Scy tcp->th_sport = nat->nat_odport; 5762255332Scy fin->fin_data[0] = ntohs(nat->nat_odport); 5763255332Scy tcp->th_dport = nat->nat_osport; 5764255332Scy fin->fin_data[1] = ntohs(nat->nat_osport); 5765255332Scy break; 5766255332Scy } 576792685Sdarrenr } 576853642Sguido 5769145522Sdarrenr 5770255332Scy if ((nat->nat_odport != 0) && (nflags & IPN_ICMPQUERY)) { 5771145522Sdarrenr icmp = fin->fin_dp; 5772145522Sdarrenr 5773255332Scy icmp->icmp_id = nat->nat_nicmpid; 5774145522Sdarrenr } 5775145522Sdarrenr 5776255332Scy csump = ipf_nat_proto(fin, nat, nflags); 5777255332Scy 5778255332Scy /* 5779255332Scy * The above comments do not hold for layer 4 (or higher) 5780255332Scy * checksums... 5781255332Scy */ 5782255332Scy if (csump != NULL) { 5783255332Scy if (nat->nat_dir == NAT_OUTBOUND) 5784255332Scy ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0); 5785255332Scy else 5786255332Scy ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0); 5787255332Scy } 5788145522Sdarrenr } 5789145522Sdarrenr 5790145522Sdarrenr fin->fin_flx |= FI_NATED; 5791145522Sdarrenr if (np != NULL && np->in_tag.ipt_num[0] != 0) 5792145522Sdarrenr fin->fin_nattag = &np->in_tag; 5793145522Sdarrenr return 1; 5794145522Sdarrenr} 5795130886Sdarrenr 5796130886Sdarrenr 5797145522Sdarrenr/* ------------------------------------------------------------------------ */ 5798255332Scy/* Function: ipf_nat_proto */ 5799145522Sdarrenr/* Returns: u_short* - pointer to transport header checksum to update, */ 5800145522Sdarrenr/* NULL if the transport protocol is not recognised */ 5801145522Sdarrenr/* as needing a checksum update. */ 5802145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 5803145522Sdarrenr/* nat(I) - pointer to NAT structure */ 5804145522Sdarrenr/* nflags(I) - NAT flags set for this packet */ 5805145522Sdarrenr/* */ 5806145522Sdarrenr/* Return the pointer to the checksum field for each protocol so understood.*/ 5807145522Sdarrenr/* If support for making other changes to a protocol header is required, */ 5808145522Sdarrenr/* that is not strictly 'address' translation, such as clamping the MSS in */ 5809145522Sdarrenr/* TCP down to a specific value, then do it from here. */ 5810145522Sdarrenr/* ------------------------------------------------------------------------ */ 5811255332Scyu_short * 5812255332Scyipf_nat_proto(fin, nat, nflags) 5813255332Scy fr_info_t *fin; 5814255332Scy nat_t *nat; 5815255332Scy u_int nflags; 5816145522Sdarrenr{ 5817145522Sdarrenr icmphdr_t *icmp; 5818145522Sdarrenr u_short *csump; 5819145522Sdarrenr tcphdr_t *tcp; 5820145522Sdarrenr udphdr_t *udp; 582153642Sguido 5822145522Sdarrenr csump = NULL; 5823145522Sdarrenr if (fin->fin_out == 0) { 5824255332Scy fin->fin_rev = (nat->nat_dir & NAT_OUTBOUND); 5825145522Sdarrenr } else { 5826255332Scy fin->fin_rev = ((nat->nat_dir & NAT_OUTBOUND) == 0); 5827145522Sdarrenr } 582853642Sguido 5829145522Sdarrenr switch (fin->fin_p) 5830145522Sdarrenr { 5831145522Sdarrenr case IPPROTO_TCP : 5832145522Sdarrenr tcp = fin->fin_dp; 5833110916Sdarrenr 5834255332Scy if ((nflags & IPN_TCP) != 0) 5835255332Scy csump = &tcp->th_sum; 583653642Sguido 5837145522Sdarrenr /* 5838145522Sdarrenr * Do a MSS CLAMPING on a SYN packet, 5839145522Sdarrenr * only deal IPv4 for now. 5840145522Sdarrenr */ 5841145522Sdarrenr if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0) 5842255332Scy ipf_nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump); 584360852Sdarrenr 5844145522Sdarrenr break; 5845145522Sdarrenr 5846145522Sdarrenr case IPPROTO_UDP : 5847145522Sdarrenr udp = fin->fin_dp; 5848145522Sdarrenr 5849255332Scy if ((nflags & IPN_UDP) != 0) { 5850255332Scy if (udp->uh_sum != 0) 5851255332Scy csump = &udp->uh_sum; 5852255332Scy } 5853145522Sdarrenr break; 5854145522Sdarrenr 5855145522Sdarrenr case IPPROTO_ICMP : 5856145522Sdarrenr icmp = fin->fin_dp; 5857145522Sdarrenr 5858145522Sdarrenr if ((nflags & IPN_ICMPQUERY) != 0) { 5859145522Sdarrenr if (icmp->icmp_cksum != 0) 5860145522Sdarrenr csump = &icmp->icmp_cksum; 586153642Sguido } 5862145522Sdarrenr break; 586353642Sguido 5864255332Scy#ifdef USE_INET6 5865255332Scy case IPPROTO_ICMPV6 : 5866255332Scy { 5867255332Scy struct icmp6_hdr *icmp6 = (struct icmp6_hdr *)fin->fin_dp; 586853642Sguido 5869255332Scy icmp6 = fin->fin_dp; 5870145522Sdarrenr 5871255332Scy if ((nflags & IPN_ICMPQUERY) != 0) { 5872255332Scy if (icmp6->icmp6_cksum != 0) 5873255332Scy csump = &icmp6->icmp6_cksum; 5874255332Scy } 5875255332Scy break; 5876255332Scy } 5877255332Scy#endif 5878145522Sdarrenr } 5879255332Scy return csump; 588053642Sguido} 588153642Sguido 588253642Sguido 5883145522Sdarrenr/* ------------------------------------------------------------------------ */ 5884255332Scy/* Function: ipf_nat_expire */ 5885145522Sdarrenr/* Returns: Nil */ 5886255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 5887145522Sdarrenr/* */ 5888145522Sdarrenr/* Check all of the timeout queues for entries at the top which need to be */ 5889145522Sdarrenr/* expired. */ 5890145522Sdarrenr/* ------------------------------------------------------------------------ */ 5891255332Scyvoid 5892255332Scyipf_nat_expire(softc) 5893255332Scy ipf_main_softc_t *softc; 589453642Sguido{ 5895255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 5896145522Sdarrenr ipftq_t *ifq, *ifqnext; 5897145522Sdarrenr ipftqent_t *tqe, *tqn; 5898161356Sguido int i; 5899153876Sguido SPL_INT(s); 590053642Sguido 590153642Sguido SPL_NET(s); 5902255332Scy WRITE_ENTER(&softc->ipf_nat); 5903255332Scy for (ifq = softn->ipf_nat_tcptq, i = 0; ifq != NULL; 5904255332Scy ifq = ifq->ifq_next) { 5905145522Sdarrenr for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { 5906255332Scy if (tqe->tqe_die > softc->ipf_ticks) 5907145522Sdarrenr break; 5908145522Sdarrenr tqn = tqe->tqe_next; 5909255332Scy ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE); 591053642Sguido } 591153642Sguido } 5912145522Sdarrenr 5913255332Scy for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifq->ifq_next) { 5914145522Sdarrenr for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { 5915255332Scy if (tqe->tqe_die > softc->ipf_ticks) 5916145522Sdarrenr break; 5917145522Sdarrenr tqn = tqe->tqe_next; 5918255332Scy ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE); 5919145522Sdarrenr } 5920145522Sdarrenr } 5921145522Sdarrenr 5922255332Scy for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) { 5923145522Sdarrenr ifqnext = ifq->ifq_next; 5924145522Sdarrenr 5925145522Sdarrenr if (((ifq->ifq_flags & IFQF_DELETE) != 0) && 5926145522Sdarrenr (ifq->ifq_ref == 0)) { 5927255332Scy ipf_freetimeoutqueue(softc, ifq); 5928145522Sdarrenr } 5929145522Sdarrenr } 5930145522Sdarrenr 5931255332Scy if (softn->ipf_nat_doflush != 0) { 5932255332Scy ipf_nat_extraflush(softc, softn, 2); 5933255332Scy softn->ipf_nat_doflush = 0; 5934170268Sdarrenr } 5935170268Sdarrenr 5936255332Scy RWLOCK_EXIT(&softc->ipf_nat); 593753642Sguido SPL_X(s); 593853642Sguido} 593953642Sguido 594053642Sguido 5941145522Sdarrenr/* ------------------------------------------------------------------------ */ 5942255332Scy/* Function: ipf_nat_sync */ 5943145522Sdarrenr/* Returns: Nil */ 5944255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 5945255332Scy/* ifp(I) - pointer to network interface */ 5946145522Sdarrenr/* */ 5947145522Sdarrenr/* Walk through all of the currently active NAT sessions, looking for those */ 5948145522Sdarrenr/* which need to have their translated address updated. */ 5949145522Sdarrenr/* ------------------------------------------------------------------------ */ 5950255332Scyvoid 5951255332Scyipf_nat_sync(softc, ifp) 5952255332Scy ipf_main_softc_t *softc; 5953255332Scy void *ifp; 595453642Sguido{ 5955255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 5956145522Sdarrenr u_32_t sum1, sum2, sumd; 5957255332Scy i6addr_t in; 5958145522Sdarrenr ipnat_t *n; 5959145522Sdarrenr nat_t *nat; 596053642Sguido void *ifp2; 5961255332Scy int idx; 5962153876Sguido SPL_INT(s); 596353642Sguido 5964255332Scy if (softc->ipf_running <= 0) 5965145522Sdarrenr return; 5966145522Sdarrenr 596753642Sguido /* 596853642Sguido * Change IP addresses for NAT sessions for any protocol except TCP 5969145522Sdarrenr * since it will break the TCP connection anyway. The only rules 5970145522Sdarrenr * which will get changed are those which are "map ... -> 0/32", 5971145522Sdarrenr * where the rule specifies the address is taken from the interface. 597253642Sguido */ 597353642Sguido SPL_NET(s); 5974255332Scy WRITE_ENTER(&softc->ipf_nat); 5975145522Sdarrenr 5976255332Scy if (softc->ipf_running <= 0) { 5977255332Scy RWLOCK_EXIT(&softc->ipf_nat); 5978145522Sdarrenr return; 5979145522Sdarrenr } 5980145522Sdarrenr 5981255332Scy for (nat = softn->ipf_nat_instances; nat; nat = nat->nat_next) { 5982145522Sdarrenr if ((nat->nat_flags & IPN_TCP) != 0) 5983145522Sdarrenr continue; 5984255332Scy 5985145522Sdarrenr n = nat->nat_ptr; 5986255332Scy if (n != NULL) { 5987255332Scy if (n->in_v[1] == 4) { 5988255332Scy if (n->in_redir & NAT_MAP) { 5989255332Scy if ((n->in_nsrcaddr != 0) || 5990255332Scy (n->in_nsrcmsk != 0xffffffff)) 5991255332Scy continue; 5992255332Scy } else if (n->in_redir & NAT_REDIRECT) { 5993255332Scy if ((n->in_ndstaddr != 0) || 5994255332Scy (n->in_ndstmsk != 0xffffffff)) 5995255332Scy continue; 5996255332Scy } 5997255332Scy } 5998255332Scy#ifdef USE_INET6 5999255332Scy if (n->in_v[1] == 4) { 6000255332Scy if (n->in_redir & NAT_MAP) { 6001255332Scy if (!IP6_ISZERO(&n->in_nsrcaddr) || 6002255332Scy !IP6_ISONES(&n->in_nsrcmsk)) 6003255332Scy continue; 6004255332Scy } else if (n->in_redir & NAT_REDIRECT) { 6005255332Scy if (!IP6_ISZERO(&n->in_ndstaddr) || 6006255332Scy !IP6_ISONES(&n->in_ndstmsk)) 6007255332Scy continue; 6008255332Scy } 6009255332Scy } 6010255332Scy#endif 6011255332Scy } 6012255332Scy 6013145522Sdarrenr if (((ifp == NULL) || (ifp == nat->nat_ifps[0]) || 6014145522Sdarrenr (ifp == nat->nat_ifps[1]))) { 6015255332Scy nat->nat_ifps[0] = GETIFP(nat->nat_ifnames[0], 6016255332Scy nat->nat_v[0]); 6017255332Scy if ((nat->nat_ifps[0] != NULL) && 6018255332Scy (nat->nat_ifps[0] != (void *)-1)) { 6019255332Scy nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); 6020255332Scy } 6021145522Sdarrenr if (nat->nat_ifnames[1][0] != '\0') { 6022145522Sdarrenr nat->nat_ifps[1] = GETIFP(nat->nat_ifnames[1], 6023255332Scy nat->nat_v[1]); 6024255332Scy } else { 6025145522Sdarrenr nat->nat_ifps[1] = nat->nat_ifps[0]; 6026255332Scy } 6027255332Scy if ((nat->nat_ifps[1] != NULL) && 6028255332Scy (nat->nat_ifps[1] != (void *)-1)) { 6029255332Scy nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); 6030255332Scy } 6031145522Sdarrenr ifp2 = nat->nat_ifps[0]; 6032145522Sdarrenr if (ifp2 == NULL) 6033145522Sdarrenr continue; 6034145522Sdarrenr 603553642Sguido /* 603653642Sguido * Change the map-to address to be the same as the 603753642Sguido * new one. 603853642Sguido */ 6039255332Scy sum1 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6); 6040255332Scy if (ipf_ifpaddr(softc, nat->nat_v[0], FRI_NORMAL, ifp2, 6041255332Scy &in, NULL) != -1) { 6042255332Scy if (nat->nat_v[0] == 4) 6043255332Scy nat->nat_nsrcip = in.in4; 6044255332Scy } 6045255332Scy sum2 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6); 604653642Sguido 604753642Sguido if (sum1 == sum2) 604853642Sguido continue; 604953642Sguido /* 605053642Sguido * Readjust the checksum adjustment to take into 605153642Sguido * account the new IP#. 605253642Sguido */ 605353642Sguido CALC_SUMD(sum1, sum2, sumd); 605455929Sguido /* XXX - dont change for TCP when solaris does 605555929Sguido * hardware checksumming. 605655929Sguido */ 605755929Sguido sumd += nat->nat_sumd[0]; 605855929Sguido nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 605955929Sguido nat->nat_sumd[1] = nat->nat_sumd[0]; 606053642Sguido } 6061145522Sdarrenr } 606253642Sguido 6063255332Scy for (n = softn->ipf_nat_list; (n != NULL); n = n->in_next) { 6064255332Scy char *base = n->in_names; 6065255332Scy 6066145522Sdarrenr if ((ifp == NULL) || (n->in_ifps[0] == ifp)) 6067255332Scy n->in_ifps[0] = ipf_resolvenic(softc, 6068255332Scy base + n->in_ifnames[0], 6069255332Scy n->in_v[0]); 6070145522Sdarrenr if ((ifp == NULL) || (n->in_ifps[1] == ifp)) 6071255332Scy n->in_ifps[1] = ipf_resolvenic(softc, 6072255332Scy base + n->in_ifnames[1], 6073255332Scy n->in_v[1]); 6074255332Scy 6075255332Scy if (n->in_redir & NAT_REDIRECT) 6076255332Scy idx = 1; 6077255332Scy else 6078255332Scy idx = 0; 6079255332Scy 6080255332Scy if (((ifp == NULL) || (n->in_ifps[idx] == ifp)) && 6081255332Scy (n->in_ifps[idx] != NULL && 6082255332Scy n->in_ifps[idx] != (void *)-1)) { 6083255332Scy 6084255332Scy ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, 6085255332Scy 0, n->in_ifps[idx]); 6086255332Scy ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, 6087255332Scy 0, n->in_ifps[idx]); 6088255332Scy ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, 6089255332Scy 0, n->in_ifps[idx]); 6090255332Scy ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, 6091255332Scy 0, n->in_ifps[idx]); 6092255332Scy } 6093145522Sdarrenr } 6094255332Scy RWLOCK_EXIT(&softc->ipf_nat); 609553642Sguido SPL_X(s); 609653642Sguido} 609753642Sguido 609853642Sguido 6099145522Sdarrenr/* ------------------------------------------------------------------------ */ 6100255332Scy/* Function: ipf_nat_icmpquerytype */ 6101145522Sdarrenr/* Returns: int - 1 == success, 0 == failure */ 6102145522Sdarrenr/* Parameters: icmptype(I) - ICMP type number */ 6103145522Sdarrenr/* */ 6104145522Sdarrenr/* Tests to see if the ICMP type number passed is a query/response type or */ 6105145522Sdarrenr/* not. */ 6106145522Sdarrenr/* ------------------------------------------------------------------------ */ 6107255332Scystatic int 6108255332Scyipf_nat_icmpquerytype(icmptype) 6109255332Scy int icmptype; 6110145522Sdarrenr{ 6111145522Sdarrenr 6112145522Sdarrenr /* 6113145522Sdarrenr * For the ICMP query NAT code, it is essential that both the query 6114145522Sdarrenr * and the reply match on the NAT rule. Because the NAT structure 6115145522Sdarrenr * does not keep track of the icmptype, and a single NAT structure 6116145522Sdarrenr * is used for all icmp types with the same src, dest and id, we 6117145522Sdarrenr * simply define the replies as queries as well. The funny thing is, 6118145522Sdarrenr * altough it seems silly to call a reply a query, this is exactly 6119145522Sdarrenr * as it is defined in the IPv4 specification 6120145522Sdarrenr */ 6121145522Sdarrenr switch (icmptype) 6122145522Sdarrenr { 6123145522Sdarrenr case ICMP_ECHOREPLY: 6124145522Sdarrenr case ICMP_ECHO: 6125324513Scy /* route advertisement/solicitation is currently unsupported: */ 6126324513Scy /* it would require rewriting the ICMP data section */ 6127145522Sdarrenr case ICMP_TSTAMP: 6128145522Sdarrenr case ICMP_TSTAMPREPLY: 6129145522Sdarrenr case ICMP_IREQ: 6130145522Sdarrenr case ICMP_IREQREPLY: 6131145522Sdarrenr case ICMP_MASKREQ: 6132145522Sdarrenr case ICMP_MASKREPLY: 6133145522Sdarrenr return 1; 6134145522Sdarrenr default: 6135145522Sdarrenr return 0; 6136145522Sdarrenr } 6137145522Sdarrenr} 6138145522Sdarrenr 6139145522Sdarrenr 6140145522Sdarrenr/* ------------------------------------------------------------------------ */ 6141145522Sdarrenr/* Function: nat_log */ 6142145522Sdarrenr/* Returns: Nil */ 6143255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6144255332Scy/* softn(I) - pointer to NAT context structure */ 6145255332Scy/* nat(I) - pointer to NAT structure */ 6146255332Scy/* action(I) - action related to NAT structure being performed */ 6147145522Sdarrenr/* */ 6148145522Sdarrenr/* Creates a NAT log entry. */ 6149145522Sdarrenr/* ------------------------------------------------------------------------ */ 6150255332Scyvoid 6151255332Scyipf_nat_log(softc, softn, nat, action) 6152255332Scy ipf_main_softc_t *softc; 6153255332Scy ipf_nat_softc_t *softn; 6154255332Scy struct nat *nat; 6155255332Scy u_int action; 615653642Sguido{ 6157145522Sdarrenr#ifdef IPFILTER_LOG 6158139005Smlaier# ifndef LARGE_NAT 615953642Sguido struct ipnat *np; 6160138979Sdarrenr int rulen; 6161138979Sdarrenr# endif 616253642Sguido struct natlog natl; 616353642Sguido void *items[1]; 616453642Sguido size_t sizes[1]; 6165138979Sdarrenr int types[1]; 616653642Sguido 6167255332Scy bcopy((char *)&nat->nat_osrc6, (char *)&natl.nl_osrcip, 6168255332Scy sizeof(natl.nl_osrcip)); 6169255332Scy bcopy((char *)&nat->nat_nsrc6, (char *)&natl.nl_nsrcip, 6170255332Scy sizeof(natl.nl_nsrcip)); 6171255332Scy bcopy((char *)&nat->nat_odst6, (char *)&natl.nl_odstip, 6172255332Scy sizeof(natl.nl_odstip)); 6173255332Scy bcopy((char *)&nat->nat_ndst6, (char *)&natl.nl_ndstip, 6174255332Scy sizeof(natl.nl_ndstip)); 6175255332Scy 6176145522Sdarrenr natl.nl_bytes[0] = nat->nat_bytes[0]; 6177145522Sdarrenr natl.nl_bytes[1] = nat->nat_bytes[1]; 6178145522Sdarrenr natl.nl_pkts[0] = nat->nat_pkts[0]; 6179145522Sdarrenr natl.nl_pkts[1] = nat->nat_pkts[1]; 6180255332Scy natl.nl_odstport = nat->nat_odport; 6181255332Scy natl.nl_osrcport = nat->nat_osport; 6182255332Scy natl.nl_nsrcport = nat->nat_nsport; 6183255332Scy natl.nl_ndstport = nat->nat_ndport; 6184255332Scy natl.nl_p[0] = nat->nat_pr[0]; 6185255332Scy natl.nl_p[1] = nat->nat_pr[1]; 6186255332Scy natl.nl_v[0] = nat->nat_v[0]; 6187255332Scy natl.nl_v[1] = nat->nat_v[1]; 6188255332Scy natl.nl_type = nat->nat_redir; 6189255332Scy natl.nl_action = action; 619053642Sguido natl.nl_rule = -1; 6191255332Scy 6192255332Scy bcopy(nat->nat_ifnames[0], natl.nl_ifnames[0], 6193255332Scy sizeof(nat->nat_ifnames[0])); 6194255332Scy bcopy(nat->nat_ifnames[1], natl.nl_ifnames[1], 6195255332Scy sizeof(nat->nat_ifnames[1])); 6196255332Scy 6197145522Sdarrenr# ifndef LARGE_NAT 619853642Sguido if (nat->nat_ptr != NULL) { 6199255332Scy for (rulen = 0, np = softn->ipf_nat_list; np != NULL; 6200255332Scy np = np->in_next, rulen++) 620153642Sguido if (np == nat->nat_ptr) { 620253642Sguido natl.nl_rule = rulen; 620353642Sguido break; 620453642Sguido } 620553642Sguido } 6206145522Sdarrenr# endif 620753642Sguido items[0] = &natl; 620853642Sguido sizes[0] = sizeof(natl); 620953642Sguido types[0] = 0; 621053642Sguido 6211255332Scy (void) ipf_log_items(softc, IPL_LOGNAT, NULL, items, sizes, types, 1); 6212145522Sdarrenr#endif 621353642Sguido} 621492685Sdarrenr 621592685Sdarrenr 621692685Sdarrenr#if defined(__OpenBSD__) 6217145522Sdarrenr/* ------------------------------------------------------------------------ */ 6218255332Scy/* Function: ipf_nat_ifdetach */ 6219145522Sdarrenr/* Returns: Nil */ 6220145522Sdarrenr/* Parameters: ifp(I) - pointer to network interface */ 6221145522Sdarrenr/* */ 6222145522Sdarrenr/* Compatibility interface for OpenBSD to trigger the correct updating of */ 6223145522Sdarrenr/* interface references within IPFilter. */ 6224145522Sdarrenr/* ------------------------------------------------------------------------ */ 6225255332Scyvoid 6226255332Scyipf_nat_ifdetach(ifp) 6227255332Scy void *ifp; 622892685Sdarrenr{ 6229255332Scy ipf_main_softc_t *softc; 6230255332Scy 6231255332Scy softc = ipf_get_softc(0); 6232255332Scy 6233255332Scy ipf_sync(ifp); 623492685Sdarrenr return; 623592685Sdarrenr} 623692685Sdarrenr#endif 6237110916Sdarrenr 6238110916Sdarrenr 6239145522Sdarrenr/* ------------------------------------------------------------------------ */ 6240255332Scy/* Function: ipf_nat_rule_deref */ 6241170268Sdarrenr/* Returns: Nil */ 6242255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6243255332Scy/* inp(I) - pointer to pointer to NAT rule */ 6244170268Sdarrenr/* Write Locks: ipf_nat */ 6245170268Sdarrenr/* */ 6246255332Scy/* Dropping the refernce count for a rule means that whatever held the */ 6247255332Scy/* pointer to this rule (*inp) is no longer interested in it and when the */ 6248255332Scy/* reference count drops to zero, any resources allocated for the rule can */ 6249255332Scy/* be released and the rule itself free'd. */ 6250170268Sdarrenr/* ------------------------------------------------------------------------ */ 6251255332Scyvoid 6252255332Scyipf_nat_rule_deref(softc, inp) 6253255332Scy ipf_main_softc_t *softc; 6254255332Scy ipnat_t **inp; 6255170268Sdarrenr{ 6256255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 6257255332Scy ipnat_t *n; 6258170268Sdarrenr 6259255332Scy n = *inp; 6260170268Sdarrenr *inp = NULL; 6261255332Scy n->in_use--; 6262255332Scy if (n->in_use > 0) 6263255332Scy return; 6264255332Scy 6265255332Scy if (n->in_apr != NULL) 6266255332Scy ipf_proxy_deref(n->in_apr); 6267255332Scy 6268255332Scy ipf_nat_rule_fini(softc, n); 6269255332Scy 6270255332Scy if (n->in_redir & NAT_REDIRECT) { 6271255332Scy if ((n->in_flags & IPN_PROXYRULE) == 0) { 6272255332Scy ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_rdr); 6273255332Scy } 6274255332Scy } 6275255332Scy if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { 6276255332Scy if ((n->in_flags & IPN_PROXYRULE) == 0) { 6277255332Scy ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_map); 6278255332Scy } 6279255332Scy } 6280255332Scy 6281255332Scy if (n->in_tqehead[0] != NULL) { 6282255332Scy if (ipf_deletetimeoutqueue(n->in_tqehead[0]) == 0) { 6283255332Scy ipf_freetimeoutqueue(softc, n->in_tqehead[1]); 6284255332Scy } 6285255332Scy } 6286255332Scy 6287255332Scy if (n->in_tqehead[1] != NULL) { 6288255332Scy if (ipf_deletetimeoutqueue(n->in_tqehead[1]) == 0) { 6289255332Scy ipf_freetimeoutqueue(softc, n->in_tqehead[1]); 6290255332Scy } 6291255332Scy } 6292255332Scy 6293255332Scy if ((n->in_flags & IPN_PROXYRULE) == 0) { 6294255332Scy ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules); 6295255332Scy } 6296255332Scy 6297255332Scy MUTEX_DESTROY(&n->in_lock); 6298255332Scy 6299255332Scy KFREES(n, n->in_size); 6300255332Scy 6301255332Scy#if SOLARIS && !defined(INSTANCES) 6302255332Scy if (softn->ipf_nat_stats.ns_rules == 0) 6303255332Scy pfil_delayed_copy = 1; 6304170268Sdarrenr#endif 6305170268Sdarrenr} 6306170268Sdarrenr 6307170268Sdarrenr 6308170268Sdarrenr/* ------------------------------------------------------------------------ */ 6309255332Scy/* Function: ipf_nat_deref */ 6310145522Sdarrenr/* Returns: Nil */ 6311255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6312255332Scy/* natp(I) - pointer to pointer to NAT table entry */ 6313145522Sdarrenr/* */ 6314145522Sdarrenr/* Decrement the reference counter for this NAT table entry and free it if */ 6315145522Sdarrenr/* there are no more things using it. */ 6316172776Sdarrenr/* */ 6317172776Sdarrenr/* IF nat_ref == 1 when this function is called, then we have an orphan nat */ 6318172776Sdarrenr/* structure *because* it only gets called on paths _after_ nat_ref has been*/ 6319172776Sdarrenr/* incremented. If nat_ref == 1 then we shouldn't decrement it here */ 6320172776Sdarrenr/* because nat_delete() will do that and send nat_ref to -1. */ 6321172776Sdarrenr/* */ 6322172776Sdarrenr/* Holding the lock on nat_lock is required to serialise nat_delete() being */ 6323172776Sdarrenr/* called from a NAT flush ioctl with a deref happening because of a packet.*/ 6324145522Sdarrenr/* ------------------------------------------------------------------------ */ 6325255332Scyvoid 6326255332Scyipf_nat_deref(softc, natp) 6327255332Scy ipf_main_softc_t *softc; 6328255332Scy nat_t **natp; 6329145522Sdarrenr{ 6330145522Sdarrenr nat_t *nat; 6331145522Sdarrenr 6332145522Sdarrenr nat = *natp; 6333145522Sdarrenr *natp = NULL; 6334172776Sdarrenr 6335172776Sdarrenr MUTEX_ENTER(&nat->nat_lock); 6336172776Sdarrenr if (nat->nat_ref > 1) { 6337172776Sdarrenr nat->nat_ref--; 6338255332Scy ASSERT(nat->nat_ref >= 0); 6339172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 6340172776Sdarrenr return; 6341172776Sdarrenr } 6342172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 6343172776Sdarrenr 6344255332Scy WRITE_ENTER(&softc->ipf_nat); 6345255332Scy ipf_nat_delete(softc, nat, NL_EXPIRE); 6346255332Scy RWLOCK_EXIT(&softc->ipf_nat); 6347145522Sdarrenr} 6348145522Sdarrenr 6349145522Sdarrenr 6350145522Sdarrenr/* ------------------------------------------------------------------------ */ 6351255332Scy/* Function: ipf_nat_clone */ 6352145522Sdarrenr/* Returns: ipstate_t* - NULL == cloning failed, */ 6353145522Sdarrenr/* else pointer to new state structure */ 6354145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 6355145522Sdarrenr/* is(I) - pointer to master state structure */ 6356145522Sdarrenr/* Write Lock: ipf_nat */ 6357145522Sdarrenr/* */ 6358145522Sdarrenr/* Create a "duplcate" state table entry from the master. */ 6359145522Sdarrenr/* ------------------------------------------------------------------------ */ 6360255332Scynat_t * 6361255332Scyipf_nat_clone(fin, nat) 6362255332Scy fr_info_t *fin; 6363255332Scy nat_t *nat; 6364145522Sdarrenr{ 6365255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 6366255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 6367145522Sdarrenr frentry_t *fr; 6368145522Sdarrenr nat_t *clone; 6369145522Sdarrenr ipnat_t *np; 6370145522Sdarrenr 6371145522Sdarrenr KMALLOC(clone, nat_t *); 6372255332Scy if (clone == NULL) { 6373255332Scy NBUMPSIDED(fin->fin_out, ns_clone_nomem); 6374145522Sdarrenr return NULL; 6375255332Scy } 6376145522Sdarrenr bcopy((char *)nat, (char *)clone, sizeof(*clone)); 6377145522Sdarrenr 6378145522Sdarrenr MUTEX_NUKE(&clone->nat_lock); 6379145522Sdarrenr 6380255332Scy clone->nat_rev = fin->fin_rev; 6381153876Sguido clone->nat_aps = NULL; 6382153876Sguido /* 6383255332Scy * Initialize all these so that ipf_nat_delete() doesn't cause a crash. 6384153876Sguido */ 6385153876Sguido clone->nat_tqe.tqe_pnext = NULL; 6386153876Sguido clone->nat_tqe.tqe_next = NULL; 6387153876Sguido clone->nat_tqe.tqe_ifq = NULL; 6388153876Sguido clone->nat_tqe.tqe_parent = clone; 6389153876Sguido 6390145522Sdarrenr clone->nat_flags &= ~SI_CLONE; 6391145522Sdarrenr clone->nat_flags |= SI_CLONED; 6392145522Sdarrenr 6393153876Sguido if (clone->nat_hm) 6394153876Sguido clone->nat_hm->hm_ref++; 6395145522Sdarrenr 6396255332Scy if (ipf_nat_insert(softc, softn, clone) == -1) { 6397145522Sdarrenr KFREE(clone); 6398255332Scy NBUMPSIDED(fin->fin_out, ns_insert_fail); 6399145522Sdarrenr return NULL; 6400145522Sdarrenr } 6401255332Scy 6402145522Sdarrenr np = clone->nat_ptr; 6403145522Sdarrenr if (np != NULL) { 6404255332Scy if (softn->ipf_nat_logging) 6405255332Scy ipf_nat_log(softc, softn, clone, NL_CLONE); 6406145522Sdarrenr np->in_use++; 6407145522Sdarrenr } 6408145522Sdarrenr fr = clone->nat_fr; 6409145522Sdarrenr if (fr != NULL) { 6410145522Sdarrenr MUTEX_ENTER(&fr->fr_lock); 6411145522Sdarrenr fr->fr_ref++; 6412145522Sdarrenr MUTEX_EXIT(&fr->fr_lock); 6413145522Sdarrenr } 6414145522Sdarrenr 6415255332Scy 6416145522Sdarrenr /* 6417145522Sdarrenr * Because the clone is created outside the normal loop of things and 6418145522Sdarrenr * TCP has special needs in terms of state, initialise the timeout 6419145522Sdarrenr * state of the new NAT from here. 6420145522Sdarrenr */ 6421255332Scy if (clone->nat_pr[0] == IPPROTO_TCP) { 6422255332Scy (void) ipf_tcp_age(&clone->nat_tqe, fin, softn->ipf_nat_tcptq, 6423255332Scy clone->nat_flags, 2); 6424145522Sdarrenr } 6425255332Scy clone->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, clone); 6426255332Scy if (softn->ipf_nat_logging) 6427255332Scy ipf_nat_log(softc, softn, clone, NL_CLONE); 6428145522Sdarrenr return clone; 6429145522Sdarrenr} 6430145522Sdarrenr 6431145522Sdarrenr 6432145522Sdarrenr/* ------------------------------------------------------------------------ */ 6433255332Scy/* Function: ipf_nat_wildok */ 6434145522Sdarrenr/* Returns: int - 1 == packet's ports match wildcards */ 6435145522Sdarrenr/* 0 == packet's ports don't match wildcards */ 6436145522Sdarrenr/* Parameters: nat(I) - NAT entry */ 6437145522Sdarrenr/* sport(I) - source port */ 6438145522Sdarrenr/* dport(I) - destination port */ 6439145522Sdarrenr/* flags(I) - wildcard flags */ 6440145522Sdarrenr/* dir(I) - packet direction */ 6441145522Sdarrenr/* */ 6442145522Sdarrenr/* Use NAT entry and packet direction to determine which combination of */ 6443145522Sdarrenr/* wildcard flags should be used. */ 6444145522Sdarrenr/* ------------------------------------------------------------------------ */ 6445255332Scyint 6446255332Scyipf_nat_wildok(nat, sport, dport, flags, dir) 6447255332Scy nat_t *nat; 6448255332Scy int sport, dport, flags, dir; 6449145522Sdarrenr{ 6450145522Sdarrenr /* 6451145522Sdarrenr * When called by dir is set to 6452145522Sdarrenr * nat_inlookup NAT_INBOUND (0) 6453145522Sdarrenr * nat_outlookup NAT_OUTBOUND (1) 6454145522Sdarrenr * 6455145522Sdarrenr * We simply combine the packet's direction in dir with the original 6456145522Sdarrenr * "intended" direction of that NAT entry in nat->nat_dir to decide 6457145522Sdarrenr * which combination of wildcard flags to allow. 6458145522Sdarrenr */ 6459255332Scy switch ((dir << 1) | (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND))) 6460145522Sdarrenr { 6461145522Sdarrenr case 3: /* outbound packet / outbound entry */ 6462255332Scy if (((nat->nat_osport == sport) || 6463145522Sdarrenr (flags & SI_W_SPORT)) && 6464255332Scy ((nat->nat_odport == dport) || 6465145522Sdarrenr (flags & SI_W_DPORT))) 6466145522Sdarrenr return 1; 6467145522Sdarrenr break; 6468145522Sdarrenr case 2: /* outbound packet / inbound entry */ 6469255332Scy if (((nat->nat_osport == dport) || 6470255332Scy (flags & SI_W_SPORT)) && 6471255332Scy ((nat->nat_odport == sport) || 6472255332Scy (flags & SI_W_DPORT))) 6473145522Sdarrenr return 1; 6474145522Sdarrenr break; 6475145522Sdarrenr case 1: /* inbound packet / outbound entry */ 6476255332Scy if (((nat->nat_osport == dport) || 6477255332Scy (flags & SI_W_SPORT)) && 6478255332Scy ((nat->nat_odport == sport) || 6479255332Scy (flags & SI_W_DPORT))) 6480145522Sdarrenr return 1; 6481145522Sdarrenr break; 6482145522Sdarrenr case 0: /* inbound packet / inbound entry */ 6483255332Scy if (((nat->nat_osport == sport) || 6484145522Sdarrenr (flags & SI_W_SPORT)) && 6485255332Scy ((nat->nat_odport == dport) || 6486145522Sdarrenr (flags & SI_W_DPORT))) 6487145522Sdarrenr return 1; 6488145522Sdarrenr break; 6489145522Sdarrenr default: 6490145522Sdarrenr break; 6491145522Sdarrenr } 6492145522Sdarrenr 6493145522Sdarrenr return(0); 6494145522Sdarrenr} 6495145522Sdarrenr 6496145522Sdarrenr 6497145522Sdarrenr/* ------------------------------------------------------------------------ */ 6498145522Sdarrenr/* Function: nat_mssclamp */ 6499145522Sdarrenr/* Returns: Nil */ 6500145522Sdarrenr/* Parameters: tcp(I) - pointer to TCP header */ 6501145522Sdarrenr/* maxmss(I) - value to clamp the TCP MSS to */ 6502145522Sdarrenr/* fin(I) - pointer to packet information */ 6503145522Sdarrenr/* csump(I) - pointer to TCP checksum */ 6504145522Sdarrenr/* */ 6505145522Sdarrenr/* Check for MSS option and clamp it if necessary. If found and changed, */ 6506145522Sdarrenr/* then the TCP header checksum will be updated to reflect the change in */ 6507145522Sdarrenr/* the MSS. */ 6508145522Sdarrenr/* ------------------------------------------------------------------------ */ 6509255332Scystatic void 6510255332Scyipf_nat_mssclamp(tcp, maxmss, fin, csump) 6511255332Scy tcphdr_t *tcp; 6512255332Scy u_32_t maxmss; 6513255332Scy fr_info_t *fin; 6514255332Scy u_short *csump; 6515110916Sdarrenr{ 6516110916Sdarrenr u_char *cp, *ep, opt; 6517110916Sdarrenr int hlen, advance; 6518110916Sdarrenr u_32_t mss, sumd; 6519110916Sdarrenr 6520145522Sdarrenr hlen = TCP_OFF(tcp) << 2; 6521110916Sdarrenr if (hlen > sizeof(*tcp)) { 6522110916Sdarrenr cp = (u_char *)tcp + sizeof(*tcp); 6523110916Sdarrenr ep = (u_char *)tcp + hlen; 6524110916Sdarrenr 6525110916Sdarrenr while (cp < ep) { 6526110916Sdarrenr opt = cp[0]; 6527110916Sdarrenr if (opt == TCPOPT_EOL) 6528110916Sdarrenr break; 6529110916Sdarrenr else if (opt == TCPOPT_NOP) { 6530110916Sdarrenr cp++; 6531110916Sdarrenr continue; 6532110916Sdarrenr } 6533145522Sdarrenr 6534145522Sdarrenr if (cp + 1 >= ep) 6535110916Sdarrenr break; 6536110916Sdarrenr advance = cp[1]; 6537145522Sdarrenr if ((cp + advance > ep) || (advance <= 0)) 6538110916Sdarrenr break; 6539145522Sdarrenr switch (opt) 6540145522Sdarrenr { 6541110916Sdarrenr case TCPOPT_MAXSEG: 6542110916Sdarrenr if (advance != 4) 6543110916Sdarrenr break; 6544145522Sdarrenr mss = cp[2] * 256 + cp[3]; 6545110916Sdarrenr if (mss > maxmss) { 6546145522Sdarrenr cp[2] = maxmss / 256; 6547145522Sdarrenr cp[3] = maxmss & 0xff; 6548110916Sdarrenr CALC_SUMD(mss, maxmss, sumd); 6549255332Scy ipf_fix_outcksum(0, csump, sumd, 0); 6550110916Sdarrenr } 6551110916Sdarrenr break; 6552110916Sdarrenr default: 6553110916Sdarrenr /* ignore unknown options */ 6554110916Sdarrenr break; 6555110916Sdarrenr } 6556145522Sdarrenr 6557145522Sdarrenr cp += advance; 6558145522Sdarrenr } 6559145522Sdarrenr } 6560145522Sdarrenr} 6561145522Sdarrenr 6562145522Sdarrenr 6563145522Sdarrenr/* ------------------------------------------------------------------------ */ 6564255332Scy/* Function: ipf_nat_setqueue */ 6565145522Sdarrenr/* Returns: Nil */ 6566255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6567255332Scy/* softn(I) - pointer to NAT context structure */ 6568255332Scy/* nat(I)- pointer to NAT structure */ 6569145522Sdarrenr/* Locks: ipf_nat (read or write) */ 6570145522Sdarrenr/* */ 6571145522Sdarrenr/* Put the NAT entry on its default queue entry, using rev as a helped in */ 6572145522Sdarrenr/* determining which queue it should be placed on. */ 6573145522Sdarrenr/* ------------------------------------------------------------------------ */ 6574255332Scyvoid 6575255332Scyipf_nat_setqueue(softc, softn, nat) 6576255332Scy ipf_main_softc_t *softc; 6577255332Scy ipf_nat_softc_t *softn; 6578255332Scy nat_t *nat; 6579145522Sdarrenr{ 6580145522Sdarrenr ipftq_t *oifq, *nifq; 6581255332Scy int rev = nat->nat_rev; 6582145522Sdarrenr 6583145522Sdarrenr if (nat->nat_ptr != NULL) 6584145522Sdarrenr nifq = nat->nat_ptr->in_tqehead[rev]; 6585145522Sdarrenr else 6586145522Sdarrenr nifq = NULL; 6587145522Sdarrenr 6588145522Sdarrenr if (nifq == NULL) { 6589255332Scy switch (nat->nat_pr[0]) 6590145522Sdarrenr { 6591145522Sdarrenr case IPPROTO_UDP : 6592255332Scy nifq = &softn->ipf_nat_udptq; 6593145522Sdarrenr break; 6594145522Sdarrenr case IPPROTO_ICMP : 6595255332Scy nifq = &softn->ipf_nat_icmptq; 6596145522Sdarrenr break; 6597145522Sdarrenr case IPPROTO_TCP : 6598255332Scy nifq = softn->ipf_nat_tcptq + 6599255332Scy nat->nat_tqe.tqe_state[rev]; 6600145522Sdarrenr break; 6601145522Sdarrenr default : 6602255332Scy nifq = &softn->ipf_nat_iptq; 6603145522Sdarrenr break; 6604145522Sdarrenr } 6605145522Sdarrenr } 6606145522Sdarrenr 6607145522Sdarrenr oifq = nat->nat_tqe.tqe_ifq; 6608145522Sdarrenr /* 6609145522Sdarrenr * If it's currently on a timeout queue, move it from one queue to 6610145522Sdarrenr * another, else put it on the end of the newly determined queue. 6611145522Sdarrenr */ 6612145522Sdarrenr if (oifq != NULL) 6613255332Scy ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, nifq); 6614145522Sdarrenr else 6615255332Scy ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, nifq, nat); 6616145522Sdarrenr return; 6617145522Sdarrenr} 6618170268Sdarrenr 6619170268Sdarrenr 6620170268Sdarrenr/* ------------------------------------------------------------------------ */ 6621170268Sdarrenr/* Function: nat_getnext */ 6622170268Sdarrenr/* Returns: int - 0 == ok, else error */ 6623255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6624255332Scy/* t(I) - pointer to ipftoken structure */ 6625170268Sdarrenr/* itp(I) - pointer to ipfgeniter_t structure */ 6626170268Sdarrenr/* */ 6627170268Sdarrenr/* Fetch the next nat/ipnat structure pointer from the linked list and */ 6628170268Sdarrenr/* copy it out to the storage space pointed to by itp_data. The next item */ 6629170268Sdarrenr/* in the list to look at is put back in the ipftoken struture. */ 6630170268Sdarrenr/* ------------------------------------------------------------------------ */ 6631255332Scystatic int 6632255332Scyipf_nat_getnext(softc, t, itp, objp) 6633255332Scy ipf_main_softc_t *softc; 6634255332Scy ipftoken_t *t; 6635255332Scy ipfgeniter_t *itp; 6636255332Scy ipfobj_t *objp; 6637170268Sdarrenr{ 6638255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 6639170268Sdarrenr hostmap_t *hm, *nexthm = NULL, zerohm; 6640170268Sdarrenr ipnat_t *ipn, *nextipnat = NULL, zeroipn; 6641170268Sdarrenr nat_t *nat, *nextnat = NULL, zeronat; 6642255332Scy int error = 0; 6643255332Scy void *nnext; 6644170268Sdarrenr 6645255332Scy if (itp->igi_nitems != 1) { 6646255332Scy IPFERROR(60075); 6647172776Sdarrenr return ENOSPC; 6648255332Scy } 6649170268Sdarrenr 6650255332Scy READ_ENTER(&softc->ipf_nat); 6651170268Sdarrenr 6652170268Sdarrenr switch (itp->igi_type) 6653170268Sdarrenr { 6654170268Sdarrenr case IPFGENITER_HOSTMAP : 6655170268Sdarrenr hm = t->ipt_data; 6656170268Sdarrenr if (hm == NULL) { 6657255332Scy nexthm = softn->ipf_hm_maplist; 6658170268Sdarrenr } else { 6659170268Sdarrenr nexthm = hm->hm_next; 6660170268Sdarrenr } 6661255332Scy if (nexthm != NULL) { 6662255332Scy ATOMIC_INC32(nexthm->hm_ref); 6663255332Scy t->ipt_data = nexthm; 6664255332Scy } else { 6665255332Scy bzero(&zerohm, sizeof(zerohm)); 6666255332Scy nexthm = &zerohm; 6667255332Scy t->ipt_data = NULL; 6668255332Scy } 6669255332Scy nnext = nexthm->hm_next; 6670170268Sdarrenr break; 6671170268Sdarrenr 6672170268Sdarrenr case IPFGENITER_IPNAT : 6673170268Sdarrenr ipn = t->ipt_data; 6674170268Sdarrenr if (ipn == NULL) { 6675255332Scy nextipnat = softn->ipf_nat_list; 6676170268Sdarrenr } else { 6677170268Sdarrenr nextipnat = ipn->in_next; 6678170268Sdarrenr } 6679255332Scy if (nextipnat != NULL) { 6680255332Scy ATOMIC_INC32(nextipnat->in_use); 6681255332Scy t->ipt_data = nextipnat; 6682255332Scy } else { 6683255332Scy bzero(&zeroipn, sizeof(zeroipn)); 6684255332Scy nextipnat = &zeroipn; 6685255332Scy t->ipt_data = NULL; 6686255332Scy } 6687255332Scy nnext = nextipnat->in_next; 6688170268Sdarrenr break; 6689170268Sdarrenr 6690170268Sdarrenr case IPFGENITER_NAT : 6691170268Sdarrenr nat = t->ipt_data; 6692170268Sdarrenr if (nat == NULL) { 6693255332Scy nextnat = softn->ipf_nat_instances; 6694170268Sdarrenr } else { 6695170268Sdarrenr nextnat = nat->nat_next; 6696170268Sdarrenr } 6697255332Scy if (nextnat != NULL) { 6698255332Scy MUTEX_ENTER(&nextnat->nat_lock); 6699255332Scy nextnat->nat_ref++; 6700255332Scy MUTEX_EXIT(&nextnat->nat_lock); 6701255332Scy t->ipt_data = nextnat; 6702255332Scy } else { 6703255332Scy bzero(&zeronat, sizeof(zeronat)); 6704255332Scy nextnat = &zeronat; 6705255332Scy t->ipt_data = NULL; 6706255332Scy } 6707255332Scy nnext = nextnat->nat_next; 6708170268Sdarrenr break; 6709255332Scy 6710170268Sdarrenr default : 6711255332Scy RWLOCK_EXIT(&softc->ipf_nat); 6712255332Scy IPFERROR(60055); 6713170268Sdarrenr return EINVAL; 6714170268Sdarrenr } 6715170268Sdarrenr 6716255332Scy RWLOCK_EXIT(&softc->ipf_nat); 6717170268Sdarrenr 6718255332Scy objp->ipfo_ptr = itp->igi_data; 6719170268Sdarrenr 6720172776Sdarrenr switch (itp->igi_type) 6721172776Sdarrenr { 6722172776Sdarrenr case IPFGENITER_HOSTMAP : 6723255332Scy error = COPYOUT(nexthm, objp->ipfo_ptr, sizeof(*nexthm)); 6724255332Scy if (error != 0) { 6725255332Scy IPFERROR(60049); 6726255332Scy error = EFAULT; 6727255332Scy } 6728172776Sdarrenr if (hm != NULL) { 6729255332Scy WRITE_ENTER(&softc->ipf_nat); 6730255332Scy ipf_nat_hostmapdel(softc, &hm); 6731255332Scy RWLOCK_EXIT(&softc->ipf_nat); 6732172776Sdarrenr } 6733172776Sdarrenr break; 6734255332Scy 6735172776Sdarrenr case IPFGENITER_IPNAT : 6736255332Scy objp->ipfo_size = nextipnat->in_size; 6737255332Scy objp->ipfo_type = IPFOBJ_IPNAT; 6738255332Scy error = ipf_outobjk(softc, objp, nextipnat); 6739172776Sdarrenr if (ipn != NULL) { 6740255332Scy WRITE_ENTER(&softc->ipf_nat); 6741255332Scy ipf_nat_rule_deref(softc, &ipn); 6742255332Scy RWLOCK_EXIT(&softc->ipf_nat); 6743172776Sdarrenr } 6744172776Sdarrenr break; 6745172776Sdarrenr 6746170268Sdarrenr case IPFGENITER_NAT : 6747255332Scy objp->ipfo_size = sizeof(nat_t); 6748255332Scy objp->ipfo_type = IPFOBJ_NAT; 6749255332Scy error = ipf_outobjk(softc, objp, nextnat); 6750255332Scy if (nat != NULL) 6751255332Scy ipf_nat_deref(softc, &nat); 6752170268Sdarrenr 6753170268Sdarrenr break; 6754170268Sdarrenr } 6755170268Sdarrenr 6756255332Scy if (nnext == NULL) 6757255332Scy ipf_token_mark_complete(t); 6758255332Scy 6759170268Sdarrenr return error; 6760170268Sdarrenr} 6761170268Sdarrenr 6762170268Sdarrenr 6763170268Sdarrenr/* ------------------------------------------------------------------------ */ 6764170268Sdarrenr/* Function: nat_extraflush */ 6765170268Sdarrenr/* Returns: int - 0 == success, -1 == failure */ 6766255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6767255332Scy/* softn(I) - pointer to NAT context structure */ 6768255332Scy/* which(I) - how to flush the active NAT table */ 6769170268Sdarrenr/* Write Locks: ipf_nat */ 6770170268Sdarrenr/* */ 6771170268Sdarrenr/* Flush nat tables. Three actions currently defined: */ 6772170268Sdarrenr/* which == 0 : flush all nat table entries */ 6773170268Sdarrenr/* which == 1 : flush TCP connections which have started to close but are */ 6774170268Sdarrenr/* stuck for some reason. */ 6775170268Sdarrenr/* which == 2 : flush TCP connections which have been idle for a long time, */ 6776170268Sdarrenr/* starting at > 4 days idle and working back in successive half-*/ 6777170268Sdarrenr/* days to at most 12 hours old. If this fails to free enough */ 6778170268Sdarrenr/* slots then work backwards in half hour slots to 30 minutes. */ 6779170268Sdarrenr/* If that too fails, then work backwards in 30 second intervals */ 6780170268Sdarrenr/* for the last 30 minutes to at worst 30 seconds idle. */ 6781170268Sdarrenr/* ------------------------------------------------------------------------ */ 6782255332Scystatic int 6783255332Scyipf_nat_extraflush(softc, softn, which) 6784255332Scy ipf_main_softc_t *softc; 6785255332Scy ipf_nat_softc_t *softn; 6786255332Scy int which; 6787170268Sdarrenr{ 6788170268Sdarrenr nat_t *nat, **natp; 6789170268Sdarrenr ipftqent_t *tqn; 6790255332Scy ipftq_t *ifq; 6791170268Sdarrenr int removed; 6792170268Sdarrenr SPL_INT(s); 6793170268Sdarrenr 6794170268Sdarrenr removed = 0; 6795170268Sdarrenr 6796170268Sdarrenr SPL_NET(s); 6797170268Sdarrenr switch (which) 6798170268Sdarrenr { 6799170268Sdarrenr case 0 : 6800255332Scy softn->ipf_nat_stats.ns_flush_all++; 6801170268Sdarrenr /* 6802170268Sdarrenr * Style 0 flush removes everything... 6803170268Sdarrenr */ 6804255332Scy for (natp = &softn->ipf_nat_instances; 6805255332Scy ((nat = *natp) != NULL); ) { 6806255332Scy ipf_nat_delete(softc, nat, NL_FLUSH); 6807170268Sdarrenr removed++; 6808170268Sdarrenr } 6809170268Sdarrenr break; 6810170268Sdarrenr 6811170268Sdarrenr case 1 : 6812255332Scy softn->ipf_nat_stats.ns_flush_closing++; 6813170268Sdarrenr /* 6814170268Sdarrenr * Since we're only interested in things that are closing, 6815170268Sdarrenr * we can start with the appropriate timeout queue. 6816170268Sdarrenr */ 6817255332Scy for (ifq = softn->ipf_nat_tcptq + IPF_TCPS_CLOSE_WAIT; 6818255332Scy ifq != NULL; ifq = ifq->ifq_next) { 6819170268Sdarrenr 6820170268Sdarrenr for (tqn = ifq->ifq_head; tqn != NULL; ) { 6821170268Sdarrenr nat = tqn->tqe_parent; 6822170268Sdarrenr tqn = tqn->tqe_next; 6823255332Scy if (nat->nat_pr[0] != IPPROTO_TCP || 6824255332Scy nat->nat_pr[1] != IPPROTO_TCP) 6825170268Sdarrenr break; 6826255332Scy ipf_nat_delete(softc, nat, NL_EXPIRE); 6827170268Sdarrenr removed++; 6828170268Sdarrenr } 6829170268Sdarrenr } 6830170268Sdarrenr 6831170268Sdarrenr /* 6832170268Sdarrenr * Also need to look through the user defined queues. 6833170268Sdarrenr */ 6834255332Scy for (ifq = softn->ipf_nat_utqe; ifq != NULL; 6835255332Scy ifq = ifq->ifq_next) { 6836170268Sdarrenr for (tqn = ifq->ifq_head; tqn != NULL; ) { 6837170268Sdarrenr nat = tqn->tqe_parent; 6838170268Sdarrenr tqn = tqn->tqe_next; 6839255332Scy if (nat->nat_pr[0] != IPPROTO_TCP || 6840255332Scy nat->nat_pr[1] != IPPROTO_TCP) 6841170268Sdarrenr continue; 6842170268Sdarrenr 6843170268Sdarrenr if ((nat->nat_tcpstate[0] > 6844170268Sdarrenr IPF_TCPS_ESTABLISHED) && 6845170268Sdarrenr (nat->nat_tcpstate[1] > 6846170268Sdarrenr IPF_TCPS_ESTABLISHED)) { 6847255332Scy ipf_nat_delete(softc, nat, NL_EXPIRE); 6848170268Sdarrenr removed++; 6849170268Sdarrenr } 6850170268Sdarrenr } 6851170268Sdarrenr } 6852170268Sdarrenr break; 6853170268Sdarrenr 6854170268Sdarrenr /* 6855170268Sdarrenr * Args 5-11 correspond to flushing those particular states 6856170268Sdarrenr * for TCP connections. 6857170268Sdarrenr */ 6858170268Sdarrenr case IPF_TCPS_CLOSE_WAIT : 6859170268Sdarrenr case IPF_TCPS_FIN_WAIT_1 : 6860170268Sdarrenr case IPF_TCPS_CLOSING : 6861170268Sdarrenr case IPF_TCPS_LAST_ACK : 6862170268Sdarrenr case IPF_TCPS_FIN_WAIT_2 : 6863170268Sdarrenr case IPF_TCPS_TIME_WAIT : 6864170268Sdarrenr case IPF_TCPS_CLOSED : 6865255332Scy softn->ipf_nat_stats.ns_flush_state++; 6866255332Scy tqn = softn->ipf_nat_tcptq[which].ifq_head; 6867170268Sdarrenr while (tqn != NULL) { 6868170268Sdarrenr nat = tqn->tqe_parent; 6869170268Sdarrenr tqn = tqn->tqe_next; 6870255332Scy ipf_nat_delete(softc, nat, NL_FLUSH); 6871170268Sdarrenr removed++; 6872170268Sdarrenr } 6873170268Sdarrenr break; 6874255332Scy 6875170268Sdarrenr default : 6876170268Sdarrenr if (which < 30) 6877170268Sdarrenr break; 6878255332Scy 6879255332Scy softn->ipf_nat_stats.ns_flush_timeout++; 6880170268Sdarrenr /* 6881170268Sdarrenr * Take a large arbitrary number to mean the number of seconds 6882170268Sdarrenr * for which which consider to be the maximum value we'll allow 6883170268Sdarrenr * the expiration to be. 6884170268Sdarrenr */ 6885170268Sdarrenr which = IPF_TTLVAL(which); 6886255332Scy for (natp = &softn->ipf_nat_instances; 6887255332Scy ((nat = *natp) != NULL); ) { 6888255332Scy if (softc->ipf_ticks - nat->nat_touched > which) { 6889255332Scy ipf_nat_delete(softc, nat, NL_FLUSH); 6890170268Sdarrenr removed++; 6891170268Sdarrenr } else 6892170268Sdarrenr natp = &nat->nat_next; 6893170268Sdarrenr } 6894170268Sdarrenr break; 6895170268Sdarrenr } 6896170268Sdarrenr 6897170268Sdarrenr if (which != 2) { 6898170268Sdarrenr SPL_X(s); 6899170268Sdarrenr return removed; 6900170268Sdarrenr } 6901170268Sdarrenr 6902255332Scy softn->ipf_nat_stats.ns_flush_queue++; 6903255332Scy 6904170268Sdarrenr /* 6905255332Scy * Asked to remove inactive entries because the table is full, try 6906255332Scy * again, 3 times, if first attempt failed with a different criteria 6907255332Scy * each time. The order tried in must be in decreasing age. 6908255332Scy * Another alternative is to implement random drop and drop N entries 6909255332Scy * at random until N have been freed up. 6910170268Sdarrenr */ 6911255332Scy if (softc->ipf_ticks - softn->ipf_nat_last_force_flush > 6912255332Scy IPF_TTLVAL(5)) { 6913255332Scy softn->ipf_nat_last_force_flush = softc->ipf_ticks; 6914255332Scy 6915255332Scy removed = ipf_queueflush(softc, ipf_nat_flush_entry, 6916255332Scy softn->ipf_nat_tcptq, 6917255332Scy softn->ipf_nat_utqe, 6918255332Scy &softn->ipf_nat_stats.ns_active, 6919255332Scy softn->ipf_nat_table_sz, 6920255332Scy softn->ipf_nat_table_wm_low); 6921170268Sdarrenr } 6922170268Sdarrenr 6923170268Sdarrenr SPL_X(s); 6924170268Sdarrenr return removed; 6925170268Sdarrenr} 6926170268Sdarrenr 6927170268Sdarrenr 6928170268Sdarrenr/* ------------------------------------------------------------------------ */ 6929255332Scy/* Function: ipf_nat_flush_entry */ 6930170268Sdarrenr/* Returns: 0 - always succeeds */ 6931255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6932255332Scy/* entry(I) - pointer to NAT entry */ 6933170268Sdarrenr/* Write Locks: ipf_nat */ 6934170268Sdarrenr/* */ 6935170268Sdarrenr/* This function is a stepping stone between ipf_queueflush() and */ 6936170268Sdarrenr/* nat_dlete(). It is used so we can provide a uniform interface via the */ 6937170268Sdarrenr/* ipf_queueflush() function. Since the nat_delete() function returns void */ 6938170268Sdarrenr/* we translate that to mean it always succeeds in deleting something. */ 6939170268Sdarrenr/* ------------------------------------------------------------------------ */ 6940255332Scystatic int 6941255332Scyipf_nat_flush_entry(softc, entry) 6942255332Scy ipf_main_softc_t *softc; 6943255332Scy void *entry; 6944170268Sdarrenr{ 6945255332Scy ipf_nat_delete(softc, entry, NL_FLUSH); 6946170268Sdarrenr return 0; 6947170268Sdarrenr} 6948172776Sdarrenr 6949172776Sdarrenr 6950172776Sdarrenr/* ------------------------------------------------------------------------ */ 6951255332Scy/* Function: ipf_nat_iterator */ 6952255332Scy/* Returns: int - 0 == ok, else error */ 6953255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6954255332Scy/* token(I) - pointer to ipftoken structure */ 6955255332Scy/* itp(I) - pointer to ipfgeniter_t structure */ 6956255332Scy/* obj(I) - pointer to data description structure */ 6957255332Scy/* */ 6958255332Scy/* This function acts as a handler for the SIOCGENITER ioctls that use a */ 6959255332Scy/* generic structure to iterate through a list. There are three different */ 6960255332Scy/* linked lists of NAT related information to go through: NAT rules, active */ 6961255332Scy/* NAT mappings and the NAT fragment cache. */ 6962255332Scy/* ------------------------------------------------------------------------ */ 6963255332Scystatic int 6964255332Scyipf_nat_iterator(softc, token, itp, obj) 6965255332Scy ipf_main_softc_t *softc; 6966255332Scy ipftoken_t *token; 6967255332Scy ipfgeniter_t *itp; 6968255332Scy ipfobj_t *obj; 6969255332Scy{ 6970255332Scy int error; 6971255332Scy 6972255332Scy if (itp->igi_data == NULL) { 6973255332Scy IPFERROR(60052); 6974255332Scy return EFAULT; 6975255332Scy } 6976255332Scy 6977255332Scy switch (itp->igi_type) 6978255332Scy { 6979255332Scy case IPFGENITER_HOSTMAP : 6980255332Scy case IPFGENITER_IPNAT : 6981255332Scy case IPFGENITER_NAT : 6982255332Scy error = ipf_nat_getnext(softc, token, itp, obj); 6983255332Scy break; 6984255332Scy 6985255332Scy case IPFGENITER_NATFRAG : 6986255332Scy error = ipf_frag_nat_next(softc, token, itp); 6987255332Scy break; 6988255332Scy default : 6989255332Scy IPFERROR(60053); 6990255332Scy error = EINVAL; 6991255332Scy break; 6992255332Scy } 6993255332Scy 6994255332Scy return error; 6995255332Scy} 6996255332Scy 6997255332Scy 6998255332Scy/* ------------------------------------------------------------------------ */ 6999255332Scy/* Function: ipf_nat_setpending */ 7000255332Scy/* Returns: Nil */ 7001255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7002255332Scy/* nat(I) - pointer to NAT structure */ 7003255332Scy/* Locks: ipf_nat (read or write) */ 7004255332Scy/* */ 7005255332Scy/* Put the NAT entry on to the pending queue - this queue has a very short */ 7006255332Scy/* lifetime where items are put that can't be deleted straight away because */ 7007255332Scy/* of locking issues but we want to delete them ASAP, anyway. In calling */ 7008255332Scy/* this function, it is assumed that the owner (if there is one, as shown */ 7009255332Scy/* by nat_me) is no longer interested in it. */ 7010255332Scy/* ------------------------------------------------------------------------ */ 7011255332Scyvoid 7012255332Scyipf_nat_setpending(softc, nat) 7013255332Scy ipf_main_softc_t *softc; 7014255332Scy nat_t *nat; 7015255332Scy{ 7016255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 7017255332Scy ipftq_t *oifq; 7018255332Scy 7019255332Scy oifq = nat->nat_tqe.tqe_ifq; 7020255332Scy if (oifq != NULL) 7021255332Scy ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, 7022255332Scy &softn->ipf_nat_pending); 7023255332Scy else 7024255332Scy ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, 7025255332Scy &softn->ipf_nat_pending, nat); 7026255332Scy 7027255332Scy if (nat->nat_me != NULL) { 7028255332Scy *nat->nat_me = NULL; 7029255332Scy nat->nat_me = NULL; 7030255332Scy nat->nat_ref--; 7031255332Scy ASSERT(nat->nat_ref >= 0); 7032255332Scy } 7033255332Scy} 7034255332Scy 7035255332Scy 7036255332Scy/* ------------------------------------------------------------------------ */ 7037255332Scy/* Function: nat_newrewrite */ 7038255332Scy/* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ 7039255332Scy/* allow rule to be moved if IPN_ROUNDR is set. */ 7040255332Scy/* Parameters: fin(I) - pointer to packet information */ 7041255332Scy/* nat(I) - pointer to NAT entry */ 7042255332Scy/* ni(I) - pointer to structure with misc. information needed */ 7043255332Scy/* to create new NAT entry. */ 7044255332Scy/* Write Lock: ipf_nat */ 7045255332Scy/* */ 7046255332Scy/* This function is responsible for setting up an active NAT session where */ 7047255332Scy/* we are changing both the source and destination parameters at the same */ 7048255332Scy/* time. The loop in here works differently to elsewhere - each iteration */ 7049255332Scy/* is responsible for changing a single parameter that can be incremented. */ 7050255332Scy/* So one pass may increase the source IP#, next source port, next dest. IP#*/ 7051255332Scy/* and the last destination port for a total of 4 iterations to try each. */ 7052255332Scy/* This is done to try and exhaustively use the translation space available.*/ 7053255332Scy/* ------------------------------------------------------------------------ */ 7054255332Scystatic int 7055255332Scyipf_nat_newrewrite(fin, nat, nai) 7056255332Scy fr_info_t *fin; 7057255332Scy nat_t *nat; 7058255332Scy natinfo_t *nai; 7059255332Scy{ 7060255332Scy int src_search = 1; 7061255332Scy int dst_search = 1; 7062255332Scy fr_info_t frnat; 7063255332Scy u_32_t flags; 7064255332Scy u_short swap; 7065255332Scy ipnat_t *np; 7066255332Scy nat_t *natl; 7067255332Scy int l = 0; 7068255332Scy int changed; 7069255332Scy 7070255332Scy natl = NULL; 7071255332Scy changed = -1; 7072255332Scy np = nai->nai_np; 7073255332Scy flags = nat->nat_flags; 7074255332Scy bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); 7075255332Scy 7076255332Scy nat->nat_hm = NULL; 7077255332Scy 7078255332Scy do { 7079255332Scy changed = -1; 7080255332Scy /* TRACE (l, src_search, dst_search, np) */ 7081338170Scy DT4(ipf_nat_rewrite_1, int, l, int, src_search, int, dst_search, ipnat_t *, np); 7082255332Scy 7083255332Scy if ((src_search == 0) && (np->in_spnext == 0) && 7084255332Scy (dst_search == 0) && (np->in_dpnext == 0)) { 7085255332Scy if (l > 0) 7086255332Scy return -1; 7087255332Scy } 7088255332Scy 7089255332Scy /* 7090255332Scy * Find a new source address 7091255332Scy */ 7092255332Scy if (ipf_nat_nextaddr(fin, &np->in_nsrc, &frnat.fin_saddr, 7093255332Scy &frnat.fin_saddr) == -1) { 7094255332Scy return -1; 7095255332Scy } 7096255332Scy 7097255332Scy if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0xffffffff)) { 7098255332Scy src_search = 0; 7099255332Scy if (np->in_stepnext == 0) 7100255332Scy np->in_stepnext = 1; 7101255332Scy 7102255332Scy } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) { 7103255332Scy src_search = 0; 7104255332Scy if (np->in_stepnext == 0) 7105255332Scy np->in_stepnext = 1; 7106255332Scy 7107255332Scy } else if (np->in_nsrcmsk == 0xffffffff) { 7108255332Scy src_search = 0; 7109255332Scy if (np->in_stepnext == 0) 7110255332Scy np->in_stepnext = 1; 7111255332Scy 7112255332Scy } else if (np->in_nsrcmsk != 0xffffffff) { 7113255332Scy if (np->in_stepnext == 0 && changed == -1) { 7114255332Scy np->in_snip++; 7115255332Scy np->in_stepnext++; 7116255332Scy changed = 0; 7117255332Scy } 7118255332Scy } 7119255332Scy 7120255332Scy if ((flags & IPN_TCPUDPICMP) != 0) { 7121255332Scy if (np->in_spnext != 0) 7122255332Scy frnat.fin_data[0] = np->in_spnext; 7123255332Scy 7124255332Scy /* 7125255332Scy * Standard port translation. Select next port. 7126255332Scy */ 7127255332Scy if ((flags & IPN_FIXEDSPORT) != 0) { 7128255332Scy np->in_stepnext = 2; 7129255332Scy } else if ((np->in_stepnext == 1) && 7130255332Scy (changed == -1) && (natl != NULL)) { 7131255332Scy np->in_spnext++; 7132255332Scy np->in_stepnext++; 7133255332Scy changed = 1; 7134255332Scy if (np->in_spnext > np->in_spmax) 7135255332Scy np->in_spnext = np->in_spmin; 7136255332Scy } 7137255332Scy } else { 7138255332Scy np->in_stepnext = 2; 7139255332Scy } 7140255332Scy np->in_stepnext &= 0x3; 7141255332Scy 7142255332Scy /* 7143255332Scy * Find a new destination address 7144255332Scy */ 7145255332Scy /* TRACE (fin, np, l, frnat) */ 7146338170Scy DT4(ipf_nat_rewrite_2, frinfo_t *, fin, ipnat_t *, np, int, l, frinfo_t *, &frnat); 7147255332Scy 7148255332Scy if (ipf_nat_nextaddr(fin, &np->in_ndst, &frnat.fin_daddr, 7149255332Scy &frnat.fin_daddr) == -1) 7150255332Scy return -1; 7151255332Scy if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) { 7152255332Scy dst_search = 0; 7153255332Scy if (np->in_stepnext == 2) 7154255332Scy np->in_stepnext = 3; 7155255332Scy 7156255332Scy } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0)) { 7157255332Scy dst_search = 0; 7158255332Scy if (np->in_stepnext == 2) 7159255332Scy np->in_stepnext = 3; 7160255332Scy 7161255332Scy } else if (np->in_ndstmsk == 0xffffffff) { 7162255332Scy dst_search = 0; 7163255332Scy if (np->in_stepnext == 2) 7164255332Scy np->in_stepnext = 3; 7165255332Scy 7166255332Scy } else if (np->in_ndstmsk != 0xffffffff) { 7167255332Scy if ((np->in_stepnext == 2) && (changed == -1) && 7168255332Scy (natl != NULL)) { 7169255332Scy changed = 2; 7170255332Scy np->in_stepnext++; 7171255332Scy np->in_dnip++; 7172255332Scy } 7173255332Scy } 7174255332Scy 7175255332Scy if ((flags & IPN_TCPUDPICMP) != 0) { 7176255332Scy if (np->in_dpnext != 0) 7177255332Scy frnat.fin_data[1] = np->in_dpnext; 7178255332Scy 7179255332Scy /* 7180255332Scy * Standard port translation. Select next port. 7181255332Scy */ 7182255332Scy if ((flags & IPN_FIXEDDPORT) != 0) { 7183255332Scy np->in_stepnext = 0; 7184255332Scy } else if (np->in_stepnext == 3 && changed == -1) { 7185255332Scy np->in_dpnext++; 7186255332Scy np->in_stepnext++; 7187255332Scy changed = 3; 7188255332Scy if (np->in_dpnext > np->in_dpmax) 7189255332Scy np->in_dpnext = np->in_dpmin; 7190255332Scy } 7191255332Scy } else { 7192255332Scy if (np->in_stepnext == 3) 7193255332Scy np->in_stepnext = 0; 7194255332Scy } 7195255332Scy 7196255332Scy /* TRACE (frnat) */ 7197338170Scy DT1(ipf_nat_rewrite_3, frinfo_t *, &frnat); 7198255332Scy 7199255332Scy /* 7200255332Scy * Here we do a lookup of the connection as seen from 7201255332Scy * the outside. If an IP# pair already exists, try 7202255332Scy * again. So if you have A->B becomes C->B, you can 7203255332Scy * also have D->E become C->E but not D->B causing 7204255332Scy * another C->B. Also take protocol and ports into 7205255332Scy * account when determining whether a pre-existing 7206255332Scy * NAT setup will cause an external conflict where 7207255332Scy * this is appropriate. 7208255332Scy * 7209255332Scy * fin_data[] is swapped around because we are doing a 7210255332Scy * lookup of the packet is if it were moving in the opposite 7211255332Scy * direction of the one we are working with now. 7212255332Scy */ 7213255332Scy if (flags & IPN_TCPUDP) { 7214255332Scy swap = frnat.fin_data[0]; 7215255332Scy frnat.fin_data[0] = frnat.fin_data[1]; 7216255332Scy frnat.fin_data[1] = swap; 7217255332Scy } 7218255332Scy if (fin->fin_out == 1) { 7219255332Scy natl = ipf_nat_inlookup(&frnat, 7220255332Scy flags & ~(SI_WILDP|NAT_SEARCH), 7221255332Scy (u_int)frnat.fin_p, 7222255332Scy frnat.fin_dst, frnat.fin_src); 7223255332Scy 7224255332Scy } else { 7225255332Scy natl = ipf_nat_outlookup(&frnat, 7226255332Scy flags & ~(SI_WILDP|NAT_SEARCH), 7227255332Scy (u_int)frnat.fin_p, 7228255332Scy frnat.fin_dst, frnat.fin_src); 7229255332Scy } 7230255332Scy if (flags & IPN_TCPUDP) { 7231255332Scy swap = frnat.fin_data[0]; 7232255332Scy frnat.fin_data[0] = frnat.fin_data[1]; 7233255332Scy frnat.fin_data[1] = swap; 7234255332Scy } 7235255332Scy 7236255332Scy /* TRACE natl, in_stepnext, l */ 7237338170Scy DT3(ipf_nat_rewrite_2, nat_t *, natl, ipnat_t *, np , int, l); 7238255332Scy 7239255332Scy if ((natl != NULL) && (l > 8)) /* XXX 8 is arbitrary */ 7240255332Scy return -1; 7241255332Scy 7242255332Scy np->in_stepnext &= 0x3; 7243255332Scy 7244255332Scy l++; 7245255332Scy changed = -1; 7246255332Scy } while (natl != NULL); 7247255332Scy 7248255332Scy nat->nat_osrcip = fin->fin_src; 7249255332Scy nat->nat_odstip = fin->fin_dst; 7250255332Scy nat->nat_nsrcip = frnat.fin_src; 7251255332Scy nat->nat_ndstip = frnat.fin_dst; 7252255332Scy 7253255332Scy if ((flags & IPN_TCPUDP) != 0) { 7254255332Scy nat->nat_osport = htons(fin->fin_data[0]); 7255255332Scy nat->nat_odport = htons(fin->fin_data[1]); 7256255332Scy nat->nat_nsport = htons(frnat.fin_data[0]); 7257255332Scy nat->nat_ndport = htons(frnat.fin_data[1]); 7258255332Scy } else if ((flags & IPN_ICMPQUERY) != 0) { 7259255332Scy nat->nat_oicmpid = fin->fin_data[1]; 7260255332Scy nat->nat_nicmpid = frnat.fin_data[1]; 7261255332Scy } 7262255332Scy 7263255332Scy return 0; 7264255332Scy} 7265255332Scy 7266255332Scy 7267255332Scy/* ------------------------------------------------------------------------ */ 7268255332Scy/* Function: nat_newdivert */ 7269255332Scy/* Returns: int - -1 == error, 0 == success */ 7270255332Scy/* Parameters: fin(I) - pointer to packet information */ 7271255332Scy/* nat(I) - pointer to NAT entry */ 7272255332Scy/* ni(I) - pointer to structure with misc. information needed */ 7273255332Scy/* to create new NAT entry. */ 7274255332Scy/* Write Lock: ipf_nat */ 7275255332Scy/* */ 7276255332Scy/* Create a new NAT divert session as defined by the NAT rule. This is */ 7277255332Scy/* somewhat different to other NAT session creation routines because we */ 7278255332Scy/* do not iterate through either port numbers or IP addresses, searching */ 7279255332Scy/* for a unique mapping, however, a complimentary duplicate check is made. */ 7280255332Scy/* ------------------------------------------------------------------------ */ 7281255332Scystatic int 7282255332Scyipf_nat_newdivert(fin, nat, nai) 7283255332Scy fr_info_t *fin; 7284255332Scy nat_t *nat; 7285255332Scy natinfo_t *nai; 7286255332Scy{ 7287255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 7288255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 7289255332Scy fr_info_t frnat; 7290255332Scy ipnat_t *np; 7291255332Scy nat_t *natl; 7292255332Scy int p; 7293255332Scy 7294255332Scy np = nai->nai_np; 7295255332Scy bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); 7296255332Scy 7297255332Scy nat->nat_pr[0] = 0; 7298255332Scy nat->nat_osrcaddr = fin->fin_saddr; 7299255332Scy nat->nat_odstaddr = fin->fin_daddr; 7300255332Scy frnat.fin_saddr = htonl(np->in_snip); 7301255332Scy frnat.fin_daddr = htonl(np->in_dnip); 7302255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 7303255332Scy nat->nat_osport = htons(fin->fin_data[0]); 7304255332Scy nat->nat_odport = htons(fin->fin_data[1]); 7305255332Scy } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { 7306255332Scy nat->nat_oicmpid = fin->fin_data[1]; 7307255332Scy } 7308255332Scy 7309255332Scy if (np->in_redir & NAT_DIVERTUDP) { 7310255332Scy frnat.fin_data[0] = np->in_spnext; 7311255332Scy frnat.fin_data[1] = np->in_dpnext; 7312255332Scy frnat.fin_flx |= FI_TCPUDP; 7313255332Scy p = IPPROTO_UDP; 7314255332Scy } else { 7315255332Scy frnat.fin_flx &= ~FI_TCPUDP; 7316255332Scy p = IPPROTO_IPIP; 7317255332Scy } 7318255332Scy 7319255332Scy if (fin->fin_out == 1) { 7320255332Scy natl = ipf_nat_inlookup(&frnat, 0, p, 7321255332Scy frnat.fin_dst, frnat.fin_src); 7322255332Scy 7323255332Scy } else { 7324255332Scy natl = ipf_nat_outlookup(&frnat, 0, p, 7325255332Scy frnat.fin_dst, frnat.fin_src); 7326255332Scy } 7327255332Scy 7328255332Scy if (natl != NULL) { 7329255332Scy NBUMPSIDED(fin->fin_out, ns_divert_exist); 7330338170Scy DT3(ns_divert_exist, fr_info_t *, fin, nat_t *, nat, natinfo_t, nai); 7331255332Scy return -1; 7332255332Scy } 7333255332Scy 7334255332Scy nat->nat_nsrcaddr = frnat.fin_saddr; 7335255332Scy nat->nat_ndstaddr = frnat.fin_daddr; 7336255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 7337255332Scy nat->nat_nsport = htons(frnat.fin_data[0]); 7338255332Scy nat->nat_ndport = htons(frnat.fin_data[1]); 7339255332Scy } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { 7340255332Scy nat->nat_nicmpid = frnat.fin_data[1]; 7341255332Scy } 7342255332Scy 7343255332Scy nat->nat_pr[fin->fin_out] = fin->fin_p; 7344255332Scy nat->nat_pr[1 - fin->fin_out] = p; 7345255332Scy 7346255332Scy if (np->in_redir & NAT_REDIRECT) 7347255332Scy nat->nat_dir = NAT_DIVERTIN; 7348255332Scy else 7349255332Scy nat->nat_dir = NAT_DIVERTOUT; 7350255332Scy 7351255332Scy return 0; 7352255332Scy} 7353255332Scy 7354255332Scy 7355255332Scy/* ------------------------------------------------------------------------ */ 7356255332Scy/* Function: nat_builddivertmp */ 7357255332Scy/* Returns: int - -1 == error, 0 == success */ 7358255332Scy/* Parameters: softn(I) - pointer to NAT context structure */ 7359255332Scy/* np(I) - pointer to a NAT rule */ 7360255332Scy/* */ 7361255332Scy/* For divert rules, a skeleton packet representing what will be prepended */ 7362255332Scy/* to the real packet is created. Even though we don't have the full */ 7363255332Scy/* packet here, a checksum is calculated that we update later when we */ 7364255332Scy/* fill in the final details. At present a 0 checksum for UDP is being set */ 7365255332Scy/* here because it is expected that divert will be used for localhost. */ 7366255332Scy/* ------------------------------------------------------------------------ */ 7367255332Scystatic int 7368255332Scyipf_nat_builddivertmp(softn, np) 7369255332Scy ipf_nat_softc_t *softn; 7370255332Scy ipnat_t *np; 7371255332Scy{ 7372255332Scy udphdr_t *uh; 7373255332Scy size_t len; 7374255332Scy ip_t *ip; 7375255332Scy 7376255332Scy if ((np->in_redir & NAT_DIVERTUDP) != 0) 7377255332Scy len = sizeof(ip_t) + sizeof(udphdr_t); 7378255332Scy else 7379255332Scy len = sizeof(ip_t); 7380255332Scy 7381255332Scy ALLOC_MB_T(np->in_divmp, len); 7382255332Scy if (np->in_divmp == NULL) { 7383255332Scy NBUMPD(ipf_nat_stats, ns_divert_build); 7384255332Scy return -1; 7385255332Scy } 7386255332Scy 7387255332Scy /* 7388255332Scy * First, the header to get the packet diverted to the new destination 7389255332Scy */ 7390255332Scy ip = MTOD(np->in_divmp, ip_t *); 7391255332Scy IP_V_A(ip, 4); 7392255332Scy IP_HL_A(ip, 5); 7393255332Scy ip->ip_tos = 0; 7394255332Scy if ((np->in_redir & NAT_DIVERTUDP) != 0) 7395255332Scy ip->ip_p = IPPROTO_UDP; 7396255332Scy else 7397255332Scy ip->ip_p = IPPROTO_IPIP; 7398255332Scy ip->ip_ttl = 255; 7399255332Scy ip->ip_off = 0; 7400255332Scy ip->ip_sum = 0; 7401255332Scy ip->ip_len = htons(len); 7402255332Scy ip->ip_id = 0; 7403255332Scy ip->ip_src.s_addr = htonl(np->in_snip); 7404255332Scy ip->ip_dst.s_addr = htonl(np->in_dnip); 7405255332Scy ip->ip_sum = ipf_cksum((u_short *)ip, sizeof(*ip)); 7406255332Scy 7407255332Scy if (np->in_redir & NAT_DIVERTUDP) { 7408255332Scy uh = (udphdr_t *)(ip + 1); 7409255332Scy uh->uh_sum = 0; 7410255332Scy uh->uh_ulen = 8; 7411255332Scy uh->uh_sport = htons(np->in_spnext); 7412255332Scy uh->uh_dport = htons(np->in_dpnext); 7413255332Scy } 7414255332Scy 7415255332Scy return 0; 7416255332Scy} 7417255332Scy 7418255332Scy 7419255332Scy#define MINDECAP (sizeof(ip_t) + sizeof(udphdr_t) + sizeof(ip_t)) 7420255332Scy 7421255332Scy/* ------------------------------------------------------------------------ */ 7422255332Scy/* Function: nat_decap */ 7423255332Scy/* Returns: int - -1 == error, 0 == success */ 7424255332Scy/* Parameters: fin(I) - pointer to packet information */ 7425255332Scy/* nat(I) - pointer to current NAT session */ 7426255332Scy/* */ 7427255332Scy/* This function is responsible for undoing a packet's encapsulation in the */ 7428255332Scy/* reverse of an encap/divert rule. After removing the outer encapsulation */ 7429255332Scy/* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/ 7430255332Scy/* match the "new" packet as it may still be used by IPFilter elsewhere. */ 7431255332Scy/* We use "dir" here as the basis for some of the expectations about the */ 7432255332Scy/* outer header. If we return an error, the goal is to leave the original */ 7433255332Scy/* packet information undisturbed - this falls short at the end where we'd */ 7434255332Scy/* need to back a backup copy of "fin" - expensive. */ 7435255332Scy/* ------------------------------------------------------------------------ */ 7436255332Scystatic int 7437255332Scyipf_nat_decap(fin, nat) 7438255332Scy fr_info_t *fin; 7439255332Scy nat_t *nat; 7440255332Scy{ 7441255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 7442255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 7443255332Scy char *hdr; 7444255332Scy int hlen; 7445255332Scy int skip; 7446255332Scy mb_t *m; 7447255332Scy 7448255332Scy if ((fin->fin_flx & FI_ICMPERR) != 0) { 7449255332Scy /* 7450255332Scy * ICMP packets don't get decapsulated, instead what we need 7451255332Scy * to do is change the ICMP reply from including (in the data 7452255332Scy * portion for errors) the encapsulated packet that we sent 7453255332Scy * out to something that resembles the original packet prior 7454255332Scy * to encapsulation. This isn't done here - all we're doing 7455255332Scy * here is changing the outer address to ensure that it gets 7456255332Scy * targetted back to the correct system. 7457255332Scy */ 7458255332Scy 7459255332Scy if (nat->nat_dir & NAT_OUTBOUND) { 7460255332Scy u_32_t sum1, sum2, sumd; 7461255332Scy 7462255332Scy sum1 = ntohl(fin->fin_daddr); 7463255332Scy sum2 = ntohl(nat->nat_osrcaddr); 7464255332Scy CALC_SUMD(sum1, sum2, sumd); 7465255332Scy fin->fin_ip->ip_dst = nat->nat_osrcip; 7466255332Scy fin->fin_daddr = nat->nat_osrcaddr; 7467255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 7468255332Scy defined(__osf__) || defined(linux) 7469255332Scy ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, sumd, 0); 7470255332Scy#endif 7471255332Scy } 7472255332Scy return 0; 7473255332Scy } 7474255332Scy 7475255332Scy m = fin->fin_m; 7476255332Scy skip = fin->fin_hlen; 7477255332Scy 7478255332Scy switch (nat->nat_dir) 7479255332Scy { 7480255332Scy case NAT_DIVERTIN : 7481255332Scy case NAT_DIVERTOUT : 7482255332Scy if (fin->fin_plen < MINDECAP) 7483255332Scy return -1; 7484255332Scy skip += sizeof(udphdr_t); 7485255332Scy break; 7486255332Scy 7487255332Scy case NAT_ENCAPIN : 7488255332Scy case NAT_ENCAPOUT : 7489255332Scy if (fin->fin_plen < (skip + sizeof(ip_t))) 7490255332Scy return -1; 7491255332Scy break; 7492255332Scy default : 7493255332Scy return -1; 7494255332Scy /* NOTREACHED */ 7495255332Scy } 7496255332Scy 7497255332Scy /* 7498255332Scy * The aim here is to keep the original packet details in "fin" for 7499255332Scy * as long as possible so that returning with an error is for the 7500255332Scy * original packet and there is little undoing work to do. 7501255332Scy */ 7502255332Scy if (M_LEN(m) < skip + sizeof(ip_t)) { 7503255332Scy if (ipf_pr_pullup(fin, skip + sizeof(ip_t)) == -1) 7504255332Scy return -1; 7505255332Scy } 7506255332Scy 7507255332Scy hdr = MTOD(fin->fin_m, char *); 7508255332Scy fin->fin_ip = (ip_t *)(hdr + skip); 7509255332Scy hlen = IP_HL(fin->fin_ip) << 2; 7510255332Scy 7511255332Scy if (ipf_pr_pullup(fin, skip + hlen) == -1) { 7512255332Scy NBUMPSIDED(fin->fin_out, ns_decap_pullup); 7513255332Scy return -1; 7514255332Scy } 7515255332Scy 7516255332Scy fin->fin_hlen = hlen; 7517255332Scy fin->fin_dlen -= skip; 7518255332Scy fin->fin_plen -= skip; 7519255332Scy fin->fin_ipoff += skip; 7520255332Scy 7521255332Scy if (ipf_makefrip(hlen, (ip_t *)hdr, fin) == -1) { 7522255332Scy NBUMPSIDED(fin->fin_out, ns_decap_bad); 7523255332Scy return -1; 7524255332Scy } 7525255332Scy 7526255332Scy return skip; 7527255332Scy} 7528255332Scy 7529255332Scy 7530255332Scy/* ------------------------------------------------------------------------ */ 7531255332Scy/* Function: nat_nextaddr */ 7532255332Scy/* Returns: int - -1 == bad input (no new address), */ 7533255332Scy/* 0 == success and dst has new address */ 7534255332Scy/* Parameters: fin(I) - pointer to packet information */ 7535255332Scy/* na(I) - how to generate new address */ 7536255332Scy/* old(I) - original address being replaced */ 7537255332Scy/* dst(O) - where to put the new address */ 7538255332Scy/* Write Lock: ipf_nat */ 7539255332Scy/* */ 7540255332Scy/* This function uses the contents of the "na" structure, in combination */ 7541255332Scy/* with "old" to produce a new address to store in "dst". Not all of the */ 7542255332Scy/* possible uses of "na" will result in a new address. */ 7543255332Scy/* ------------------------------------------------------------------------ */ 7544255332Scystatic int 7545255332Scyipf_nat_nextaddr(fin, na, old, dst) 7546255332Scy fr_info_t *fin; 7547255332Scy nat_addr_t *na; 7548255332Scy u_32_t *old, *dst; 7549255332Scy{ 7550255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 7551255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 7552255332Scy u_32_t amin, amax, new; 7553255332Scy i6addr_t newip; 7554255332Scy int error; 7555255332Scy 7556255332Scy new = 0; 7557255332Scy amin = na->na_addr[0].in4.s_addr; 7558255332Scy 7559255332Scy switch (na->na_atype) 7560255332Scy { 7561255332Scy case FRI_RANGE : 7562255332Scy amax = na->na_addr[1].in4.s_addr; 7563255332Scy break; 7564255332Scy 7565255332Scy case FRI_NETMASKED : 7566255332Scy case FRI_DYNAMIC : 7567255332Scy case FRI_NORMAL : 7568255332Scy /* 7569255332Scy * Compute the maximum address by adding the inverse of the 7570255332Scy * netmask to the minimum address. 7571255332Scy */ 7572255332Scy amax = ~na->na_addr[1].in4.s_addr; 7573255332Scy amax |= amin; 7574255332Scy break; 7575255332Scy 7576255332Scy case FRI_LOOKUP : 7577255332Scy break; 7578255332Scy 7579255332Scy case FRI_BROADCAST : 7580255332Scy case FRI_PEERADDR : 7581255332Scy case FRI_NETWORK : 7582255332Scy default : 7583338170Scy DT4(ns_na_atype, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new); 7584255332Scy return -1; 7585255332Scy } 7586255332Scy 7587255332Scy error = -1; 7588255332Scy 7589255332Scy if (na->na_atype == FRI_LOOKUP) { 7590255332Scy if (na->na_type == IPLT_DSTLIST) { 7591255332Scy error = ipf_dstlist_select_node(fin, na->na_ptr, dst, 7592255332Scy NULL); 7593255332Scy } else { 7594255332Scy NBUMPSIDE(fin->fin_out, ns_badnextaddr); 7595338170Scy DT4(ns_badnextaddr_1, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new); 7596255332Scy } 7597255332Scy 7598255332Scy } else if (na->na_atype == IPLT_NONE) { 7599255332Scy /* 7600255332Scy * 0/0 as the new address means leave it alone. 7601255332Scy */ 7602255332Scy if (na->na_addr[0].in4.s_addr == 0 && 7603255332Scy na->na_addr[1].in4.s_addr == 0) { 7604255332Scy new = *old; 7605255332Scy 7606255332Scy /* 7607255332Scy * 0/32 means get the interface's address 7608255332Scy */ 7609255332Scy } else if (na->na_addr[0].in4.s_addr == 0 && 7610255332Scy na->na_addr[1].in4.s_addr == 0xffffffff) { 7611255332Scy if (ipf_ifpaddr(softc, 4, na->na_atype, 7612255332Scy fin->fin_ifp, &newip, NULL) == -1) { 7613255332Scy NBUMPSIDED(fin->fin_out, ns_ifpaddrfail); 7614338170Scy DT4(ns_ifpaddrfail, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new); 7615255332Scy return -1; 7616255332Scy } 7617255332Scy new = newip.in4.s_addr; 7618255332Scy } else { 7619255332Scy new = htonl(na->na_nextip); 7620255332Scy } 7621255332Scy *dst = new; 7622255332Scy error = 0; 7623255332Scy 7624255332Scy } else { 7625255332Scy NBUMPSIDE(fin->fin_out, ns_badnextaddr); 7626338170Scy DT4(ns_badnextaddr_2, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new); 7627255332Scy } 7628255332Scy 7629255332Scy return error; 7630255332Scy} 7631255332Scy 7632255332Scy 7633255332Scy/* ------------------------------------------------------------------------ */ 7634255332Scy/* Function: nat_nextaddrinit */ 7635255332Scy/* Returns: int - 0 == success, else error number */ 7636255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7637255332Scy/* na(I) - NAT address information for generating new addr*/ 7638255332Scy/* initial(I) - flag indicating if it is the first call for */ 7639255332Scy/* this "na" structure. */ 7640255332Scy/* ifp(I) - network interface to derive address */ 7641255332Scy/* information from. */ 7642255332Scy/* */ 7643255332Scy/* This function is expected to be called in two scenarious: when a new NAT */ 7644255332Scy/* rule is loaded into the kernel and when the list of NAT rules is sync'd */ 7645255332Scy/* up with the valid network interfaces (possibly due to them changing.) */ 7646255332Scy/* To distinguish between these, the "initial" parameter is used. If it is */ 7647255332Scy/* 1 then this indicates the rule has just been reloaded and 0 for when we */ 7648255332Scy/* are updating information. This difference is important because in */ 7649255332Scy/* instances where we are not updating address information associated with */ 7650255332Scy/* a network interface, we don't want to disturb what the "next" address to */ 7651255332Scy/* come out of ipf_nat_nextaddr() will be. */ 7652255332Scy/* ------------------------------------------------------------------------ */ 7653255332Scystatic int 7654255332Scyipf_nat_nextaddrinit(softc, base, na, initial, ifp) 7655255332Scy ipf_main_softc_t *softc; 7656255332Scy char *base; 7657255332Scy nat_addr_t *na; 7658255332Scy int initial; 7659255332Scy void *ifp; 7660255332Scy{ 7661255332Scy 7662255332Scy switch (na->na_atype) 7663255332Scy { 7664255332Scy case FRI_LOOKUP : 7665255332Scy if (na->na_subtype == 0) { 7666255332Scy na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT, 7667255332Scy na->na_type, 7668255332Scy na->na_num, 7669255332Scy &na->na_func); 7670255332Scy } else if (na->na_subtype == 1) { 7671255332Scy na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT, 7672255332Scy na->na_type, 7673255332Scy base + na->na_num, 7674255332Scy &na->na_func); 7675255332Scy } 7676255332Scy if (na->na_func == NULL) { 7677255332Scy IPFERROR(60060); 7678255332Scy return ESRCH; 7679255332Scy } 7680255332Scy if (na->na_ptr == NULL) { 7681255332Scy IPFERROR(60056); 7682255332Scy return ESRCH; 7683255332Scy } 7684255332Scy break; 7685255332Scy 7686255332Scy case FRI_DYNAMIC : 7687255332Scy case FRI_BROADCAST : 7688255332Scy case FRI_NETWORK : 7689255332Scy case FRI_NETMASKED : 7690255332Scy case FRI_PEERADDR : 7691255332Scy if (ifp != NULL) 7692255332Scy (void )ipf_ifpaddr(softc, 4, na->na_atype, ifp, 7693255332Scy &na->na_addr[0], &na->na_addr[1]); 7694255332Scy break; 7695255332Scy 7696255332Scy case FRI_SPLIT : 7697255332Scy case FRI_RANGE : 7698255332Scy if (initial) 7699255332Scy na->na_nextip = ntohl(na->na_addr[0].in4.s_addr); 7700255332Scy break; 7701255332Scy 7702255332Scy case FRI_NONE : 7703255332Scy na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr; 7704255332Scy return 0; 7705255332Scy 7706255332Scy case FRI_NORMAL : 7707255332Scy na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr; 7708255332Scy break; 7709255332Scy 7710255332Scy default : 7711255332Scy IPFERROR(60054); 7712255332Scy return EINVAL; 7713255332Scy } 7714255332Scy 7715255332Scy if (initial && (na->na_atype == FRI_NORMAL)) { 7716255332Scy if (na->na_addr[0].in4.s_addr == 0) { 7717255332Scy if ((na->na_addr[1].in4.s_addr == 0xffffffff) || 7718255332Scy (na->na_addr[1].in4.s_addr == 0)) { 7719255332Scy return 0; 7720255332Scy } 7721255332Scy } 7722255332Scy 7723255332Scy if (na->na_addr[1].in4.s_addr == 0xffffffff) { 7724255332Scy na->na_nextip = ntohl(na->na_addr[0].in4.s_addr); 7725255332Scy } else { 7726255332Scy na->na_nextip = ntohl(na->na_addr[0].in4.s_addr) + 1; 7727255332Scy } 7728255332Scy } 7729255332Scy 7730255332Scy return 0; 7731255332Scy} 7732255332Scy 7733255332Scy 7734255332Scy/* ------------------------------------------------------------------------ */ 7735255332Scy/* Function: ipf_nat_matchflush */ 7736255332Scy/* Returns: int - -1 == error, 0 == success */ 7737255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7738255332Scy/* softn(I) - pointer to NAT context structure */ 7739255332Scy/* nat(I) - pointer to current NAT session */ 7740255332Scy/* */ 7741255332Scy/* ------------------------------------------------------------------------ */ 7742255332Scystatic int 7743255332Scyipf_nat_matchflush(softc, softn, data) 7744255332Scy ipf_main_softc_t *softc; 7745255332Scy ipf_nat_softc_t *softn; 7746255332Scy caddr_t data; 7747255332Scy{ 7748255332Scy int *array, flushed, error; 7749255332Scy nat_t *nat, *natnext; 7750255332Scy ipfobj_t obj; 7751255332Scy 7752255332Scy error = ipf_matcharray_load(softc, data, &obj, &array); 7753255332Scy if (error != 0) 7754255332Scy return error; 7755255332Scy 7756255332Scy flushed = 0; 7757255332Scy 7758255332Scy for (nat = softn->ipf_nat_instances; nat != NULL; nat = natnext) { 7759255332Scy natnext = nat->nat_next; 7760255332Scy if (ipf_nat_matcharray(nat, array, softc->ipf_ticks) == 0) { 7761255332Scy ipf_nat_delete(softc, nat, NL_FLUSH); 7762255332Scy flushed++; 7763255332Scy } 7764255332Scy } 7765255332Scy 7766255332Scy obj.ipfo_retval = flushed; 7767255332Scy error = BCOPYOUT(&obj, data, sizeof(obj)); 7768255332Scy 7769255332Scy KFREES(array, array[0] * sizeof(*array)); 7770255332Scy 7771255332Scy return error; 7772255332Scy} 7773255332Scy 7774255332Scy 7775255332Scy/* ------------------------------------------------------------------------ */ 7776255332Scy/* Function: ipf_nat_matcharray */ 7777255332Scy/* Returns: int - -1 == error, 0 == success */ 7778255332Scy/* Parameters: fin(I) - pointer to packet information */ 7779255332Scy/* nat(I) - pointer to current NAT session */ 7780255332Scy/* */ 7781255332Scy/* ------------------------------------------------------------------------ */ 7782255332Scystatic int 7783255332Scyipf_nat_matcharray(nat, array, ticks) 7784255332Scy nat_t *nat; 7785255332Scy int *array; 7786255332Scy u_long ticks; 7787255332Scy{ 7788255332Scy int i, n, *x, e, p; 7789255332Scy 7790255332Scy e = 0; 7791255332Scy n = array[0]; 7792255332Scy x = array + 1; 7793255332Scy 7794255332Scy for (; n > 0; x += 3 + x[2]) { 7795255332Scy if (x[0] == IPF_EXP_END) 7796255332Scy break; 7797255332Scy e = 0; 7798255332Scy 7799255332Scy n -= x[2] + 3; 7800255332Scy if (n < 0) 7801255332Scy break; 7802255332Scy 7803255332Scy p = x[0] >> 16; 7804255332Scy if (p != 0 && p != nat->nat_pr[1]) 7805255332Scy break; 7806255332Scy 7807255332Scy switch (x[0]) 7808255332Scy { 7809255332Scy case IPF_EXP_IP_PR : 7810255332Scy for (i = 0; !e && i < x[2]; i++) { 7811255332Scy e |= (nat->nat_pr[1] == x[i + 3]); 7812255332Scy } 7813255332Scy break; 7814255332Scy 7815255332Scy case IPF_EXP_IP_SRCADDR : 7816255332Scy if (nat->nat_v[0] == 4) { 7817255332Scy for (i = 0; !e && i < x[2]; i++) { 7818255332Scy e |= ((nat->nat_osrcaddr & x[i + 4]) == 7819255332Scy x[i + 3]); 7820255332Scy } 7821255332Scy } 7822255332Scy if (nat->nat_v[1] == 4) { 7823255332Scy for (i = 0; !e && i < x[2]; i++) { 7824255332Scy e |= ((nat->nat_nsrcaddr & x[i + 4]) == 7825255332Scy x[i + 3]); 7826255332Scy } 7827255332Scy } 7828255332Scy break; 7829255332Scy 7830255332Scy case IPF_EXP_IP_DSTADDR : 7831255332Scy if (nat->nat_v[0] == 4) { 7832255332Scy for (i = 0; !e && i < x[2]; i++) { 7833255332Scy e |= ((nat->nat_odstaddr & x[i + 4]) == 7834255332Scy x[i + 3]); 7835255332Scy } 7836255332Scy } 7837255332Scy if (nat->nat_v[1] == 4) { 7838255332Scy for (i = 0; !e && i < x[2]; i++) { 7839255332Scy e |= ((nat->nat_ndstaddr & x[i + 4]) == 7840255332Scy x[i + 3]); 7841255332Scy } 7842255332Scy } 7843255332Scy break; 7844255332Scy 7845255332Scy case IPF_EXP_IP_ADDR : 7846255332Scy for (i = 0; !e && i < x[2]; i++) { 7847255332Scy if (nat->nat_v[0] == 4) { 7848255332Scy e |= ((nat->nat_osrcaddr & x[i + 4]) == 7849255332Scy x[i + 3]); 7850255332Scy } 7851255332Scy if (nat->nat_v[1] == 4) { 7852255332Scy e |= ((nat->nat_nsrcaddr & x[i + 4]) == 7853255332Scy x[i + 3]); 7854255332Scy } 7855255332Scy if (nat->nat_v[0] == 4) { 7856255332Scy e |= ((nat->nat_odstaddr & x[i + 4]) == 7857255332Scy x[i + 3]); 7858255332Scy } 7859255332Scy if (nat->nat_v[1] == 4) { 7860255332Scy e |= ((nat->nat_ndstaddr & x[i + 4]) == 7861255332Scy x[i + 3]); 7862255332Scy } 7863255332Scy } 7864255332Scy break; 7865255332Scy 7866255332Scy#ifdef USE_INET6 7867255332Scy case IPF_EXP_IP6_SRCADDR : 7868255332Scy if (nat->nat_v[0] == 6) { 7869255332Scy for (i = 0; !e && i < x[3]; i++) { 7870255332Scy e |= IP6_MASKEQ(&nat->nat_osrc6, 7871255332Scy x + i + 7, x + i + 3); 7872255332Scy } 7873255332Scy } 7874255332Scy if (nat->nat_v[1] == 6) { 7875255332Scy for (i = 0; !e && i < x[3]; i++) { 7876255332Scy e |= IP6_MASKEQ(&nat->nat_nsrc6, 7877255332Scy x + i + 7, x + i + 3); 7878255332Scy } 7879255332Scy } 7880255332Scy break; 7881255332Scy 7882255332Scy case IPF_EXP_IP6_DSTADDR : 7883255332Scy if (nat->nat_v[0] == 6) { 7884255332Scy for (i = 0; !e && i < x[3]; i++) { 7885255332Scy e |= IP6_MASKEQ(&nat->nat_odst6, 7886255332Scy x + i + 7, 7887255332Scy x + i + 3); 7888255332Scy } 7889255332Scy } 7890255332Scy if (nat->nat_v[1] == 6) { 7891255332Scy for (i = 0; !e && i < x[3]; i++) { 7892255332Scy e |= IP6_MASKEQ(&nat->nat_ndst6, 7893255332Scy x + i + 7, 7894255332Scy x + i + 3); 7895255332Scy } 7896255332Scy } 7897255332Scy break; 7898255332Scy 7899255332Scy case IPF_EXP_IP6_ADDR : 7900255332Scy for (i = 0; !e && i < x[3]; i++) { 7901255332Scy if (nat->nat_v[0] == 6) { 7902255332Scy e |= IP6_MASKEQ(&nat->nat_osrc6, 7903255332Scy x + i + 7, 7904255332Scy x + i + 3); 7905255332Scy } 7906255332Scy if (nat->nat_v[0] == 6) { 7907255332Scy e |= IP6_MASKEQ(&nat->nat_odst6, 7908255332Scy x + i + 7, 7909255332Scy x + i + 3); 7910255332Scy } 7911255332Scy if (nat->nat_v[1] == 6) { 7912255332Scy e |= IP6_MASKEQ(&nat->nat_nsrc6, 7913255332Scy x + i + 7, 7914255332Scy x + i + 3); 7915255332Scy } 7916255332Scy if (nat->nat_v[1] == 6) { 7917255332Scy e |= IP6_MASKEQ(&nat->nat_ndst6, 7918255332Scy x + i + 7, 7919255332Scy x + i + 3); 7920255332Scy } 7921255332Scy } 7922255332Scy break; 7923255332Scy#endif 7924255332Scy 7925255332Scy case IPF_EXP_UDP_PORT : 7926255332Scy case IPF_EXP_TCP_PORT : 7927255332Scy for (i = 0; !e && i < x[2]; i++) { 7928255332Scy e |= (nat->nat_nsport == x[i + 3]) || 7929255332Scy (nat->nat_ndport == x[i + 3]); 7930255332Scy } 7931255332Scy break; 7932255332Scy 7933255332Scy case IPF_EXP_UDP_SPORT : 7934255332Scy case IPF_EXP_TCP_SPORT : 7935255332Scy for (i = 0; !e && i < x[2]; i++) { 7936255332Scy e |= (nat->nat_nsport == x[i + 3]); 7937255332Scy } 7938255332Scy break; 7939255332Scy 7940255332Scy case IPF_EXP_UDP_DPORT : 7941255332Scy case IPF_EXP_TCP_DPORT : 7942255332Scy for (i = 0; !e && i < x[2]; i++) { 7943255332Scy e |= (nat->nat_ndport == x[i + 3]); 7944255332Scy } 7945255332Scy break; 7946255332Scy 7947255332Scy case IPF_EXP_TCP_STATE : 7948255332Scy for (i = 0; !e && i < x[2]; i++) { 7949255332Scy e |= (nat->nat_tcpstate[0] == x[i + 3]) || 7950255332Scy (nat->nat_tcpstate[1] == x[i + 3]); 7951255332Scy } 7952255332Scy break; 7953255332Scy 7954255332Scy case IPF_EXP_IDLE_GT : 7955255332Scy e |= (ticks - nat->nat_touched > x[3]); 7956255332Scy break; 7957255332Scy } 7958255332Scy e ^= x[1]; 7959255332Scy 7960255332Scy if (!e) 7961255332Scy break; 7962255332Scy } 7963255332Scy 7964255332Scy return e; 7965255332Scy} 7966255332Scy 7967255332Scy 7968255332Scy/* ------------------------------------------------------------------------ */ 7969255332Scy/* Function: ipf_nat_gettable */ 7970172776Sdarrenr/* Returns: int - 0 = success, else error */ 7971255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7972255332Scy/* softn(I) - pointer to NAT context structure */ 7973255332Scy/* data(I) - pointer to ioctl data */ 7974172776Sdarrenr/* */ 7975172776Sdarrenr/* This function handles ioctl requests for tables of nat information. */ 7976172776Sdarrenr/* At present the only table it deals with is the hash bucket statistics. */ 7977172776Sdarrenr/* ------------------------------------------------------------------------ */ 7978255332Scystatic int 7979255332Scyipf_nat_gettable(softc, softn, data) 7980255332Scy ipf_main_softc_t *softc; 7981255332Scy ipf_nat_softc_t *softn; 7982255332Scy char *data; 7983172776Sdarrenr{ 7984172776Sdarrenr ipftable_t table; 7985172776Sdarrenr int error; 7986172776Sdarrenr 7987255332Scy error = ipf_inobj(softc, data, NULL, &table, IPFOBJ_GTABLE); 7988172776Sdarrenr if (error != 0) 7989172776Sdarrenr return error; 7990172776Sdarrenr 7991172776Sdarrenr switch (table.ita_type) 7992172776Sdarrenr { 7993172776Sdarrenr case IPFTABLE_BUCKETS_NATIN : 7994255332Scy error = COPYOUT(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, 7995255332Scy table.ita_table, 7996255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 7997172776Sdarrenr break; 7998172776Sdarrenr 7999172776Sdarrenr case IPFTABLE_BUCKETS_NATOUT : 8000255332Scy error = COPYOUT(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, 8001255332Scy table.ita_table, 8002255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 8003172776Sdarrenr break; 8004172776Sdarrenr 8005172776Sdarrenr default : 8006255332Scy IPFERROR(60058); 8007172776Sdarrenr return EINVAL; 8008172776Sdarrenr } 8009172776Sdarrenr 8010172776Sdarrenr if (error != 0) { 8011255332Scy IPFERROR(60059); 8012172776Sdarrenr error = EFAULT; 8013172776Sdarrenr } 8014172776Sdarrenr return error; 8015172776Sdarrenr} 8016255332Scy 8017255332Scy 8018255332Scy/* ------------------------------------------------------------------------ */ 8019255332Scy/* Function: ipf_nat_settimeout */ 8020255332Scy/* Returns: int - 0 = success, else failure */ 8021255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8022255332Scy/* t(I) - pointer to tunable */ 8023255332Scy/* p(I) - pointer to new tuning data */ 8024255332Scy/* */ 8025255332Scy/* Apply the timeout change to the NAT timeout queues. */ 8026255332Scy/* ------------------------------------------------------------------------ */ 8027255332Scyint 8028255332Scyipf_nat_settimeout(softc, t, p) 8029255332Scy struct ipf_main_softc_s *softc; 8030255332Scy ipftuneable_t *t; 8031255332Scy ipftuneval_t *p; 8032255332Scy{ 8033255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 8034255332Scy 8035255332Scy if (!strncmp(t->ipft_name, "tcp_", 4)) 8036255332Scy return ipf_settimeout_tcp(t, p, softn->ipf_nat_tcptq); 8037255332Scy 8038255332Scy if (!strcmp(t->ipft_name, "udp_timeout")) { 8039255332Scy ipf_apply_timeout(&softn->ipf_nat_udptq, p->ipftu_int); 8040255332Scy } else if (!strcmp(t->ipft_name, "udp_ack_timeout")) { 8041255332Scy ipf_apply_timeout(&softn->ipf_nat_udpacktq, p->ipftu_int); 8042255332Scy } else if (!strcmp(t->ipft_name, "icmp_timeout")) { 8043255332Scy ipf_apply_timeout(&softn->ipf_nat_icmptq, p->ipftu_int); 8044255332Scy } else if (!strcmp(t->ipft_name, "icmp_ack_timeout")) { 8045255332Scy ipf_apply_timeout(&softn->ipf_nat_icmpacktq, p->ipftu_int); 8046255332Scy } else if (!strcmp(t->ipft_name, "ip_timeout")) { 8047255332Scy ipf_apply_timeout(&softn->ipf_nat_iptq, p->ipftu_int); 8048255332Scy } else { 8049255332Scy IPFERROR(60062); 8050255332Scy return ESRCH; 8051255332Scy } 8052255332Scy return 0; 8053255332Scy} 8054255332Scy 8055255332Scy 8056255332Scy/* ------------------------------------------------------------------------ */ 8057255332Scy/* Function: ipf_nat_rehash */ 8058255332Scy/* Returns: int - 0 = success, else failure */ 8059255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8060255332Scy/* t(I) - pointer to tunable */ 8061255332Scy/* p(I) - pointer to new tuning data */ 8062255332Scy/* */ 8063255332Scy/* To change the size of the basic NAT table, we need to first allocate the */ 8064255332Scy/* new tables (lest it fails and we've got nowhere to store all of the NAT */ 8065255332Scy/* sessions currently active) and then walk through the entire list and */ 8066255332Scy/* insert them into the table. There are two tables here: an inbound one */ 8067255332Scy/* and an outbound one. Each NAT entry goes into each table once. */ 8068255332Scy/* ------------------------------------------------------------------------ */ 8069255332Scyint 8070255332Scyipf_nat_rehash(softc, t, p) 8071255332Scy ipf_main_softc_t *softc; 8072255332Scy ipftuneable_t *t; 8073255332Scy ipftuneval_t *p; 8074255332Scy{ 8075255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 8076255332Scy nat_t **newtab[2], *nat, **natp; 8077255332Scy u_int *bucketlens[2]; 8078255332Scy u_int maxbucket; 8079255332Scy u_int newsize; 8080255332Scy int error; 8081255332Scy u_int hv; 8082255332Scy int i; 8083255332Scy 8084255332Scy newsize = p->ipftu_int; 8085255332Scy /* 8086255332Scy * In case there is nothing to do... 8087255332Scy */ 8088255332Scy if (newsize == softn->ipf_nat_table_sz) 8089255332Scy return 0; 8090255332Scy 8091255332Scy newtab[0] = NULL; 8092255332Scy newtab[1] = NULL; 8093255332Scy bucketlens[0] = NULL; 8094255332Scy bucketlens[1] = NULL; 8095255332Scy /* 8096255332Scy * 4 tables depend on the NAT table size: the inbound looking table, 8097255332Scy * the outbound lookup table and the hash chain length for each. 8098255332Scy */ 8099255332Scy KMALLOCS(newtab[0], nat_t **, newsize * sizeof(nat_t *)); 8100255332Scy if (newtab == NULL) { 8101255332Scy error = 60063; 8102255332Scy goto badrehash; 8103255332Scy } 8104255332Scy 8105255332Scy KMALLOCS(newtab[1], nat_t **, newsize * sizeof(nat_t *)); 8106255332Scy if (newtab == NULL) { 8107255332Scy error = 60064; 8108255332Scy goto badrehash; 8109255332Scy } 8110255332Scy 8111255332Scy KMALLOCS(bucketlens[0], u_int *, newsize * sizeof(u_int)); 8112255332Scy if (bucketlens[0] == NULL) { 8113255332Scy error = 60065; 8114255332Scy goto badrehash; 8115255332Scy } 8116255332Scy 8117255332Scy KMALLOCS(bucketlens[1], u_int *, newsize * sizeof(u_int)); 8118255332Scy if (bucketlens[1] == NULL) { 8119255332Scy error = 60066; 8120255332Scy goto badrehash; 8121255332Scy } 8122255332Scy 8123255332Scy /* 8124255332Scy * Recalculate the maximum length based on the new size. 8125255332Scy */ 8126255332Scy for (maxbucket = 0, i = newsize; i > 0; i >>= 1) 8127255332Scy maxbucket++; 8128255332Scy maxbucket *= 2; 8129255332Scy 8130255332Scy bzero((char *)newtab[0], newsize * sizeof(nat_t *)); 8131255332Scy bzero((char *)newtab[1], newsize * sizeof(nat_t *)); 8132255332Scy bzero((char *)bucketlens[0], newsize * sizeof(u_int)); 8133255332Scy bzero((char *)bucketlens[1], newsize * sizeof(u_int)); 8134255332Scy 8135255332Scy WRITE_ENTER(&softc->ipf_nat); 8136255332Scy 8137255332Scy if (softn->ipf_nat_table[0] != NULL) { 8138255332Scy KFREES(softn->ipf_nat_table[0], 8139255332Scy softn->ipf_nat_table_sz * 8140255332Scy sizeof(*softn->ipf_nat_table[0])); 8141255332Scy } 8142255332Scy softn->ipf_nat_table[0] = newtab[0]; 8143255332Scy 8144255332Scy if (softn->ipf_nat_table[1] != NULL) { 8145255332Scy KFREES(softn->ipf_nat_table[1], 8146255332Scy softn->ipf_nat_table_sz * 8147255332Scy sizeof(*softn->ipf_nat_table[1])); 8148255332Scy } 8149255332Scy softn->ipf_nat_table[1] = newtab[1]; 8150255332Scy 8151255332Scy if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) { 8152255332Scy KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, 8153255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 8154255332Scy } 8155255332Scy softn->ipf_nat_stats.ns_side[0].ns_bucketlen = bucketlens[0]; 8156255332Scy 8157255332Scy if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) { 8158255332Scy KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, 8159255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 8160255332Scy } 8161255332Scy softn->ipf_nat_stats.ns_side[1].ns_bucketlen = bucketlens[1]; 8162255332Scy 8163255332Scy#ifdef USE_INET6 8164255332Scy if (softn->ipf_nat_stats.ns_side6[0].ns_bucketlen != NULL) { 8165255332Scy KFREES(softn->ipf_nat_stats.ns_side6[0].ns_bucketlen, 8166255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 8167255332Scy } 8168255332Scy softn->ipf_nat_stats.ns_side6[0].ns_bucketlen = bucketlens[0]; 8169255332Scy 8170255332Scy if (softn->ipf_nat_stats.ns_side6[1].ns_bucketlen != NULL) { 8171255332Scy KFREES(softn->ipf_nat_stats.ns_side6[1].ns_bucketlen, 8172255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 8173255332Scy } 8174255332Scy softn->ipf_nat_stats.ns_side6[1].ns_bucketlen = bucketlens[1]; 8175255332Scy#endif 8176255332Scy 8177255332Scy softn->ipf_nat_maxbucket = maxbucket; 8178255332Scy softn->ipf_nat_table_sz = newsize; 8179255332Scy /* 8180255332Scy * Walk through the entire list of NAT table entries and put them 8181255332Scy * in the new NAT table, somewhere. Because we have a new table, 8182255332Scy * we need to restart the counter of how many chains are in use. 8183255332Scy */ 8184255332Scy softn->ipf_nat_stats.ns_side[0].ns_inuse = 0; 8185255332Scy softn->ipf_nat_stats.ns_side[1].ns_inuse = 0; 8186255332Scy#ifdef USE_INET6 8187255332Scy softn->ipf_nat_stats.ns_side6[0].ns_inuse = 0; 8188255332Scy softn->ipf_nat_stats.ns_side6[1].ns_inuse = 0; 8189255332Scy#endif 8190255332Scy 8191255332Scy for (nat = softn->ipf_nat_instances; nat != NULL; nat = nat->nat_next) { 8192255332Scy nat->nat_hnext[0] = NULL; 8193255332Scy nat->nat_phnext[0] = NULL; 8194255332Scy hv = nat->nat_hv[0] % softn->ipf_nat_table_sz; 8195255332Scy 8196255332Scy natp = &softn->ipf_nat_table[0][hv]; 8197255332Scy if (*natp) { 8198255332Scy (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 8199255332Scy } else { 8200255332Scy NBUMPSIDE(0, ns_inuse); 8201255332Scy } 8202255332Scy nat->nat_phnext[0] = natp; 8203255332Scy nat->nat_hnext[0] = *natp; 8204255332Scy *natp = nat; 8205255332Scy NBUMPSIDE(0, ns_bucketlen[hv]); 8206255332Scy 8207255332Scy nat->nat_hnext[1] = NULL; 8208255332Scy nat->nat_phnext[1] = NULL; 8209255332Scy hv = nat->nat_hv[1] % softn->ipf_nat_table_sz; 8210255332Scy 8211255332Scy natp = &softn->ipf_nat_table[1][hv]; 8212255332Scy if (*natp) { 8213255332Scy (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 8214255332Scy } else { 8215255332Scy NBUMPSIDE(1, ns_inuse); 8216255332Scy } 8217255332Scy nat->nat_phnext[1] = natp; 8218255332Scy nat->nat_hnext[1] = *natp; 8219255332Scy *natp = nat; 8220255332Scy NBUMPSIDE(1, ns_bucketlen[hv]); 8221255332Scy } 8222255332Scy RWLOCK_EXIT(&softc->ipf_nat); 8223255332Scy 8224255332Scy return 0; 8225255332Scy 8226255332Scybadrehash: 8227255332Scy if (bucketlens[1] != NULL) { 8228255332Scy KFREES(bucketlens[0], newsize * sizeof(u_int)); 8229255332Scy } 8230255332Scy if (bucketlens[0] != NULL) { 8231255332Scy KFREES(bucketlens[0], newsize * sizeof(u_int)); 8232255332Scy } 8233255332Scy if (newtab[0] != NULL) { 8234255332Scy KFREES(newtab[0], newsize * sizeof(nat_t *)); 8235255332Scy } 8236255332Scy if (newtab[1] != NULL) { 8237255332Scy KFREES(newtab[1], newsize * sizeof(nat_t *)); 8238255332Scy } 8239255332Scy IPFERROR(error); 8240255332Scy return ENOMEM; 8241255332Scy} 8242255332Scy 8243255332Scy 8244255332Scy/* ------------------------------------------------------------------------ */ 8245255332Scy/* Function: ipf_nat_rehash_rules */ 8246255332Scy/* Returns: int - 0 = success, else failure */ 8247255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8248255332Scy/* t(I) - pointer to tunable */ 8249255332Scy/* p(I) - pointer to new tuning data */ 8250255332Scy/* */ 8251255332Scy/* All of the NAT rules hang off of a hash table that is searched with a */ 8252255332Scy/* hash on address after the netmask is applied. There is a different table*/ 8253255332Scy/* for both inbound rules (rdr) and outbound (map.) The resizing will only */ 8254255332Scy/* affect one of these two tables. */ 8255255332Scy/* ------------------------------------------------------------------------ */ 8256255332Scyint 8257255332Scyipf_nat_rehash_rules(softc, t, p) 8258255332Scy ipf_main_softc_t *softc; 8259255332Scy ipftuneable_t *t; 8260255332Scy ipftuneval_t *p; 8261255332Scy{ 8262255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 8263255332Scy ipnat_t **newtab, *np, ***old, **npp; 8264255332Scy u_int newsize; 8265255332Scy u_int mask; 8266255332Scy u_int hv; 8267255332Scy 8268255332Scy newsize = p->ipftu_int; 8269255332Scy /* 8270255332Scy * In case there is nothing to do... 8271255332Scy */ 8272255332Scy if (newsize == *t->ipft_pint) 8273255332Scy return 0; 8274255332Scy 8275255332Scy /* 8276255332Scy * All inbound rules have the NAT_REDIRECT bit set in in_redir and 8277255332Scy * all outbound rules have either NAT_MAP or MAT_MAPBLK set. 8278255332Scy * This if statement allows for some more generic code to be below, 8279255332Scy * rather than two huge gobs of code that almost do the same thing. 8280255332Scy */ 8281255332Scy if (t->ipft_pint == &softn->ipf_nat_rdrrules_sz) { 8282255332Scy old = &softn->ipf_nat_rdr_rules; 8283255332Scy mask = NAT_REDIRECT; 8284255332Scy } else { 8285255332Scy old = &softn->ipf_nat_map_rules; 8286255332Scy mask = NAT_MAP|NAT_MAPBLK; 8287255332Scy } 8288255332Scy 8289255332Scy KMALLOCS(newtab, ipnat_t **, newsize * sizeof(ipnat_t *)); 8290255332Scy if (newtab == NULL) { 8291255332Scy IPFERROR(60067); 8292255332Scy return ENOMEM; 8293255332Scy } 8294255332Scy 8295255332Scy bzero((char *)newtab, newsize * sizeof(ipnat_t *)); 8296255332Scy 8297255332Scy WRITE_ENTER(&softc->ipf_nat); 8298255332Scy 8299255332Scy if (*old != NULL) { 8300255332Scy KFREES(*old, *t->ipft_pint * sizeof(ipnat_t **)); 8301255332Scy } 8302255332Scy *old = newtab; 8303255332Scy *t->ipft_pint = newsize; 8304255332Scy 8305255332Scy for (np = softn->ipf_nat_list; np != NULL; np = np->in_next) { 8306255332Scy if ((np->in_redir & mask) == 0) 8307255332Scy continue; 8308255332Scy 8309255332Scy if (np->in_redir & NAT_REDIRECT) { 8310255332Scy np->in_rnext = NULL; 8311255332Scy hv = np->in_hv[0] % newsize; 8312255332Scy for (npp = newtab + hv; *npp != NULL; ) 8313255332Scy npp = &(*npp)->in_rnext; 8314255332Scy np->in_prnext = npp; 8315255332Scy *npp = np; 8316255332Scy } 8317255332Scy if (np->in_redir & NAT_MAP) { 8318255332Scy np->in_mnext = NULL; 8319255332Scy hv = np->in_hv[1] % newsize; 8320255332Scy for (npp = newtab + hv; *npp != NULL; ) 8321255332Scy npp = &(*npp)->in_mnext; 8322255332Scy np->in_pmnext = npp; 8323255332Scy *npp = np; 8324255332Scy } 8325255332Scy 8326255332Scy } 8327255332Scy RWLOCK_EXIT(&softc->ipf_nat); 8328255332Scy 8329255332Scy return 0; 8330255332Scy} 8331255332Scy 8332255332Scy 8333255332Scy/* ------------------------------------------------------------------------ */ 8334255332Scy/* Function: ipf_nat_hostmap_rehash */ 8335255332Scy/* Returns: int - 0 = success, else failure */ 8336255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8337255332Scy/* t(I) - pointer to tunable */ 8338255332Scy/* p(I) - pointer to new tuning data */ 8339255332Scy/* */ 8340255332Scy/* Allocate and populate a new hash table that will contain a reference to */ 8341255332Scy/* all of the active IP# translations currently in place. */ 8342255332Scy/* ------------------------------------------------------------------------ */ 8343255332Scyint 8344255332Scyipf_nat_hostmap_rehash(softc, t, p) 8345255332Scy ipf_main_softc_t *softc; 8346255332Scy ipftuneable_t *t; 8347255332Scy ipftuneval_t *p; 8348255332Scy{ 8349255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 8350255332Scy hostmap_t *hm, **newtab; 8351255332Scy u_int newsize; 8352255332Scy u_int hv; 8353255332Scy 8354255332Scy newsize = p->ipftu_int; 8355255332Scy /* 8356255332Scy * In case there is nothing to do... 8357255332Scy */ 8358255332Scy if (newsize == *t->ipft_pint) 8359255332Scy return 0; 8360255332Scy 8361255332Scy KMALLOCS(newtab, hostmap_t **, newsize * sizeof(hostmap_t *)); 8362255332Scy if (newtab == NULL) { 8363255332Scy IPFERROR(60068); 8364255332Scy return ENOMEM; 8365255332Scy } 8366255332Scy 8367255332Scy bzero((char *)newtab, newsize * sizeof(hostmap_t *)); 8368255332Scy 8369255332Scy WRITE_ENTER(&softc->ipf_nat); 8370255332Scy if (softn->ipf_hm_maptable != NULL) { 8371255332Scy KFREES(softn->ipf_hm_maptable, 8372255332Scy softn->ipf_nat_hostmap_sz * sizeof(hostmap_t *)); 8373255332Scy } 8374255332Scy softn->ipf_hm_maptable = newtab; 8375255332Scy softn->ipf_nat_hostmap_sz = newsize; 8376255332Scy 8377255332Scy for (hm = softn->ipf_hm_maplist; hm != NULL; hm = hm->hm_next) { 8378255332Scy hv = hm->hm_hv % softn->ipf_nat_hostmap_sz; 8379255332Scy hm->hm_hnext = softn->ipf_hm_maptable[hv]; 8380255332Scy hm->hm_phnext = softn->ipf_hm_maptable + hv; 8381255332Scy if (softn->ipf_hm_maptable[hv] != NULL) 8382255332Scy softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; 8383255332Scy softn->ipf_hm_maptable[hv] = hm; 8384255332Scy } 8385255332Scy RWLOCK_EXIT(&softc->ipf_nat); 8386255332Scy 8387255332Scy return 0; 8388255332Scy} 8389255332Scy 8390255332Scy 8391255332Scy/* ------------------------------------------------------------------------ */ 8392255332Scy/* Function: ipf_nat_add_tq */ 8393255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8394255332Scy/* */ 8395255332Scy/* ------------------------------------------------------------------------ */ 8396255332Scyipftq_t * 8397255332Scyipf_nat_add_tq(softc, ttl) 8398255332Scy ipf_main_softc_t *softc; 8399255332Scy int ttl; 8400255332Scy{ 8401255332Scy ipf_nat_softc_t *softs = softc->ipf_nat_soft; 8402255332Scy 8403255332Scy return ipf_addtimeoutqueue(softc, &softs->ipf_nat_utqe, ttl); 8404255332Scy} 8405255332Scy 8406255332Scy/* ------------------------------------------------------------------------ */ 8407255332Scy/* Function: ipf_nat_uncreate */ 8408255332Scy/* Returns: Nil */ 8409255332Scy/* Parameters: fin(I) - pointer to packet information */ 8410255332Scy/* */ 8411255332Scy/* This function is used to remove a NAT entry from the NAT table when we */ 8412255332Scy/* decide that the create was actually in error. It is thus assumed that */ 8413255332Scy/* fin_flx will have both FI_NATED and FI_NATNEW set. Because we're dealing */ 8414255332Scy/* with the translated packet (not the original), we have to reverse the */ 8415255332Scy/* lookup. Although doing the lookup is expensive (relatively speaking), it */ 8416255332Scy/* is not anticipated that this will be a frequent occurance for normal */ 8417255332Scy/* traffic patterns. */ 8418255332Scy/* ------------------------------------------------------------------------ */ 8419255332Scyvoid 8420255332Scyipf_nat_uncreate(fin) 8421255332Scy fr_info_t *fin; 8422255332Scy{ 8423255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 8424255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 8425255332Scy int nflags; 8426255332Scy nat_t *nat; 8427255332Scy 8428255332Scy switch (fin->fin_p) 8429255332Scy { 8430255332Scy case IPPROTO_TCP : 8431255332Scy nflags = IPN_TCP; 8432255332Scy break; 8433255332Scy case IPPROTO_UDP : 8434255332Scy nflags = IPN_UDP; 8435255332Scy break; 8436255332Scy default : 8437255332Scy nflags = 0; 8438255332Scy break; 8439255332Scy } 8440255332Scy 8441255332Scy WRITE_ENTER(&softc->ipf_nat); 8442255332Scy 8443255332Scy if (fin->fin_out == 0) { 8444255332Scy nat = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p, 8445255332Scy fin->fin_dst, fin->fin_src); 8446255332Scy } else { 8447255332Scy nat = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p, 8448255332Scy fin->fin_src, fin->fin_dst); 8449255332Scy } 8450255332Scy 8451255332Scy if (nat != NULL) { 8452255332Scy NBUMPSIDE(fin->fin_out, ns_uncreate[0]); 8453255332Scy ipf_nat_delete(softc, nat, NL_DESTROY); 8454255332Scy } else { 8455255332Scy NBUMPSIDE(fin->fin_out, ns_uncreate[1]); 8456255332Scy } 8457255332Scy 8458255332Scy RWLOCK_EXIT(&softc->ipf_nat); 8459255332Scy} 8460255332Scy 8461255332Scy 8462255332Scy/* ------------------------------------------------------------------------ */ 8463255332Scy/* Function: ipf_nat_cmp_rules */ 8464255332Scy/* Returns: int - 0 == success, else rules do not match. */ 8465255332Scy/* Parameters: n1(I) - first rule to compare */ 8466255332Scy/* n2(I) - first rule to compare */ 8467255332Scy/* */ 8468255332Scy/* Compare two rules using pointers to each rule. A straight bcmp will not */ 8469255332Scy/* work as some fields (such as in_dst, in_pkts) actually do change once */ 8470255332Scy/* the rule has been loaded into the kernel. Whilst this function returns */ 8471255332Scy/* various non-zero returns, they're strictly to aid in debugging. Use of */ 8472255332Scy/* this function should simply care if the result is zero or not. */ 8473255332Scy/* ------------------------------------------------------------------------ */ 8474255332Scystatic int 8475255332Scyipf_nat_cmp_rules(n1, n2) 8476255332Scy ipnat_t *n1, *n2; 8477255332Scy{ 8478255332Scy if (n1->in_size != n2->in_size) 8479255332Scy return 1; 8480255332Scy 8481255332Scy if (bcmp((char *)&n1->in_v, (char *)&n2->in_v, 8482255332Scy offsetof(ipnat_t, in_ndst) - offsetof(ipnat_t, in_v)) != 0) 8483255332Scy return 2; 8484255332Scy 8485255332Scy if (bcmp((char *)&n1->in_tuc, (char *)&n2->in_tuc, 8486255332Scy n1->in_size - offsetof(ipnat_t, in_tuc)) != 0) 8487255332Scy return 3; 8488255332Scy if (n1->in_ndst.na_atype != n2->in_ndst.na_atype) 8489255332Scy return 5; 8490255332Scy if (n1->in_ndst.na_function != n2->in_ndst.na_function) 8491255332Scy return 6; 8492255332Scy if (bcmp((char *)&n1->in_ndst.na_addr, (char *)&n2->in_ndst.na_addr, 8493255332Scy sizeof(n1->in_ndst.na_addr))) 8494255332Scy return 7; 8495255332Scy if (n1->in_nsrc.na_atype != n2->in_nsrc.na_atype) 8496255332Scy return 8; 8497255332Scy if (n1->in_nsrc.na_function != n2->in_nsrc.na_function) 8498255332Scy return 9; 8499255332Scy if (bcmp((char *)&n1->in_nsrc.na_addr, (char *)&n2->in_nsrc.na_addr, 8500255332Scy sizeof(n1->in_nsrc.na_addr))) 8501255332Scy return 10; 8502255332Scy if (n1->in_odst.na_atype != n2->in_odst.na_atype) 8503255332Scy return 11; 8504255332Scy if (n1->in_odst.na_function != n2->in_odst.na_function) 8505255332Scy return 12; 8506255332Scy if (bcmp((char *)&n1->in_odst.na_addr, (char *)&n2->in_odst.na_addr, 8507255332Scy sizeof(n1->in_odst.na_addr))) 8508255332Scy return 13; 8509255332Scy if (n1->in_osrc.na_atype != n2->in_osrc.na_atype) 8510255332Scy return 14; 8511255332Scy if (n1->in_osrc.na_function != n2->in_osrc.na_function) 8512255332Scy return 15; 8513255332Scy if (bcmp((char *)&n1->in_osrc.na_addr, (char *)&n2->in_osrc.na_addr, 8514255332Scy sizeof(n1->in_osrc.na_addr))) 8515255332Scy return 16; 8516255332Scy return 0; 8517255332Scy} 8518255332Scy 8519255332Scy 8520255332Scy/* ------------------------------------------------------------------------ */ 8521255332Scy/* Function: ipf_nat_rule_init */ 8522255332Scy/* Returns: int - 0 == success, else rules do not match. */ 8523255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8524255332Scy/* softn(I) - pointer to NAT context structure */ 8525255332Scy/* n(I) - first rule to compare */ 8526255332Scy/* */ 8527255332Scy/* ------------------------------------------------------------------------ */ 8528255332Scystatic int 8529255332Scyipf_nat_rule_init(softc, softn, n) 8530255332Scy ipf_main_softc_t *softc; 8531255332Scy ipf_nat_softc_t *softn; 8532255332Scy ipnat_t *n; 8533255332Scy{ 8534255332Scy int error = 0; 8535255332Scy 8536255332Scy if ((n->in_flags & IPN_SIPRANGE) != 0) 8537255332Scy n->in_nsrcatype = FRI_RANGE; 8538255332Scy 8539255332Scy if ((n->in_flags & IPN_DIPRANGE) != 0) 8540255332Scy n->in_ndstatype = FRI_RANGE; 8541255332Scy 8542255332Scy if ((n->in_flags & IPN_SPLIT) != 0) 8543255332Scy n->in_ndstatype = FRI_SPLIT; 8544255332Scy 8545255332Scy if ((n->in_redir & (NAT_MAP|NAT_REWRITE|NAT_DIVERTUDP)) != 0) 8546255332Scy n->in_spnext = n->in_spmin; 8547255332Scy 8548255332Scy if ((n->in_redir & (NAT_REWRITE|NAT_DIVERTUDP)) != 0) { 8549255332Scy n->in_dpnext = n->in_dpmin; 8550255332Scy } else if (n->in_redir == NAT_REDIRECT) { 8551255332Scy n->in_dpnext = n->in_dpmin; 8552255332Scy } 8553255332Scy 8554255332Scy n->in_stepnext = 0; 8555255332Scy 8556255332Scy switch (n->in_v[0]) 8557255332Scy { 8558255332Scy case 4 : 8559255332Scy error = ipf_nat_ruleaddrinit(softc, softn, n); 8560255332Scy if (error != 0) 8561255332Scy return error; 8562255332Scy break; 8563255332Scy#ifdef USE_INET6 8564255332Scy case 6 : 8565255332Scy error = ipf_nat6_ruleaddrinit(softc, softn, n); 8566255332Scy if (error != 0) 8567255332Scy return error; 8568255332Scy break; 8569255332Scy#endif 8570255332Scy default : 8571255332Scy break; 8572255332Scy } 8573255332Scy 8574255332Scy if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) { 8575255332Scy /* 8576255332Scy * Prerecord whether or not the destination of the divert 8577255332Scy * is local or not to the interface the packet is going 8578255332Scy * to be sent out. 8579255332Scy */ 8580255332Scy n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1], 8581255332Scy n->in_ifps[1], &n->in_ndstip6); 8582255332Scy } 8583255332Scy 8584255332Scy return error; 8585255332Scy} 8586255332Scy 8587255332Scy 8588255332Scy/* ------------------------------------------------------------------------ */ 8589255332Scy/* Function: ipf_nat_rule_fini */ 8590255332Scy/* Returns: int - 0 == success, else rules do not match. */ 8591255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8592255332Scy/* n(I) - rule to work on */ 8593255332Scy/* */ 8594255332Scy/* This function is used to release any objects that were referenced during */ 8595255332Scy/* the rule initialisation. This is useful both when free'ing the rule and */ 8596255332Scy/* when handling ioctls that need to initialise these fields but not */ 8597255332Scy/* actually use them after the ioctl processing has finished. */ 8598255332Scy/* ------------------------------------------------------------------------ */ 8599255332Scystatic void 8600255332Scyipf_nat_rule_fini(softc, n) 8601255332Scy ipf_main_softc_t *softc; 8602255332Scy ipnat_t *n; 8603255332Scy{ 8604255332Scy if (n->in_odst.na_atype == FRI_LOOKUP && n->in_odst.na_ptr != NULL) 8605255332Scy ipf_lookup_deref(softc, n->in_odst.na_type, n->in_odst.na_ptr); 8606255332Scy 8607255332Scy if (n->in_osrc.na_atype == FRI_LOOKUP && n->in_osrc.na_ptr != NULL) 8608255332Scy ipf_lookup_deref(softc, n->in_osrc.na_type, n->in_osrc.na_ptr); 8609255332Scy 8610255332Scy if (n->in_ndst.na_atype == FRI_LOOKUP && n->in_ndst.na_ptr != NULL) 8611255332Scy ipf_lookup_deref(softc, n->in_ndst.na_type, n->in_ndst.na_ptr); 8612255332Scy 8613255332Scy if (n->in_nsrc.na_atype == FRI_LOOKUP && n->in_nsrc.na_ptr != NULL) 8614255332Scy ipf_lookup_deref(softc, n->in_nsrc.na_type, n->in_nsrc.na_ptr); 8615255332Scy 8616255332Scy if (n->in_divmp != NULL) 8617255332Scy FREE_MB_T(n->in_divmp); 8618255332Scy} 8619