1145522Sdarrenr/* $FreeBSD$ */ 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$"; 116172776Sdarrenr/* static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.195.2.102 2007/10/16 10:08:10 darrenr Exp $"; */ 11780482Sdarrenr#endif 11880482Sdarrenr 119145522Sdarrenr 120255332Scy#define NATFSUM(n,v,f) ((v) == 4 ? (n)->f.in4.s_addr : (n)->f.i6[0] + \ 121255332Scy (n)->f.i6[1] + (n)->f.i6[2] + (n)->f.i6[3]) 122255332Scy#define NBUMP(x) softn->(x)++ 123255332Scy#define NBUMPD(x, y) do { \ 124255332Scy softn->x.y++; \ 125255332Scy DT(y); \ 126255332Scy } while (0) 127255332Scy#define NBUMPSIDE(y,x) softn->ipf_nat_stats.ns_side[y].x++ 128255332Scy#define NBUMPSIDED(y,x) do { softn->ipf_nat_stats.ns_side[y].x++; \ 129255332Scy DT(x); } while (0) 130255332Scy#define NBUMPSIDEX(y,x,z) \ 131255332Scy do { softn->ipf_nat_stats.ns_side[y].x++; \ 132255332Scy DT(z); } while (0) 133255332Scy#define NBUMPSIDEDF(y,x)do { softn->ipf_nat_stats.ns_side[y].x++; \ 134255332Scy DT1(x, fr_info_t *, fin); } while (0) 135255332Scy 136255332Scyfrentry_t ipfnatblock; 137255332Scy 138255332Scystatic ipftuneable_t ipf_nat_tuneables[] = { 139255332Scy /* nat */ 140255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_lock) }, 141255332Scy "nat_lock", 0, 1, 142255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_lock), 143255332Scy IPFT_RDONLY, NULL, NULL }, 144255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_sz) }, 145255332Scy "nat_table_size", 1, 0x7fffffff, 146255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_table_sz), 147255332Scy 0, NULL, ipf_nat_rehash }, 148255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_max) }, 149255332Scy "nat_table_max", 1, 0x7fffffff, 150255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_table_max), 151255332Scy 0, NULL, NULL }, 152255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maprules_sz) }, 153255332Scy "nat_rules_size", 1, 0x7fffffff, 154255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_maprules_sz), 155255332Scy 0, NULL, ipf_nat_rehash_rules }, 156255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_rdrrules_sz) }, 157255332Scy "rdr_rules_size", 1, 0x7fffffff, 158255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_rdrrules_sz), 159255332Scy 0, NULL, ipf_nat_rehash_rules }, 160255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_hostmap_sz) }, 161255332Scy "hostmap_size", 1, 0x7fffffff, 162255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_hostmap_sz), 163255332Scy 0, NULL, ipf_nat_hostmap_rehash }, 164255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maxbucket) }, 165255332Scy "nat_maxbucket",1, 0x7fffffff, 166255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_maxbucket), 167255332Scy 0, NULL, NULL }, 168255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_logging) }, 169255332Scy "nat_logging", 0, 1, 170255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_logging), 171255332Scy 0, NULL, NULL }, 172255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_doflush) }, 173255332Scy "nat_doflush", 0, 1, 174255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_doflush), 175255332Scy 0, NULL, NULL }, 176255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_low) }, 177255332Scy "nat_table_wm_low", 1, 99, 178255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_low), 179255332Scy 0, NULL, NULL }, 180255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_high) }, 181255332Scy "nat_table_wm_high", 2, 100, 182255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_high), 183255332Scy 0, NULL, NULL }, 184255332Scy { { 0 }, 185255332Scy NULL, 0, 0, 186255332Scy 0, 187255332Scy 0, NULL, NULL } 188255332Scy}; 189255332Scy 190145522Sdarrenr/* ======================================================================== */ 191145522Sdarrenr/* How the NAT is organised and works. */ 192145522Sdarrenr/* */ 193145522Sdarrenr/* Inside (interface y) NAT Outside (interface x) */ 194145522Sdarrenr/* -------------------- -+- ------------------------------------- */ 195255332Scy/* Packet going | out, processsed by ipf_nat_checkout() for x */ 196145522Sdarrenr/* ------------> | ------------> */ 197145522Sdarrenr/* src=10.1.1.1 | src=192.1.1.1 */ 198145522Sdarrenr/* | */ 199255332Scy/* | in, processed by ipf_nat_checkin() for x */ 200145522Sdarrenr/* <------------ | <------------ */ 201145522Sdarrenr/* dst=10.1.1.1 | dst=192.1.1.1 */ 202145522Sdarrenr/* -------------------- -+- ------------------------------------- */ 203255332Scy/* ipf_nat_checkout() - changes ip_src and if required, sport */ 204145522Sdarrenr/* - creates a new mapping, if required. */ 205255332Scy/* ipf_nat_checkin() - changes ip_dst and if required, dport */ 206145522Sdarrenr/* */ 207145522Sdarrenr/* In the NAT table, internal source is recorded as "in" and externally */ 208145522Sdarrenr/* seen as "out". */ 209145522Sdarrenr/* ======================================================================== */ 210145522Sdarrenr 211145522Sdarrenr 212255332Scy#if SOLARIS && !defined(INSTANCES) 213255332Scyextern int pfil_delayed_copy; 214255332Scy#endif 215255332Scy 216255332Scystatic int ipf_nat_flush_entry __P((ipf_main_softc_t *, void *)); 217255332Scystatic int ipf_nat_getent __P((ipf_main_softc_t *, caddr_t, int)); 218255332Scystatic int ipf_nat_getsz __P((ipf_main_softc_t *, caddr_t, int)); 219255332Scystatic int ipf_nat_putent __P((ipf_main_softc_t *, caddr_t, int)); 220255332Scystatic void ipf_nat_addmap __P((ipf_nat_softc_t *, ipnat_t *)); 221255332Scystatic void ipf_nat_addrdr __P((ipf_nat_softc_t *, ipnat_t *)); 222255332Scystatic int ipf_nat_builddivertmp __P((ipf_nat_softc_t *, ipnat_t *)); 223255332Scystatic int ipf_nat_clearlist __P((ipf_main_softc_t *, ipf_nat_softc_t *)); 224255332Scystatic int ipf_nat_cmp_rules __P((ipnat_t *, ipnat_t *)); 225255332Scystatic int ipf_nat_decap __P((fr_info_t *, nat_t *)); 226255332Scystatic void ipf_nat_delrule __P((ipf_main_softc_t *, ipf_nat_softc_t *, 227255332Scy ipnat_t *, int)); 228255332Scystatic int ipf_nat_extraflush __P((ipf_main_softc_t *, ipf_nat_softc_t *, int)); 229255332Scystatic int ipf_nat_finalise __P((fr_info_t *, nat_t *)); 230255332Scystatic int ipf_nat_flushtable __P((ipf_main_softc_t *, ipf_nat_softc_t *)); 231255332Scystatic int ipf_nat_getnext __P((ipf_main_softc_t *, ipftoken_t *, 232255332Scy ipfgeniter_t *, ipfobj_t *)); 233255332Scystatic int ipf_nat_gettable __P((ipf_main_softc_t *, ipf_nat_softc_t *, 234255332Scy char *)); 235255332Scystatic hostmap_t *ipf_nat_hostmap __P((ipf_nat_softc_t *, ipnat_t *, 236255332Scy struct in_addr, struct in_addr, 237255332Scy struct in_addr, u_32_t)); 238255332Scystatic int ipf_nat_icmpquerytype __P((int)); 239255332Scystatic int ipf_nat_iterator __P((ipf_main_softc_t *, ipftoken_t *, 240255332Scy ipfgeniter_t *, ipfobj_t *)); 241255332Scystatic int ipf_nat_match __P((fr_info_t *, ipnat_t *)); 242255332Scystatic int ipf_nat_matcharray __P((nat_t *, int *, u_long)); 243255332Scystatic int ipf_nat_matchflush __P((ipf_main_softc_t *, ipf_nat_softc_t *, 244255332Scy caddr_t)); 245255332Scystatic void ipf_nat_mssclamp __P((tcphdr_t *, u_32_t, fr_info_t *, 246255332Scy u_short *)); 247255332Scystatic int ipf_nat_newmap __P((fr_info_t *, nat_t *, natinfo_t *)); 248255332Scystatic int ipf_nat_newdivert __P((fr_info_t *, nat_t *, natinfo_t *)); 249255332Scystatic int ipf_nat_newrdr __P((fr_info_t *, nat_t *, natinfo_t *)); 250255332Scystatic int ipf_nat_newrewrite __P((fr_info_t *, nat_t *, natinfo_t *)); 251255332Scystatic int ipf_nat_nextaddr __P((fr_info_t *, nat_addr_t *, u_32_t *, 252255332Scy u_32_t *)); 253255332Scystatic int ipf_nat_nextaddrinit __P((ipf_main_softc_t *, char *, 254255332Scy nat_addr_t *, int, void *)); 255255332Scystatic int ipf_nat_resolverule __P((ipf_main_softc_t *, ipnat_t *)); 256255332Scystatic int ipf_nat_ruleaddrinit __P((ipf_main_softc_t *, 257255332Scy ipf_nat_softc_t *, ipnat_t *)); 258255332Scystatic void ipf_nat_rule_fini __P((ipf_main_softc_t *, ipnat_t *)); 259255332Scystatic int ipf_nat_rule_init __P((ipf_main_softc_t *, ipf_nat_softc_t *, 260255332Scy ipnat_t *)); 261255332Scystatic int ipf_nat_siocaddnat __P((ipf_main_softc_t *, ipf_nat_softc_t *, 262255332Scy ipnat_t *, int)); 263255332Scystatic void ipf_nat_siocdelnat __P((ipf_main_softc_t *, ipf_nat_softc_t *, 264255332Scy ipnat_t *, int)); 265255332Scystatic void ipf_nat_tabmove __P((ipf_nat_softc_t *, nat_t *)); 266255332Scy 267255332Scy/* ------------------------------------------------------------------------ */ 268255332Scy/* Function: ipf_nat_main_load */ 269255332Scy/* Returns: int - 0 == success, -1 == failure */ 270255332Scy/* Parameters: Nil */ 271255332Scy/* */ 272255332Scy/* The only global NAT structure that needs to be initialised is the filter */ 273255332Scy/* rule that is used with blocking packets. */ 274255332Scy/* ------------------------------------------------------------------------ */ 275255332Scyint 276255332Scyipf_nat_main_load() 277255332Scy{ 278255332Scy bzero((char *)&ipfnatblock, sizeof(ipfnatblock)); 279255332Scy ipfnatblock.fr_flags = FR_BLOCK|FR_QUICK; 280255332Scy ipfnatblock.fr_ref = 1; 281255332Scy 282255332Scy return 0; 283255332Scy} 284255332Scy 285255332Scy 286255332Scy/* ------------------------------------------------------------------------ */ 287255332Scy/* Function: ipf_nat_main_unload */ 288255332Scy/* Returns: int - 0 == success, -1 == failure */ 289255332Scy/* Parameters: Nil */ 290255332Scy/* */ 291255332Scy/* A null-op function that exists as a placeholder so that the flow in */ 292255332Scy/* other functions is obvious. */ 293255332Scy/* ------------------------------------------------------------------------ */ 294255332Scyint 295255332Scyipf_nat_main_unload() 296255332Scy{ 297255332Scy return 0; 298255332Scy} 299255332Scy 300255332Scy 301255332Scy/* ------------------------------------------------------------------------ */ 302255332Scy/* Function: ipf_nat_soft_create */ 303255332Scy/* Returns: void * - NULL = failure, else pointer to NAT context */ 304255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 305255332Scy/* */ 306255332Scy/* Allocate the initial soft context structure for NAT and populate it with */ 307255332Scy/* some default values. Creating the tables is left until we call _init so */ 308255332Scy/* that sizes can be changed before we get under way. */ 309255332Scy/* ------------------------------------------------------------------------ */ 310255332Scyvoid * 311255332Scyipf_nat_soft_create(softc) 312255332Scy ipf_main_softc_t *softc; 313255332Scy{ 314255332Scy ipf_nat_softc_t *softn; 315255332Scy 316255332Scy KMALLOC(softn, ipf_nat_softc_t *); 317255332Scy if (softn == NULL) 318255332Scy return NULL; 319255332Scy 320255332Scy bzero((char *)softn, sizeof(*softn)); 321255332Scy 322255332Scy softn->ipf_nat_tune = ipf_tune_array_copy(softn, 323255332Scy sizeof(ipf_nat_tuneables), 324255332Scy ipf_nat_tuneables); 325255332Scy if (softn->ipf_nat_tune == NULL) { 326255332Scy ipf_nat_soft_destroy(softc, softn); 327255332Scy return NULL; 328255332Scy } 329255332Scy if (ipf_tune_array_link(softc, softn->ipf_nat_tune) == -1) { 330255332Scy ipf_nat_soft_destroy(softc, softn); 331255332Scy return NULL; 332255332Scy } 333255332Scy 334255332Scy softn->ipf_nat_list_tail = &softn->ipf_nat_list; 335255332Scy 336255332Scy softn->ipf_nat_table_max = NAT_TABLE_MAX; 337255332Scy softn->ipf_nat_table_sz = NAT_TABLE_SZ; 338255332Scy softn->ipf_nat_maprules_sz = NAT_SIZE; 339255332Scy softn->ipf_nat_rdrrules_sz = RDR_SIZE; 340255332Scy softn->ipf_nat_hostmap_sz = HOSTMAP_SIZE; 341255332Scy softn->ipf_nat_doflush = 0; 342145522Sdarrenr#ifdef IPFILTER_LOG 343255332Scy softn->ipf_nat_logging = 1; 344145522Sdarrenr#else 345255332Scy softn->ipf_nat_logging = 0; 346145522Sdarrenr#endif 34753642Sguido 348255332Scy softn->ipf_nat_defage = DEF_NAT_AGE; 349255332Scy softn->ipf_nat_defipage = IPF_TTLVAL(60); 350255332Scy softn->ipf_nat_deficmpage = IPF_TTLVAL(3); 351255332Scy softn->ipf_nat_table_wm_high = 99; 352255332Scy softn->ipf_nat_table_wm_low = 90; 35353642Sguido 354255332Scy return softn; 355255332Scy} 35653642Sguido 357255332Scy/* ------------------------------------------------------------------------ */ 358255332Scy/* Function: ipf_nat_soft_destroy */ 359255332Scy/* Returns: Nil */ 360255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 361255332Scy/* */ 362255332Scy/* ------------------------------------------------------------------------ */ 363255332Scyvoid 364255332Scyipf_nat_soft_destroy(softc, arg) 365255332Scy ipf_main_softc_t *softc; 366255332Scy void *arg; 367255332Scy{ 368255332Scy ipf_nat_softc_t *softn = arg; 36953642Sguido 370255332Scy if (softn->ipf_nat_tune != NULL) { 371255332Scy ipf_tune_array_unlink(softc, softn->ipf_nat_tune); 372255332Scy KFREES(softn->ipf_nat_tune, sizeof(ipf_nat_tuneables)); 373255332Scy softn->ipf_nat_tune = NULL; 374255332Scy } 375255332Scy 376255332Scy KFREE(softn); 377255332Scy} 378255332Scy 379255332Scy 380145522Sdarrenr/* ------------------------------------------------------------------------ */ 381255332Scy/* Function: ipf_nat_init */ 382145522Sdarrenr/* Returns: int - 0 == success, -1 == failure */ 383255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 384145522Sdarrenr/* */ 385145522Sdarrenr/* Initialise all of the NAT locks, tables and other structures. */ 386145522Sdarrenr/* ------------------------------------------------------------------------ */ 387255332Scyint 388255332Scyipf_nat_soft_init(softc, arg) 389255332Scy ipf_main_softc_t *softc; 390255332Scy void *arg; 39153642Sguido{ 392255332Scy ipf_nat_softc_t *softn = arg; 393255332Scy ipftq_t *tq; 394145522Sdarrenr int i; 395145522Sdarrenr 396255332Scy KMALLOCS(softn->ipf_nat_table[0], nat_t **, \ 397255332Scy sizeof(nat_t *) * softn->ipf_nat_table_sz); 398255332Scy 399255332Scy if (softn->ipf_nat_table[0] != NULL) { 400255332Scy bzero((char *)softn->ipf_nat_table[0], 401255332Scy softn->ipf_nat_table_sz * sizeof(nat_t *)); 402255332Scy } else { 40353642Sguido return -1; 404255332Scy } 40553642Sguido 406255332Scy KMALLOCS(softn->ipf_nat_table[1], nat_t **, \ 407255332Scy sizeof(nat_t *) * softn->ipf_nat_table_sz); 408255332Scy 409255332Scy if (softn->ipf_nat_table[1] != NULL) { 410255332Scy bzero((char *)softn->ipf_nat_table[1], 411255332Scy softn->ipf_nat_table_sz * sizeof(nat_t *)); 412255332Scy } else { 413145522Sdarrenr return -2; 414255332Scy } 41553642Sguido 416255332Scy KMALLOCS(softn->ipf_nat_map_rules, ipnat_t **, \ 417255332Scy sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz); 418255332Scy 419255332Scy if (softn->ipf_nat_map_rules != NULL) { 420255332Scy bzero((char *)softn->ipf_nat_map_rules, 421255332Scy softn->ipf_nat_maprules_sz * sizeof(ipnat_t *)); 422255332Scy } else { 423145522Sdarrenr return -3; 424255332Scy } 42553642Sguido 426255332Scy KMALLOCS(softn->ipf_nat_rdr_rules, ipnat_t **, \ 427255332Scy sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz); 428255332Scy 429255332Scy if (softn->ipf_nat_rdr_rules != NULL) { 430255332Scy bzero((char *)softn->ipf_nat_rdr_rules, 431255332Scy softn->ipf_nat_rdrrules_sz * sizeof(ipnat_t *)); 432255332Scy } else { 433145522Sdarrenr return -4; 434255332Scy } 43560852Sdarrenr 436255332Scy KMALLOCS(softn->ipf_hm_maptable, hostmap_t **, \ 437255332Scy sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); 438255332Scy 439255332Scy if (softn->ipf_hm_maptable != NULL) { 440255332Scy bzero((char *)softn->ipf_hm_maptable, 441255332Scy sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); 442255332Scy } else { 443145522Sdarrenr return -5; 444255332Scy } 445255332Scy softn->ipf_hm_maplist = NULL; 446145522Sdarrenr 447255332Scy KMALLOCS(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, u_int *, 448255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 449255332Scy 450255332Scy if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen == NULL) { 451145522Sdarrenr return -6; 452255332Scy } 453255332Scy bzero((char *)softn->ipf_nat_stats.ns_side[0].ns_bucketlen, 454255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 455145522Sdarrenr 456255332Scy KMALLOCS(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, u_int *, 457255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 458255332Scy 459255332Scy if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen == NULL) { 460145522Sdarrenr return -7; 461255332Scy } 462145522Sdarrenr 463255332Scy bzero((char *)softn->ipf_nat_stats.ns_side[1].ns_bucketlen, 464255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 465145522Sdarrenr 466255332Scy if (softn->ipf_nat_maxbucket == 0) { 467255332Scy for (i = softn->ipf_nat_table_sz; i > 0; i >>= 1) 468255332Scy softn->ipf_nat_maxbucket++; 469255332Scy softn->ipf_nat_maxbucket *= 2; 470145522Sdarrenr } 471145522Sdarrenr 472255332Scy ipf_sttab_init(softc, softn->ipf_nat_tcptq); 473145522Sdarrenr /* 474145522Sdarrenr * Increase this because we may have "keep state" following this too 475145522Sdarrenr * and packet storms can occur if this is removed too quickly. 476145522Sdarrenr */ 477255332Scy softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack; 478255332Scy softn->ipf_nat_tcptq[IPF_TCP_NSTATES - 1].ifq_next = 479255332Scy &softn->ipf_nat_udptq; 480145522Sdarrenr 481255332Scy IPFTQ_INIT(&softn->ipf_nat_udptq, softn->ipf_nat_defage, 482255332Scy "nat ipftq udp tab"); 483255332Scy softn->ipf_nat_udptq.ifq_next = &softn->ipf_nat_udpacktq; 484255332Scy 485255332Scy IPFTQ_INIT(&softn->ipf_nat_udpacktq, softn->ipf_nat_defage, 486255332Scy "nat ipftq udpack tab"); 487255332Scy softn->ipf_nat_udpacktq.ifq_next = &softn->ipf_nat_icmptq; 488255332Scy 489255332Scy IPFTQ_INIT(&softn->ipf_nat_icmptq, softn->ipf_nat_deficmpage, 490255332Scy "nat icmp ipftq tab"); 491255332Scy softn->ipf_nat_icmptq.ifq_next = &softn->ipf_nat_icmpacktq; 492255332Scy 493255332Scy IPFTQ_INIT(&softn->ipf_nat_icmpacktq, softn->ipf_nat_defage, 494255332Scy "nat icmpack ipftq tab"); 495255332Scy softn->ipf_nat_icmpacktq.ifq_next = &softn->ipf_nat_iptq; 496255332Scy 497255332Scy IPFTQ_INIT(&softn->ipf_nat_iptq, softn->ipf_nat_defipage, 498255332Scy "nat ip ipftq tab"); 499255332Scy softn->ipf_nat_iptq.ifq_next = &softn->ipf_nat_pending; 500255332Scy 501255332Scy IPFTQ_INIT(&softn->ipf_nat_pending, 1, "nat pending ipftq tab"); 502255332Scy softn->ipf_nat_pending.ifq_next = NULL; 503255332Scy 504255332Scy for (i = 0, tq = softn->ipf_nat_tcptq; i < IPF_TCP_NSTATES; i++, tq++) { 505255332Scy if (tq->ifq_ttl < softn->ipf_nat_deficmpage) 506255332Scy tq->ifq_ttl = softn->ipf_nat_deficmpage; 507145522Sdarrenr#ifdef LARGE_NAT 508255332Scy else if (tq->ifq_ttl > softn->ipf_nat_defage) 509255332Scy tq->ifq_ttl = softn->ipf_nat_defage; 510145522Sdarrenr#endif 511145522Sdarrenr } 512145522Sdarrenr 513145522Sdarrenr /* 514145522Sdarrenr * Increase this because we may have "keep state" following 515145522Sdarrenr * this too and packet storms can occur if this is removed 516145522Sdarrenr * too quickly. 517145522Sdarrenr */ 518255332Scy softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack; 519145522Sdarrenr 520255332Scy MUTEX_INIT(&softn->ipf_nat_new, "ipf nat new mutex"); 521255332Scy MUTEX_INIT(&softn->ipf_nat_io, "ipf nat io mutex"); 522145522Sdarrenr 523255332Scy softn->ipf_nat_inited = 1; 524145522Sdarrenr 52553642Sguido return 0; 52653642Sguido} 52753642Sguido 52853642Sguido 529145522Sdarrenr/* ------------------------------------------------------------------------ */ 530255332Scy/* Function: ipf_nat_soft_fini */ 531145522Sdarrenr/* Returns: Nil */ 532255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 533255332Scy/* */ 534255332Scy/* Free all memory used by NAT structures allocated at runtime. */ 535255332Scy/* ------------------------------------------------------------------------ */ 536255332Scyint 537255332Scyipf_nat_soft_fini(softc, arg) 538255332Scy ipf_main_softc_t *softc; 539255332Scy void *arg; 540255332Scy{ 541255332Scy ipf_nat_softc_t *softn = arg; 542255332Scy ipftq_t *ifq, *ifqnext; 543255332Scy 544255332Scy (void) ipf_nat_clearlist(softc, softn); 545255332Scy (void) ipf_nat_flushtable(softc, softn); 546255332Scy 547255332Scy /* 548255332Scy * Proxy timeout queues are not cleaned here because although they 549255332Scy * exist on the NAT list, ipf_proxy_unload is called after unload 550255332Scy * and the proxies actually are responsible for them being created. 551255332Scy * Should the proxy timeouts have their own list? There's no real 552255332Scy * justification as this is the only complication. 553255332Scy */ 554255332Scy for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) { 555255332Scy ifqnext = ifq->ifq_next; 556255332Scy if (ipf_deletetimeoutqueue(ifq) == 0) 557255332Scy ipf_freetimeoutqueue(softc, ifq); 558255332Scy } 559255332Scy 560255332Scy if (softn->ipf_nat_table[0] != NULL) { 561255332Scy KFREES(softn->ipf_nat_table[0], 562255332Scy sizeof(nat_t *) * softn->ipf_nat_table_sz); 563255332Scy softn->ipf_nat_table[0] = NULL; 564255332Scy } 565255332Scy if (softn->ipf_nat_table[1] != NULL) { 566255332Scy KFREES(softn->ipf_nat_table[1], 567255332Scy sizeof(nat_t *) * softn->ipf_nat_table_sz); 568255332Scy softn->ipf_nat_table[1] = NULL; 569255332Scy } 570255332Scy if (softn->ipf_nat_map_rules != NULL) { 571255332Scy KFREES(softn->ipf_nat_map_rules, 572255332Scy sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz); 573255332Scy softn->ipf_nat_map_rules = NULL; 574255332Scy } 575255332Scy if (softn->ipf_nat_rdr_rules != NULL) { 576255332Scy KFREES(softn->ipf_nat_rdr_rules, 577255332Scy sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz); 578255332Scy softn->ipf_nat_rdr_rules = NULL; 579255332Scy } 580255332Scy if (softn->ipf_hm_maptable != NULL) { 581255332Scy KFREES(softn->ipf_hm_maptable, 582255332Scy sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); 583255332Scy softn->ipf_hm_maptable = NULL; 584255332Scy } 585255332Scy if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) { 586255332Scy KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, 587255332Scy sizeof(u_int) * softn->ipf_nat_table_sz); 588255332Scy softn->ipf_nat_stats.ns_side[0].ns_bucketlen = NULL; 589255332Scy } 590255332Scy if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) { 591255332Scy KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, 592255332Scy sizeof(u_int) * softn->ipf_nat_table_sz); 593255332Scy softn->ipf_nat_stats.ns_side[1].ns_bucketlen = NULL; 594255332Scy } 595255332Scy 596255332Scy if (softn->ipf_nat_inited == 1) { 597255332Scy softn->ipf_nat_inited = 0; 598255332Scy ipf_sttab_destroy(softn->ipf_nat_tcptq); 599255332Scy 600255332Scy MUTEX_DESTROY(&softn->ipf_nat_new); 601255332Scy MUTEX_DESTROY(&softn->ipf_nat_io); 602255332Scy 603255332Scy MUTEX_DESTROY(&softn->ipf_nat_udptq.ifq_lock); 604255332Scy MUTEX_DESTROY(&softn->ipf_nat_udpacktq.ifq_lock); 605255332Scy MUTEX_DESTROY(&softn->ipf_nat_icmptq.ifq_lock); 606255332Scy MUTEX_DESTROY(&softn->ipf_nat_icmpacktq.ifq_lock); 607255332Scy MUTEX_DESTROY(&softn->ipf_nat_iptq.ifq_lock); 608255332Scy MUTEX_DESTROY(&softn->ipf_nat_pending.ifq_lock); 609255332Scy } 610255332Scy 611255332Scy return 0; 612255332Scy} 613255332Scy 614255332Scy 615255332Scy/* ------------------------------------------------------------------------ */ 616255332Scy/* Function: ipf_nat_setlock */ 617255332Scy/* Returns: Nil */ 618255332Scy/* Parameters: arg(I) - pointer to soft state information */ 619255332Scy/* tmp(I) - new lock value */ 620255332Scy/* */ 621255332Scy/* Set the "lock status" of NAT to the value in tmp. */ 622255332Scy/* ------------------------------------------------------------------------ */ 623255332Scyvoid 624255332Scyipf_nat_setlock(arg, tmp) 625255332Scy void *arg; 626255332Scy int tmp; 627255332Scy{ 628255332Scy ipf_nat_softc_t *softn = arg; 629255332Scy 630255332Scy softn->ipf_nat_lock = tmp; 631255332Scy} 632255332Scy 633255332Scy 634255332Scy/* ------------------------------------------------------------------------ */ 635255332Scy/* Function: ipf_nat_addrdr */ 636255332Scy/* Returns: Nil */ 637145522Sdarrenr/* Parameters: n(I) - pointer to NAT rule to add */ 638145522Sdarrenr/* */ 639145522Sdarrenr/* Adds a redirect rule to the hash table of redirect rules and the list of */ 640145522Sdarrenr/* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ 641145522Sdarrenr/* use by redirect rules. */ 642145522Sdarrenr/* ------------------------------------------------------------------------ */ 643255332Scystatic void 644255332Scyipf_nat_addrdr(softn, n) 645255332Scy ipf_nat_softc_t *softn; 646255332Scy ipnat_t *n; 64753642Sguido{ 64860852Sdarrenr ipnat_t **np; 64960852Sdarrenr u_32_t j; 65053642Sguido u_int hv; 651255332Scy u_int rhv; 65260852Sdarrenr int k; 65353642Sguido 654255332Scy if (n->in_odstatype == FRI_NORMAL) { 655255332Scy k = count4bits(n->in_odstmsk); 656255332Scy ipf_inet_mask_add(k, &softn->ipf_nat_rdr_mask); 657255332Scy j = (n->in_odstaddr & n->in_odstmsk); 658255332Scy rhv = NAT_HASH_FN(j, 0, 0xffffffff); 659255332Scy } else { 660255332Scy ipf_inet_mask_add(0, &softn->ipf_nat_rdr_mask); 661255332Scy j = 0; 662255332Scy rhv = 0; 663255332Scy } 664255332Scy hv = rhv % softn->ipf_nat_rdrrules_sz; 665255332Scy np = softn->ipf_nat_rdr_rules + hv; 66660852Sdarrenr while (*np != NULL) 66760852Sdarrenr np = &(*np)->in_rnext; 66860852Sdarrenr n->in_rnext = NULL; 66960852Sdarrenr n->in_prnext = np; 670255332Scy n->in_hv[0] = hv; 671255332Scy n->in_use++; 67260852Sdarrenr *np = n; 67353642Sguido} 67453642Sguido 67553642Sguido 676145522Sdarrenr/* ------------------------------------------------------------------------ */ 677255332Scy/* Function: ipf_nat_addmap */ 678145522Sdarrenr/* Returns: Nil */ 679145522Sdarrenr/* Parameters: n(I) - pointer to NAT rule to add */ 680145522Sdarrenr/* */ 681145522Sdarrenr/* Adds a NAT map rule to the hash table of rules and the list of loaded */ 682145522Sdarrenr/* NAT rules. Updates the bitmask indicating which netmasks are in use by */ 683145522Sdarrenr/* redirect rules. */ 684145522Sdarrenr/* ------------------------------------------------------------------------ */ 685255332Scystatic void 686255332Scyipf_nat_addmap(softn, n) 687255332Scy ipf_nat_softc_t *softn; 688255332Scy ipnat_t *n; 68960852Sdarrenr{ 69060852Sdarrenr ipnat_t **np; 69160852Sdarrenr u_32_t j; 69260852Sdarrenr u_int hv; 693255332Scy u_int rhv; 69460852Sdarrenr int k; 69560852Sdarrenr 696255332Scy if (n->in_osrcatype == FRI_NORMAL) { 697255332Scy k = count4bits(n->in_osrcmsk); 698255332Scy ipf_inet_mask_add(k, &softn->ipf_nat_map_mask); 699255332Scy j = (n->in_osrcaddr & n->in_osrcmsk); 700255332Scy rhv = NAT_HASH_FN(j, 0, 0xffffffff); 701255332Scy } else { 702255332Scy ipf_inet_mask_add(0, &softn->ipf_nat_map_mask); 703255332Scy j = 0; 704255332Scy rhv = 0; 705255332Scy } 706255332Scy hv = rhv % softn->ipf_nat_maprules_sz; 707255332Scy np = softn->ipf_nat_map_rules + hv; 70860852Sdarrenr while (*np != NULL) 70960852Sdarrenr np = &(*np)->in_mnext; 71060852Sdarrenr n->in_mnext = NULL; 71160852Sdarrenr n->in_pmnext = np; 712255332Scy n->in_hv[1] = rhv; 713255332Scy n->in_use++; 71460852Sdarrenr *np = n; 71560852Sdarrenr} 71660852Sdarrenr 71760852Sdarrenr 718145522Sdarrenr/* ------------------------------------------------------------------------ */ 719255332Scy/* Function: ipf_nat_delrdr */ 720145522Sdarrenr/* Returns: Nil */ 721145522Sdarrenr/* Parameters: n(I) - pointer to NAT rule to delete */ 722145522Sdarrenr/* */ 723145522Sdarrenr/* Removes a redirect rule from the hash table of redirect rules. */ 724145522Sdarrenr/* ------------------------------------------------------------------------ */ 725255332Scyvoid 726255332Scyipf_nat_delrdr(softn, n) 727255332Scy ipf_nat_softc_t *softn; 728255332Scy ipnat_t *n; 72960852Sdarrenr{ 730255332Scy if (n->in_odstatype == FRI_NORMAL) { 731255332Scy int k = count4bits(n->in_odstmsk); 732255332Scy ipf_inet_mask_del(k, &softn->ipf_nat_rdr_mask); 733255332Scy } else { 734255332Scy ipf_inet_mask_del(0, &softn->ipf_nat_rdr_mask); 735255332Scy } 73660852Sdarrenr if (n->in_rnext) 73760852Sdarrenr n->in_rnext->in_prnext = n->in_prnext; 73860852Sdarrenr *n->in_prnext = n->in_rnext; 739255332Scy n->in_use--; 74060852Sdarrenr} 74160852Sdarrenr 74260852Sdarrenr 743145522Sdarrenr/* ------------------------------------------------------------------------ */ 744255332Scy/* Function: ipf_nat_delmap */ 745145522Sdarrenr/* Returns: Nil */ 746145522Sdarrenr/* Parameters: n(I) - pointer to NAT rule to delete */ 747145522Sdarrenr/* */ 748145522Sdarrenr/* Removes a NAT map rule from the hash table of NAT map rules. */ 749145522Sdarrenr/* ------------------------------------------------------------------------ */ 750255332Scyvoid 751255332Scyipf_nat_delmap(softn, n) 752255332Scy ipf_nat_softc_t *softn; 753255332Scy ipnat_t *n; 75453642Sguido{ 755255332Scy if (n->in_osrcatype == FRI_NORMAL) { 756255332Scy int k = count4bits(n->in_osrcmsk); 757255332Scy ipf_inet_mask_del(k, &softn->ipf_nat_map_mask); 758255332Scy } else { 759255332Scy ipf_inet_mask_del(0, &softn->ipf_nat_map_mask); 760255332Scy } 761145522Sdarrenr if (n->in_mnext != NULL) 76260852Sdarrenr n->in_mnext->in_pmnext = n->in_pmnext; 76360852Sdarrenr *n->in_pmnext = n->in_mnext; 764255332Scy n->in_use--; 76560852Sdarrenr} 76660852Sdarrenr 76760852Sdarrenr 768145522Sdarrenr/* ------------------------------------------------------------------------ */ 769255332Scy/* Function: ipf_nat_hostmap */ 770145522Sdarrenr/* Returns: struct hostmap* - NULL if no hostmap could be created, */ 771145522Sdarrenr/* else a pointer to the hostmapping to use */ 772145522Sdarrenr/* Parameters: np(I) - pointer to NAT rule */ 773145522Sdarrenr/* real(I) - real IP address */ 774145522Sdarrenr/* map(I) - mapped IP address */ 775145522Sdarrenr/* port(I) - destination port number */ 776145522Sdarrenr/* Write Locks: ipf_nat */ 777145522Sdarrenr/* */ 778145522Sdarrenr/* Check if an ip address has already been allocated for a given mapping */ 779145522Sdarrenr/* that is not doing port based translation. If is not yet allocated, then */ 780145522Sdarrenr/* create a new entry if a non-NULL NAT rule pointer has been supplied. */ 781145522Sdarrenr/* ------------------------------------------------------------------------ */ 782255332Scystatic struct hostmap * 783255332Scyipf_nat_hostmap(softn, np, src, dst, map, port) 784255332Scy ipf_nat_softc_t *softn; 785255332Scy ipnat_t *np; 786255332Scy struct in_addr src; 787255332Scy struct in_addr dst; 788255332Scy struct in_addr map; 789255332Scy u_32_t port; 79060852Sdarrenr{ 79160852Sdarrenr hostmap_t *hm; 792255332Scy u_int hv, rhv; 79353642Sguido 794145522Sdarrenr hv = (src.s_addr ^ dst.s_addr); 795145522Sdarrenr hv += src.s_addr; 796145522Sdarrenr hv += dst.s_addr; 797255332Scy rhv = hv; 798255332Scy hv %= softn->ipf_nat_hostmap_sz; 799255332Scy for (hm = softn->ipf_hm_maptable[hv]; hm; hm = hm->hm_hnext) 800255332Scy if ((hm->hm_osrcip.s_addr == src.s_addr) && 801255332Scy (hm->hm_odstip.s_addr == dst.s_addr) && 802145522Sdarrenr ((np == NULL) || (np == hm->hm_ipnat)) && 803145522Sdarrenr ((port == 0) || (port == hm->hm_port))) { 804255332Scy softn->ipf_nat_stats.ns_hm_addref++; 80560852Sdarrenr hm->hm_ref++; 80660852Sdarrenr return hm; 80760852Sdarrenr } 80860852Sdarrenr 809255332Scy if (np == NULL) { 810255332Scy softn->ipf_nat_stats.ns_hm_nullnp++; 811145522Sdarrenr return NULL; 812255332Scy } 813145522Sdarrenr 81460852Sdarrenr KMALLOC(hm, hostmap_t *); 81560852Sdarrenr if (hm) { 816255332Scy hm->hm_next = softn->ipf_hm_maplist; 817255332Scy hm->hm_pnext = &softn->ipf_hm_maplist; 818255332Scy if (softn->ipf_hm_maplist != NULL) 819255332Scy softn->ipf_hm_maplist->hm_pnext = &hm->hm_next; 820255332Scy softn->ipf_hm_maplist = hm; 821255332Scy hm->hm_hnext = softn->ipf_hm_maptable[hv]; 822255332Scy hm->hm_phnext = softn->ipf_hm_maptable + hv; 823255332Scy if (softn->ipf_hm_maptable[hv] != NULL) 824255332Scy softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; 825255332Scy softn->ipf_hm_maptable[hv] = hm; 82660852Sdarrenr hm->hm_ipnat = np; 827255332Scy np->in_use++; 828255332Scy hm->hm_osrcip = src; 829255332Scy hm->hm_odstip = dst; 830255332Scy hm->hm_nsrcip = map; 831255332Scy hm->hm_ndstip.s_addr = 0; 83260852Sdarrenr hm->hm_ref = 1; 833145522Sdarrenr hm->hm_port = port; 834255332Scy hm->hm_hv = rhv; 835255332Scy hm->hm_v = 4; 836255332Scy softn->ipf_nat_stats.ns_hm_new++; 837255332Scy } else { 838255332Scy softn->ipf_nat_stats.ns_hm_newfail++; 83960852Sdarrenr } 84060852Sdarrenr return hm; 84153642Sguido} 84253642Sguido 84353642Sguido 844145522Sdarrenr/* ------------------------------------------------------------------------ */ 845255332Scy/* Function: ipf_nat_hostmapdel */ 846145522Sdarrenr/* Returns: Nil */ 847170268Sdarrenr/* Parameters: hmp(I) - pointer to hostmap structure pointer */ 848145522Sdarrenr/* Write Locks: ipf_nat */ 849145522Sdarrenr/* */ 850145522Sdarrenr/* Decrement the references to this hostmap structure by one. If this */ 851145522Sdarrenr/* reaches zero then remove it and free it. */ 852145522Sdarrenr/* ------------------------------------------------------------------------ */ 853255332Scyvoid 854255332Scyipf_nat_hostmapdel(softc, hmp) 855255332Scy ipf_main_softc_t *softc; 856255332Scy struct hostmap **hmp; 85760852Sdarrenr{ 858170268Sdarrenr struct hostmap *hm; 859170268Sdarrenr 860170268Sdarrenr hm = *hmp; 861170268Sdarrenr *hmp = NULL; 862170268Sdarrenr 863145522Sdarrenr hm->hm_ref--; 86460852Sdarrenr if (hm->hm_ref == 0) { 865255332Scy ipf_nat_rule_deref(softc, &hm->hm_ipnat); 866170268Sdarrenr if (hm->hm_hnext) 867170268Sdarrenr hm->hm_hnext->hm_phnext = hm->hm_phnext; 868170268Sdarrenr *hm->hm_phnext = hm->hm_hnext; 86960852Sdarrenr if (hm->hm_next) 87060852Sdarrenr hm->hm_next->hm_pnext = hm->hm_pnext; 87160852Sdarrenr *hm->hm_pnext = hm->hm_next; 87260852Sdarrenr KFREE(hm); 87360852Sdarrenr } 87460852Sdarrenr} 87560852Sdarrenr 87660852Sdarrenr 877145522Sdarrenr/* ------------------------------------------------------------------------ */ 878255332Scy/* Function: ipf_fix_outcksum */ 879145522Sdarrenr/* Returns: Nil */ 880145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 881145522Sdarrenr/* sp(I) - location of 16bit checksum to update */ 882145522Sdarrenr/* n((I) - amount to adjust checksum by */ 883145522Sdarrenr/* */ 884145522Sdarrenr/* Adjusts the 16bit checksum by "n" for packets going out. */ 885145522Sdarrenr/* ------------------------------------------------------------------------ */ 886255332Scyvoid 887255332Scyipf_fix_outcksum(cksum, sp, n, partial) 888255332Scy int cksum; 889255332Scy u_short *sp; 890255332Scy u_32_t n, partial; 89153642Sguido{ 892145522Sdarrenr u_short sumshort; 893145522Sdarrenr u_32_t sum1; 89453642Sguido 895145522Sdarrenr if (n == 0) 89653642Sguido return; 897145522Sdarrenr 898255332Scy if (cksum == 4) { 899255332Scy *sp = 0; 90055929Sguido return; 90155929Sguido } 902255332Scy if (cksum == 2) { 903255332Scy sum1 = partial; 904255332Scy sum1 = (sum1 & 0xffff) + (sum1 >> 16); 905255332Scy *sp = htons(sum1); 906255332Scy return; 907255332Scy } 90853642Sguido sum1 = (~ntohs(*sp)) & 0xffff; 90953642Sguido sum1 += (n); 91053642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 91153642Sguido /* Again */ 91253642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 91353642Sguido sumshort = ~(u_short)sum1; 91453642Sguido *(sp) = htons(sumshort); 91553642Sguido} 91653642Sguido 91753642Sguido 918145522Sdarrenr/* ------------------------------------------------------------------------ */ 919255332Scy/* Function: ipf_fix_incksum */ 920145522Sdarrenr/* Returns: Nil */ 921145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 922145522Sdarrenr/* sp(I) - location of 16bit checksum to update */ 923145522Sdarrenr/* n((I) - amount to adjust checksum by */ 924145522Sdarrenr/* */ 925145522Sdarrenr/* Adjusts the 16bit checksum by "n" for packets going in. */ 926145522Sdarrenr/* ------------------------------------------------------------------------ */ 927255332Scyvoid 928255332Scyipf_fix_incksum(cksum, sp, n, partial) 929255332Scy int cksum; 930255332Scy u_short *sp; 931255332Scy u_32_t n, partial; 93253642Sguido{ 933145522Sdarrenr u_short sumshort; 934145522Sdarrenr u_32_t sum1; 93553642Sguido 936145522Sdarrenr if (n == 0) 93753642Sguido return; 938145522Sdarrenr 939255332Scy if (cksum == 4) { 940255332Scy *sp = 0; 94155929Sguido return; 94255929Sguido } 943255332Scy if (cksum == 2) { 944255332Scy sum1 = partial; 945255332Scy sum1 = (sum1 & 0xffff) + (sum1 >> 16); 946255332Scy *sp = htons(sum1); 947255332Scy return; 948255332Scy } 949255332Scy 95053642Sguido sum1 = (~ntohs(*sp)) & 0xffff; 95153642Sguido sum1 += ~(n) & 0xffff; 95253642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 95353642Sguido /* Again */ 95453642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 95553642Sguido sumshort = ~(u_short)sum1; 95653642Sguido *(sp) = htons(sumshort); 95753642Sguido} 95853642Sguido 95953642Sguido 960145522Sdarrenr/* ------------------------------------------------------------------------ */ 961255332Scy/* Function: ipf_fix_datacksum */ 962145522Sdarrenr/* Returns: Nil */ 963145522Sdarrenr/* Parameters: sp(I) - location of 16bit checksum to update */ 964145522Sdarrenr/* n((I) - amount to adjust checksum by */ 965145522Sdarrenr/* */ 966145522Sdarrenr/* Fix_datacksum is used *only* for the adjustments of checksums in the */ 967145522Sdarrenr/* data section of an IP packet. */ 968145522Sdarrenr/* */ 969145522Sdarrenr/* The only situation in which you need to do this is when NAT'ing an */ 970145522Sdarrenr/* ICMP error message. Such a message, contains in its body the IP header */ 971145522Sdarrenr/* of the original IP packet, that causes the error. */ 972145522Sdarrenr/* */ 973145522Sdarrenr/* You can't use fix_incksum or fix_outcksum in that case, because for the */ 974145522Sdarrenr/* kernel the data section of the ICMP error is just data, and no special */ 975145522Sdarrenr/* processing like hardware cksum or ntohs processing have been done by the */ 976145522Sdarrenr/* kernel on the data section. */ 977145522Sdarrenr/* ------------------------------------------------------------------------ */ 978255332Scyvoid 979255332Scyipf_fix_datacksum(sp, n) 980255332Scy u_short *sp; 981255332Scy u_32_t n; 98267614Sdarrenr{ 983145522Sdarrenr u_short sumshort; 984145522Sdarrenr u_32_t sum1; 98567614Sdarrenr 986145522Sdarrenr if (n == 0) 98767614Sdarrenr return; 98867614Sdarrenr 98967614Sdarrenr sum1 = (~ntohs(*sp)) & 0xffff; 99067614Sdarrenr sum1 += (n); 99167614Sdarrenr sum1 = (sum1 >> 16) + (sum1 & 0xffff); 99267614Sdarrenr /* Again */ 99367614Sdarrenr sum1 = (sum1 >> 16) + (sum1 & 0xffff); 99467614Sdarrenr sumshort = ~(u_short)sum1; 99567614Sdarrenr *(sp) = htons(sumshort); 99667614Sdarrenr} 99767614Sdarrenr 99853642Sguido 999145522Sdarrenr/* ------------------------------------------------------------------------ */ 1000255332Scy/* Function: ipf_nat_ioctl */ 1001145522Sdarrenr/* Returns: int - 0 == success, != 0 == failure */ 1002255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1003255332Scy/* data(I) - pointer to ioctl data */ 1004255332Scy/* cmd(I) - ioctl command integer */ 1005255332Scy/* mode(I) - file mode bits used with open */ 1006255332Scy/* uid(I) - uid of calling process */ 1007255332Scy/* ctx(I) - pointer used as key for finding context */ 1008145522Sdarrenr/* */ 1009145522Sdarrenr/* Processes an ioctl call made to operate on the IP Filter NAT device. */ 1010145522Sdarrenr/* ------------------------------------------------------------------------ */ 1011255332Scyint 1012255332Scyipf_nat_ioctl(softc, data, cmd, mode, uid, ctx) 1013255332Scy ipf_main_softc_t *softc; 1014255332Scy ioctlcmd_t cmd; 1015255332Scy caddr_t data; 1016255332Scy int mode, uid; 1017255332Scy void *ctx; 101853642Sguido{ 1019255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 102095418Sdarrenr int error = 0, ret, arg, getlock; 1021255332Scy ipnat_t *nat, *nt, *n; 102253642Sguido ipnat_t natd; 1023170268Sdarrenr SPL_INT(s); 102453642Sguido 1025255332Scy#if BSD_GE_YEAR(199306) && defined(_KERNEL) 1026255332Scy# if NETBSD_GE_REV(399002000) 1027170268Sdarrenr if ((mode & FWRITE) && 1028170268Sdarrenr kauth_authorize_network(curlwp->l_cred, KAUTH_NETWORK_FIREWALL, 1029170268Sdarrenr KAUTH_REQ_NETWORK_FIREWALL_FW, 1030255332Scy NULL, NULL, NULL)) 1031170268Sdarrenr# else 1032192895Sjamie# if defined(__FreeBSD_version) && (__FreeBSD_version >= 500034) 1033255332Scy if (securelevel_ge(curthread->td_ucred, 3) && (mode & FWRITE)) 1034192895Sjamie# else 1035255332Scy if ((securelevel >= 3) && (mode & FWRITE)) 1036192895Sjamie# endif 1037255332Scy# endif 1038255332Scy { 1039255332Scy IPFERROR(60001); 1040170268Sdarrenr return EPERM; 1041170268Sdarrenr } 104253642Sguido#endif 104353642Sguido 1044145522Sdarrenr#if defined(__osf__) && defined(_KERNEL) 1045145522Sdarrenr getlock = 0; 1046145522Sdarrenr#else 1047145522Sdarrenr getlock = (mode & NAT_LOCKHELD) ? 0 : 1; 1048145522Sdarrenr#endif 1049145522Sdarrenr 1050255332Scy n = NULL; 1051255332Scy nt = NULL; 1052255332Scy nat = NULL; 1053145522Sdarrenr 1054255332Scy if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT) || 1055255332Scy (cmd == (ioctlcmd_t)SIOCPURGENAT)) { 105695418Sdarrenr if (mode & NAT_SYSSPACE) { 105795418Sdarrenr bcopy(data, (char *)&natd, sizeof(natd)); 1058255332Scy nat = &natd; 105995418Sdarrenr error = 0; 106095418Sdarrenr } else { 1061255332Scy bzero(&natd, sizeof(natd)); 1062255332Scy error = ipf_inobj(softc, data, NULL, &natd, 1063255332Scy IPFOBJ_IPNAT); 1064255332Scy if (error != 0) 1065255332Scy goto done; 1066255332Scy 1067255332Scy if (natd.in_size < sizeof(ipnat_t)) { 1068255332Scy error = EINVAL; 1069255332Scy goto done; 1070255332Scy } 1071255332Scy KMALLOCS(nt, ipnat_t *, natd.in_size); 1072255332Scy if (nt == NULL) { 1073255332Scy IPFERROR(60070); 1074255332Scy error = ENOMEM; 1075255332Scy goto done; 1076255332Scy } 1077255332Scy bzero(nt, natd.in_size); 1078255332Scy error = ipf_inobjsz(softc, data, nt, IPFOBJ_IPNAT, 1079255332Scy natd.in_size); 1080255332Scy if (error) 1081255332Scy goto done; 1082255332Scy nat = nt; 108395418Sdarrenr } 108453642Sguido 1085255332Scy /* 1086255332Scy * For add/delete, look to see if the NAT entry is 1087255332Scy * already present 1088255332Scy */ 108953642Sguido nat->in_flags &= IPN_USERFLAGS; 109053642Sguido if ((nat->in_redir & NAT_MAPBLK) == 0) { 1091255332Scy if (nat->in_osrcatype == FRI_NORMAL || 1092255332Scy nat->in_osrcatype == FRI_NONE) 1093255332Scy nat->in_osrcaddr &= nat->in_osrcmsk; 1094255332Scy if (nat->in_odstatype == FRI_NORMAL || 1095255332Scy nat->in_odstatype == FRI_NONE) 1096255332Scy nat->in_odstaddr &= nat->in_odstmsk; 1097255332Scy if ((nat->in_flags & (IPN_SPLIT|IPN_SIPRANGE)) == 0) { 1098255332Scy if (nat->in_nsrcatype == FRI_NORMAL) 1099255332Scy nat->in_nsrcaddr &= nat->in_nsrcmsk; 1100255332Scy if (nat->in_ndstatype == FRI_NORMAL) 1101255332Scy nat->in_ndstaddr &= nat->in_ndstmsk; 1102255332Scy } 110353642Sguido } 1104255332Scy 1105255332Scy error = ipf_nat_rule_init(softc, softn, nat); 1106255332Scy if (error != 0) 1107255332Scy goto done; 1108255332Scy 1109255332Scy MUTEX_ENTER(&softn->ipf_nat_io); 1110255332Scy for (n = softn->ipf_nat_list; n != NULL; n = n->in_next) 1111255332Scy if (ipf_nat_cmp_rules(nat, n) == 0) 111253642Sguido break; 111353642Sguido } 111453642Sguido 111553642Sguido switch (cmd) 111653642Sguido { 111755929Sguido#ifdef IPFILTER_LOG 111855929Sguido case SIOCIPFFB : 111960852Sdarrenr { 112060852Sdarrenr int tmp; 112160852Sdarrenr 1122255332Scy if (!(mode & FWRITE)) { 1123255332Scy IPFERROR(60002); 112455929Sguido error = EPERM; 1125255332Scy } else { 1126255332Scy tmp = ipf_log_clear(softc, IPL_LOGNAT); 1127255332Scy error = BCOPYOUT(&tmp, data, sizeof(tmp)); 1128255332Scy if (error != 0) { 1129255332Scy IPFERROR(60057); 1130170268Sdarrenr error = EFAULT; 1131255332Scy } 113260852Sdarrenr } 113355929Sguido break; 113460852Sdarrenr } 1135170268Sdarrenr 1136145522Sdarrenr case SIOCSETLG : 1137255332Scy if (!(mode & FWRITE)) { 1138255332Scy IPFERROR(60003); 1139145522Sdarrenr error = EPERM; 1140255332Scy } else { 1141255332Scy error = BCOPYIN(data, &softn->ipf_nat_logging, 1142255332Scy sizeof(softn->ipf_nat_logging)); 1143170268Sdarrenr if (error != 0) 1144170268Sdarrenr error = EFAULT; 1145145522Sdarrenr } 1146145522Sdarrenr break; 1147170268Sdarrenr 1148145522Sdarrenr case SIOCGETLG : 1149255332Scy error = BCOPYOUT(&softn->ipf_nat_logging, data, 1150255332Scy sizeof(softn->ipf_nat_logging)); 1151255332Scy if (error != 0) { 1152255332Scy IPFERROR(60004); 1153170268Sdarrenr error = EFAULT; 1154255332Scy } 1155145522Sdarrenr break; 1156170268Sdarrenr 1157145522Sdarrenr case FIONREAD : 1158255332Scy arg = ipf_log_bytesused(softc, IPL_LOGNAT); 1159170268Sdarrenr error = BCOPYOUT(&arg, data, sizeof(arg)); 1160255332Scy if (error != 0) { 1161255332Scy IPFERROR(60005); 1162170268Sdarrenr error = EFAULT; 1163255332Scy } 1164145522Sdarrenr break; 116555929Sguido#endif 116653642Sguido case SIOCADNAT : 116753642Sguido if (!(mode & FWRITE)) { 1168255332Scy IPFERROR(60006); 116953642Sguido error = EPERM; 1170145522Sdarrenr } else if (n != NULL) { 1171255332Scy natd.in_flineno = n->in_flineno; 1172255332Scy (void) ipf_outobj(softc, data, &natd, IPFOBJ_IPNAT); 1173255332Scy IPFERROR(60007); 117453642Sguido error = EEXIST; 1175145522Sdarrenr } else if (nt == NULL) { 1176255332Scy IPFERROR(60008); 1177145522Sdarrenr error = ENOMEM; 117853642Sguido } 1179145522Sdarrenr if (error != 0) { 1180255332Scy MUTEX_EXIT(&softn->ipf_nat_io); 118153642Sguido break; 118253642Sguido } 1183255332Scy if (nat != nt) 1184255332Scy bcopy((char *)nat, (char *)nt, sizeof(*n)); 1185255332Scy error = ipf_nat_siocaddnat(softc, softn, nt, getlock); 1186255332Scy MUTEX_EXIT(&softn->ipf_nat_io); 1187255332Scy if (error == 0) { 1188255332Scy nat = NULL; 1189145522Sdarrenr nt = NULL; 1190255332Scy } 119153642Sguido break; 1192170268Sdarrenr 119353642Sguido case SIOCRMNAT : 1194255332Scy case SIOCPURGENAT : 119553642Sguido if (!(mode & FWRITE)) { 1196255332Scy IPFERROR(60009); 119753642Sguido error = EPERM; 119853642Sguido n = NULL; 1199145522Sdarrenr } else if (n == NULL) { 1200255332Scy IPFERROR(60010); 1201145522Sdarrenr error = ESRCH; 120253642Sguido } 1203145522Sdarrenr 1204145522Sdarrenr if (error != 0) { 1205255332Scy MUTEX_EXIT(&softn->ipf_nat_io); 120653642Sguido break; 120753642Sguido } 1208255332Scy if (cmd == (ioctlcmd_t)SIOCPURGENAT) { 1209255332Scy error = ipf_outobjsz(softc, data, n, IPFOBJ_IPNAT, 1210255332Scy n->in_size); 1211255332Scy if (error) { 1212255332Scy MUTEX_EXIT(&softn->ipf_nat_io); 1213255332Scy goto done; 1214255332Scy } 1215255332Scy n->in_flags |= IPN_PURGE; 1216255332Scy } 1217255332Scy ipf_nat_siocdelnat(softc, softn, n, getlock); 1218145522Sdarrenr 1219255332Scy MUTEX_EXIT(&softn->ipf_nat_io); 122053642Sguido n = NULL; 122153642Sguido break; 1222170268Sdarrenr 122353642Sguido case SIOCGNATS : 1224255332Scy { 1225255332Scy natstat_t *nsp = &softn->ipf_nat_stats; 1226255332Scy 1227255332Scy nsp->ns_side[0].ns_table = softn->ipf_nat_table[0]; 1228255332Scy nsp->ns_side[1].ns_table = softn->ipf_nat_table[1]; 1229255332Scy nsp->ns_list = softn->ipf_nat_list; 1230255332Scy nsp->ns_maptable = softn->ipf_hm_maptable; 1231255332Scy nsp->ns_maplist = softn->ipf_hm_maplist; 1232255332Scy nsp->ns_nattab_sz = softn->ipf_nat_table_sz; 1233255332Scy nsp->ns_nattab_max = softn->ipf_nat_table_max; 1234255332Scy nsp->ns_rultab_sz = softn->ipf_nat_maprules_sz; 1235255332Scy nsp->ns_rdrtab_sz = softn->ipf_nat_rdrrules_sz; 1236255332Scy nsp->ns_hostmap_sz = softn->ipf_nat_hostmap_sz; 1237255332Scy nsp->ns_instances = softn->ipf_nat_instances; 1238255332Scy nsp->ns_ticks = softc->ipf_ticks; 1239255332Scy#ifdef IPFILTER_LOGGING 1240255332Scy nsp->ns_log_ok = ipf_log_logok(softc, IPF_LOGNAT); 1241255332Scy nsp->ns_log_fail = ipf_log_failures(softc, IPF_LOGNAT); 1242255332Scy#else 1243255332Scy nsp->ns_log_ok = 0; 1244255332Scy nsp->ns_log_fail = 0; 1245255332Scy#endif 1246255332Scy error = ipf_outobj(softc, data, nsp, IPFOBJ_NATSTAT); 124753642Sguido break; 1248255332Scy } 1249170268Sdarrenr 125053642Sguido case SIOCGNATL : 125153642Sguido { 125253642Sguido natlookup_t nl; 125353642Sguido 1254255332Scy error = ipf_inobj(softc, data, NULL, &nl, IPFOBJ_NATLOOKUP); 1255145522Sdarrenr if (error == 0) { 1256173181Sdarrenr void *ptr; 1257173181Sdarrenr 1258173181Sdarrenr if (getlock) { 1259255332Scy READ_ENTER(&softc->ipf_nat); 1260173181Sdarrenr } 1261255332Scy 1262255332Scy switch (nl.nl_v) 1263255332Scy { 1264255332Scy case 4 : 1265255332Scy ptr = ipf_nat_lookupredir(&nl); 1266255332Scy break; 1267255332Scy#ifdef USE_INET6 1268255332Scy case 6 : 1269255332Scy ptr = ipf_nat6_lookupredir(&nl); 1270255332Scy break; 1271255332Scy#endif 1272255332Scy default: 1273255332Scy ptr = NULL; 1274255332Scy break; 1275255332Scy } 1276255332Scy 1277173181Sdarrenr if (getlock) { 1278255332Scy RWLOCK_EXIT(&softc->ipf_nat); 1279173181Sdarrenr } 1280173181Sdarrenr if (ptr != NULL) { 1281255332Scy error = ipf_outobj(softc, data, &nl, 1282255332Scy IPFOBJ_NATLOOKUP); 1283145522Sdarrenr } else { 1284255332Scy IPFERROR(60011); 1285145522Sdarrenr error = ESRCH; 1286145522Sdarrenr } 1287145522Sdarrenr } 128853642Sguido break; 128953642Sguido } 1290170268Sdarrenr 129160852Sdarrenr case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */ 129253642Sguido if (!(mode & FWRITE)) { 1293255332Scy IPFERROR(60012); 129453642Sguido error = EPERM; 129553642Sguido break; 129653642Sguido } 1297145522Sdarrenr if (getlock) { 1298255332Scy WRITE_ENTER(&softc->ipf_nat); 1299145522Sdarrenr } 1300170268Sdarrenr 1301170268Sdarrenr error = BCOPYIN(data, &arg, sizeof(arg)); 1302255332Scy if (error != 0) { 1303255332Scy IPFERROR(60013); 1304170268Sdarrenr error = EFAULT; 1305255332Scy } else { 1306170268Sdarrenr if (arg == 0) 1307255332Scy ret = ipf_nat_flushtable(softc, softn); 1308170268Sdarrenr else if (arg == 1) 1309255332Scy ret = ipf_nat_clearlist(softc, softn); 1310170268Sdarrenr else 1311255332Scy ret = ipf_nat_extraflush(softc, softn, arg); 1312255332Scy ipf_proxy_flush(softc->ipf_proxy_soft, arg); 1313170268Sdarrenr } 1314170268Sdarrenr 1315145522Sdarrenr if (getlock) { 1316255332Scy RWLOCK_EXIT(&softc->ipf_nat); 131760852Sdarrenr } 1318145522Sdarrenr if (error == 0) { 1319170268Sdarrenr error = BCOPYOUT(&ret, data, sizeof(ret)); 1320145522Sdarrenr } 132153642Sguido break; 1322170268Sdarrenr 1323255332Scy case SIOCMATCHFLUSH : 1324255332Scy if (!(mode & FWRITE)) { 1325255332Scy IPFERROR(60014); 1326255332Scy error = EPERM; 1327255332Scy break; 1328255332Scy } 1329255332Scy if (getlock) { 1330255332Scy WRITE_ENTER(&softc->ipf_nat); 1331255332Scy } 1332255332Scy 1333255332Scy error = ipf_nat_matchflush(softc, softn, data); 1334255332Scy 1335255332Scy if (getlock) { 1336255332Scy RWLOCK_EXIT(&softc->ipf_nat); 1337255332Scy } 1338255332Scy break; 1339255332Scy 1340145522Sdarrenr case SIOCPROXY : 1341255332Scy error = ipf_proxy_ioctl(softc, data, cmd, mode, ctx); 1342145522Sdarrenr break; 1343170268Sdarrenr 134460852Sdarrenr case SIOCSTLCK : 1345153876Sguido if (!(mode & FWRITE)) { 1346255332Scy IPFERROR(60015); 1347153876Sguido error = EPERM; 1348153876Sguido } else { 1349255332Scy error = ipf_lock(data, &softn->ipf_nat_lock); 1350153876Sguido } 135153642Sguido break; 1352170268Sdarrenr 135360852Sdarrenr case SIOCSTPUT : 1354153876Sguido if ((mode & FWRITE) != 0) { 1355255332Scy error = ipf_nat_putent(softc, data, getlock); 1356145522Sdarrenr } else { 1357255332Scy IPFERROR(60016); 135860852Sdarrenr error = EACCES; 1359145522Sdarrenr } 136060852Sdarrenr break; 1361170268Sdarrenr 136260852Sdarrenr case SIOCSTGSZ : 1363255332Scy if (softn->ipf_nat_lock) { 1364255332Scy error = ipf_nat_getsz(softc, data, getlock); 1365255332Scy } else { 1366255332Scy IPFERROR(60017); 136760852Sdarrenr error = EACCES; 1368255332Scy } 136960852Sdarrenr break; 1370170268Sdarrenr 137160852Sdarrenr case SIOCSTGET : 1372255332Scy if (softn->ipf_nat_lock) { 1373255332Scy error = ipf_nat_getent(softc, data, getlock); 1374255332Scy } else { 1375255332Scy IPFERROR(60018); 137660852Sdarrenr error = EACCES; 1377255332Scy } 137860852Sdarrenr break; 1379170268Sdarrenr 1380170268Sdarrenr case SIOCGENITER : 1381170268Sdarrenr { 1382170268Sdarrenr ipfgeniter_t iter; 1383170268Sdarrenr ipftoken_t *token; 1384255332Scy ipfobj_t obj; 1385170268Sdarrenr 1386255332Scy error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER); 1387255332Scy if (error != 0) 1388255332Scy break; 1389255332Scy 1390170268Sdarrenr SPL_SCHED(s); 1391255332Scy token = ipf_token_find(softc, iter.igi_type, uid, ctx); 1392255332Scy if (token != NULL) { 1393255332Scy error = ipf_nat_iterator(softc, token, &iter, &obj); 1394255332Scy WRITE_ENTER(&softc->ipf_tokens); 1395255332Scy ipf_token_deref(softc, token); 1396255332Scy RWLOCK_EXIT(&softc->ipf_tokens); 1397170268Sdarrenr } 1398170268Sdarrenr SPL_X(s); 1399170268Sdarrenr break; 1400170268Sdarrenr } 1401170268Sdarrenr 1402170268Sdarrenr case SIOCIPFDELTOK : 1403255332Scy error = BCOPYIN(data, &arg, sizeof(arg)); 1404170268Sdarrenr if (error == 0) { 1405170268Sdarrenr SPL_SCHED(s); 1406255332Scy error = ipf_token_del(softc, arg, uid, ctx); 1407170268Sdarrenr SPL_X(s); 1408170268Sdarrenr } else { 1409255332Scy IPFERROR(60019); 1410170268Sdarrenr error = EFAULT; 1411170268Sdarrenr } 1412170268Sdarrenr break; 1413170268Sdarrenr 1414170268Sdarrenr case SIOCGTQTAB : 1415255332Scy error = ipf_outobj(softc, data, softn->ipf_nat_tcptq, 1416255332Scy IPFOBJ_STATETQTAB); 1417170268Sdarrenr break; 1418170268Sdarrenr 1419172776Sdarrenr case SIOCGTABL : 1420255332Scy error = ipf_nat_gettable(softc, softn, data); 1421172776Sdarrenr break; 1422172776Sdarrenr 142353642Sguido default : 1424255332Scy IPFERROR(60020); 142553642Sguido error = EINVAL; 142653642Sguido break; 142753642Sguido } 142860852Sdarrenrdone: 1429255332Scy if (nat != NULL) 1430255332Scy ipf_nat_rule_fini(softc, nat); 1431170268Sdarrenr if (nt != NULL) 1432255332Scy KFREES(nt, nt->in_size); 143353642Sguido return error; 143453642Sguido} 143553642Sguido 143653642Sguido 1437145522Sdarrenr/* ------------------------------------------------------------------------ */ 1438255332Scy/* Function: ipf_nat_siocaddnat */ 1439145522Sdarrenr/* Returns: int - 0 == success, != 0 == failure */ 1440255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1441255332Scy/* softn(I) - pointer to NAT context structure */ 1442255332Scy/* n(I) - pointer to new NAT rule */ 1443145522Sdarrenr/* np(I) - pointer to where to insert new NAT rule */ 1444255332Scy/* getlock(I) - flag indicating if lock on is held */ 1445255332Scy/* Mutex Locks: ipf_nat_io */ 1446145522Sdarrenr/* */ 1447145522Sdarrenr/* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 1448145522Sdarrenr/* from information passed to the kernel, then add it to the appropriate */ 1449145522Sdarrenr/* NAT rule table(s). */ 1450145522Sdarrenr/* ------------------------------------------------------------------------ */ 1451255332Scystatic int 1452255332Scyipf_nat_siocaddnat(softc, softn, n, getlock) 1453255332Scy ipf_main_softc_t *softc; 1454255332Scy ipf_nat_softc_t *softn; 1455255332Scy ipnat_t *n; 1456255332Scy int getlock; 1457145522Sdarrenr{ 1458255332Scy int error = 0; 1459145522Sdarrenr 1460255332Scy if (ipf_nat_resolverule(softc, n) != 0) { 1461255332Scy IPFERROR(60022); 1462161356Sguido return ENOENT; 1463255332Scy } 1464145522Sdarrenr 1465255332Scy if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) { 1466255332Scy IPFERROR(60023); 1467145522Sdarrenr return EINVAL; 1468255332Scy } 1469145522Sdarrenr 1470255332Scy if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) { 1471145522Sdarrenr /* 1472255332Scy * Prerecord whether or not the destination of the divert 1473255332Scy * is local or not to the interface the packet is going 1474255332Scy * to be sent out. 1475145522Sdarrenr */ 1476255332Scy n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1], 1477255332Scy n->in_ifps[1], &n->in_ndstip6); 1478145522Sdarrenr } 1479145522Sdarrenr 1480145522Sdarrenr if (getlock) { 1481255332Scy WRITE_ENTER(&softc->ipf_nat); 1482145522Sdarrenr } 1483145522Sdarrenr n->in_next = NULL; 1484255332Scy n->in_pnext = softn->ipf_nat_list_tail; 1485255332Scy *n->in_pnext = n; 1486255332Scy softn->ipf_nat_list_tail = &n->in_next; 1487255332Scy n->in_use++; 1488145522Sdarrenr 1489145522Sdarrenr if (n->in_redir & NAT_REDIRECT) { 1490145522Sdarrenr n->in_flags &= ~IPN_NOTDST; 1491255332Scy switch (n->in_v[0]) 1492255332Scy { 1493255332Scy case 4 : 1494255332Scy ipf_nat_addrdr(softn, n); 1495255332Scy break; 1496255332Scy#ifdef USE_INET6 1497255332Scy case 6 : 1498255332Scy ipf_nat6_addrdr(softn, n); 1499255332Scy break; 1500255332Scy#endif 1501255332Scy default : 1502255332Scy break; 1503255332Scy } 1504255332Scy ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_rdr); 1505145522Sdarrenr } 1506255332Scy 1507145522Sdarrenr if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { 1508145522Sdarrenr n->in_flags &= ~IPN_NOTSRC; 1509255332Scy switch (n->in_v[0]) 1510255332Scy { 1511255332Scy case 4 : 1512255332Scy ipf_nat_addmap(softn, n); 1513255332Scy break; 1514255332Scy#ifdef USE_INET6 1515255332Scy case 6 : 1516255332Scy ipf_nat6_addmap(softn, n); 1517255332Scy break; 1518255332Scy#endif 1519255332Scy default : 1520255332Scy break; 1521255332Scy } 1522255332Scy ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_map); 1523145522Sdarrenr } 1524255332Scy 1525255332Scy if (n->in_age[0] != 0) 1526255332Scy n->in_tqehead[0] = ipf_addtimeoutqueue(softc, 1527255332Scy &softn->ipf_nat_utqe, 1528255332Scy n->in_age[0]); 1529255332Scy 1530255332Scy if (n->in_age[1] != 0) 1531255332Scy n->in_tqehead[1] = ipf_addtimeoutqueue(softc, 1532255332Scy &softn->ipf_nat_utqe, 1533255332Scy n->in_age[1]); 1534255332Scy 1535170268Sdarrenr MUTEX_INIT(&n->in_lock, "ipnat rule lock"); 1536170268Sdarrenr 1537145522Sdarrenr n = NULL; 1538255332Scy ATOMIC_INC32(softn->ipf_nat_stats.ns_rules); 1539255332Scy#if SOLARIS && !defined(INSTANCES) 1540145522Sdarrenr pfil_delayed_copy = 0; 1541145522Sdarrenr#endif 1542145522Sdarrenr if (getlock) { 1543255332Scy RWLOCK_EXIT(&softc->ipf_nat); /* WRITE */ 1544145522Sdarrenr } 1545145522Sdarrenr 1546145522Sdarrenr return error; 1547145522Sdarrenr} 1548145522Sdarrenr 1549145522Sdarrenr 1550145522Sdarrenr/* ------------------------------------------------------------------------ */ 1551255332Scy/* Function: ipf_nat_ruleaddrinit */ 1552255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1553255332Scy/* softn(I) - pointer to NAT context structure */ 1554255332Scy/* n(I) - pointer to NAT rule */ 1555255332Scy/* */ 1556255332Scy/* Initialise all of the NAT address structures in a NAT rule. */ 1557255332Scy/* ------------------------------------------------------------------------ */ 1558255332Scystatic int 1559255332Scyipf_nat_ruleaddrinit(softc, softn, n) 1560255332Scy ipf_main_softc_t *softc; 1561255332Scy ipf_nat_softc_t *softn; 1562255332Scy ipnat_t *n; 1563255332Scy{ 1564255332Scy int idx, error; 1565255332Scy 1566255332Scy if ((n->in_ndst.na_atype == FRI_LOOKUP) && 1567255332Scy (n->in_ndst.na_type != IPLT_DSTLIST)) { 1568255332Scy IPFERROR(60071); 1569255332Scy return EINVAL; 1570255332Scy } 1571255332Scy if ((n->in_nsrc.na_atype == FRI_LOOKUP) && 1572255332Scy (n->in_nsrc.na_type != IPLT_DSTLIST)) { 1573255332Scy IPFERROR(60069); 1574255332Scy return EINVAL; 1575255332Scy } 1576255332Scy 1577255332Scy if (n->in_redir == NAT_BIMAP) { 1578255332Scy n->in_ndstaddr = n->in_osrcaddr; 1579255332Scy n->in_ndstmsk = n->in_osrcmsk; 1580255332Scy n->in_odstaddr = n->in_nsrcaddr; 1581255332Scy n->in_odstmsk = n->in_nsrcmsk; 1582255332Scy 1583255332Scy } 1584255332Scy 1585255332Scy if (n->in_redir & NAT_REDIRECT) 1586255332Scy idx = 1; 1587255332Scy else 1588255332Scy idx = 0; 1589255332Scy /* 1590255332Scy * Initialise all of the address fields. 1591255332Scy */ 1592255332Scy error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, 1, 1593255332Scy n->in_ifps[idx]); 1594255332Scy if (error != 0) 1595255332Scy return error; 1596255332Scy 1597255332Scy error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, 1, 1598255332Scy n->in_ifps[idx]); 1599255332Scy if (error != 0) 1600255332Scy return error; 1601255332Scy 1602255332Scy error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, 1, 1603255332Scy n->in_ifps[idx]); 1604255332Scy if (error != 0) 1605255332Scy return error; 1606255332Scy 1607255332Scy error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, 1, 1608255332Scy n->in_ifps[idx]); 1609255332Scy if (error != 0) 1610255332Scy return error; 1611255332Scy 1612255332Scy if (n->in_redir & NAT_DIVERTUDP) 1613255332Scy ipf_nat_builddivertmp(softn, n); 1614255332Scy 1615255332Scy return 0; 1616255332Scy} 1617255332Scy 1618255332Scy 1619255332Scy/* ------------------------------------------------------------------------ */ 1620255332Scy/* Function: ipf_nat_resolvrule */ 1621145522Sdarrenr/* Returns: Nil */ 1622255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1623255332Scy/* n(I) - pointer to NAT rule */ 1624145522Sdarrenr/* */ 1625145522Sdarrenr/* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 1626145522Sdarrenr/* from information passed to the kernel, then add it to the appropriate */ 1627145522Sdarrenr/* NAT rule table(s). */ 1628145522Sdarrenr/* ------------------------------------------------------------------------ */ 1629255332Scystatic int 1630255332Scyipf_nat_resolverule(softc, n) 1631255332Scy ipf_main_softc_t *softc; 1632255332Scy ipnat_t *n; 1633145522Sdarrenr{ 1634255332Scy char *base; 1635145522Sdarrenr 1636255332Scy base = n->in_names; 1637255332Scy 1638255332Scy n->in_ifps[0] = ipf_resolvenic(softc, base + n->in_ifnames[0], 1639255332Scy n->in_v[0]); 1640255332Scy 1641255332Scy if (n->in_ifnames[1] == -1) { 1642255332Scy n->in_ifnames[1] = n->in_ifnames[0]; 1643145522Sdarrenr n->in_ifps[1] = n->in_ifps[0]; 1644145522Sdarrenr } else { 1645255332Scy n->in_ifps[1] = ipf_resolvenic(softc, base + n->in_ifnames[1], 1646255332Scy n->in_v[1]); 1647145522Sdarrenr } 1648145522Sdarrenr 1649255332Scy if (n->in_plabel != -1) { 1650255332Scy if (n->in_redir & NAT_REDIRECT) 1651255332Scy n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft, 1652255332Scy n->in_pr[0], 1653255332Scy base + n->in_plabel); 1654255332Scy else 1655255332Scy n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft, 1656255332Scy n->in_pr[1], 1657255332Scy base + n->in_plabel); 1658161356Sguido if (n->in_apr == NULL) 1659161356Sguido return -1; 1660145522Sdarrenr } 1661161356Sguido return 0; 1662145522Sdarrenr} 1663145522Sdarrenr 1664145522Sdarrenr 1665145522Sdarrenr/* ------------------------------------------------------------------------ */ 1666255332Scy/* Function: ipf_nat_siocdelnat */ 1667145522Sdarrenr/* Returns: int - 0 == success, != 0 == failure */ 1668255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1669255332Scy/* softn(I) - pointer to NAT context structure */ 1670255332Scy/* n(I) - pointer to new NAT rule */ 1671255332Scy/* getlock(I) - flag indicating if lock on is held */ 1672255332Scy/* Mutex Locks: ipf_nat_io */ 1673145522Sdarrenr/* */ 1674145522Sdarrenr/* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 1675145522Sdarrenr/* from information passed to the kernel, then add it to the appropriate */ 1676145522Sdarrenr/* NAT rule table(s). */ 1677145522Sdarrenr/* ------------------------------------------------------------------------ */ 1678255332Scystatic void 1679255332Scyipf_nat_siocdelnat(softc, softn, n, getlock) 1680255332Scy ipf_main_softc_t *softc; 1681255332Scy ipf_nat_softc_t *softn; 1682255332Scy ipnat_t *n; 1683255332Scy int getlock; 1684145522Sdarrenr{ 1685255332Scy#ifdef IPF_NAT6 1686255332Scy int i; 1687255332Scy#endif 1688255332Scy 1689145522Sdarrenr if (getlock) { 1690255332Scy WRITE_ENTER(&softc->ipf_nat); 1691145522Sdarrenr } 1692145522Sdarrenr 1693255332Scy ipf_nat_delrule(softc, softn, n, 1); 1694145522Sdarrenr 1695145522Sdarrenr if (getlock) { 1696255332Scy RWLOCK_EXIT(&softc->ipf_nat); /* READ/WRITE */ 1697145522Sdarrenr } 1698145522Sdarrenr} 1699145522Sdarrenr 1700145522Sdarrenr 1701145522Sdarrenr/* ------------------------------------------------------------------------ */ 1702255332Scy/* Function: ipf_nat_getsz */ 1703145522Sdarrenr/* Returns: int - 0 == success, != 0 is the error value. */ 1704255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1705255332Scy/* data(I) - pointer to natget structure with kernel */ 1706255332Scy/* pointer get the size of. */ 1707255332Scy/* getlock(I) - flag indicating whether or not the caller */ 1708255332Scy/* holds a lock on ipf_nat */ 1709145522Sdarrenr/* */ 1710145522Sdarrenr/* Handle SIOCSTGSZ. */ 1711145522Sdarrenr/* Return the size of the nat list entry to be copied back to user space. */ 1712145522Sdarrenr/* The size of the entry is stored in the ng_sz field and the enture natget */ 1713145522Sdarrenr/* structure is copied back to the user. */ 1714145522Sdarrenr/* ------------------------------------------------------------------------ */ 1715255332Scystatic int 1716255332Scyipf_nat_getsz(softc, data, getlock) 1717255332Scy ipf_main_softc_t *softc; 1718255332Scy caddr_t data; 1719255332Scy int getlock; 172060852Sdarrenr{ 1721255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 172260852Sdarrenr ap_session_t *aps; 172360852Sdarrenr nat_t *nat, *n; 172460852Sdarrenr natget_t ng; 1725255332Scy int error; 172660852Sdarrenr 1727255332Scy error = BCOPYIN(data, &ng, sizeof(ng)); 1728255332Scy if (error != 0) { 1729255332Scy IPFERROR(60024); 1730170268Sdarrenr return EFAULT; 1731255332Scy } 173260852Sdarrenr 1733173181Sdarrenr if (getlock) { 1734255332Scy READ_ENTER(&softc->ipf_nat); 1735173181Sdarrenr } 1736173181Sdarrenr 173760852Sdarrenr nat = ng.ng_ptr; 173860852Sdarrenr if (!nat) { 1739255332Scy nat = softn->ipf_nat_instances; 174060852Sdarrenr ng.ng_sz = 0; 1741145522Sdarrenr /* 1742145522Sdarrenr * Empty list so the size returned is 0. Simple. 1743145522Sdarrenr */ 174460852Sdarrenr if (nat == NULL) { 1745173181Sdarrenr if (getlock) { 1746255332Scy RWLOCK_EXIT(&softc->ipf_nat); 1747173181Sdarrenr } 1748255332Scy error = BCOPYOUT(&ng, data, sizeof(ng)); 1749255332Scy if (error != 0) { 1750255332Scy IPFERROR(60025); 1751170268Sdarrenr return EFAULT; 1752255332Scy } 1753145522Sdarrenr return 0; 175460852Sdarrenr } 175560852Sdarrenr } else { 175660852Sdarrenr /* 175760852Sdarrenr * Make sure the pointer we're copying from exists in the 175860852Sdarrenr * current list of entries. Security precaution to prevent 175960852Sdarrenr * copying of random kernel data. 176060852Sdarrenr */ 1761255332Scy for (n = softn->ipf_nat_instances; n; n = n->nat_next) 176260852Sdarrenr if (n == nat) 176360852Sdarrenr break; 1764173181Sdarrenr if (n == NULL) { 1765173181Sdarrenr if (getlock) { 1766255332Scy RWLOCK_EXIT(&softc->ipf_nat); 1767173181Sdarrenr } 1768255332Scy IPFERROR(60026); 176960852Sdarrenr return ESRCH; 1770173181Sdarrenr } 177160852Sdarrenr } 177260852Sdarrenr 1773145522Sdarrenr /* 1774145522Sdarrenr * Incluse any space required for proxy data structures. 1775145522Sdarrenr */ 177660852Sdarrenr ng.ng_sz = sizeof(nat_save_t); 177760852Sdarrenr aps = nat->nat_aps; 1778145522Sdarrenr if (aps != NULL) { 1779145522Sdarrenr ng.ng_sz += sizeof(ap_session_t) - 4; 1780145522Sdarrenr if (aps->aps_data != 0) 1781145522Sdarrenr ng.ng_sz += aps->aps_psiz; 178260852Sdarrenr } 1783173181Sdarrenr if (getlock) { 1784255332Scy RWLOCK_EXIT(&softc->ipf_nat); 1785173181Sdarrenr } 178660852Sdarrenr 1787255332Scy error = BCOPYOUT(&ng, data, sizeof(ng)); 1788255332Scy if (error != 0) { 1789255332Scy IPFERROR(60027); 1790170268Sdarrenr return EFAULT; 1791255332Scy } 1792145522Sdarrenr return 0; 179360852Sdarrenr} 179460852Sdarrenr 179560852Sdarrenr 1796145522Sdarrenr/* ------------------------------------------------------------------------ */ 1797255332Scy/* Function: ipf_nat_getent */ 1798145522Sdarrenr/* Returns: int - 0 == success, != 0 is the error value. */ 1799255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1800255332Scy/* data(I) - pointer to natget structure with kernel pointer*/ 1801255332Scy/* to NAT structure to copy out. */ 1802255332Scy/* getlock(I) - flag indicating whether or not the caller */ 1803255332Scy/* holds a lock on ipf_nat */ 1804145522Sdarrenr/* */ 1805145522Sdarrenr/* Handle SIOCSTGET. */ 1806145522Sdarrenr/* Copies out NAT entry to user space. Any additional data held for a */ 1807145522Sdarrenr/* proxy is also copied, as to is the NAT rule which was responsible for it */ 1808145522Sdarrenr/* ------------------------------------------------------------------------ */ 1809255332Scystatic int 1810255332Scyipf_nat_getent(softc, data, getlock) 1811255332Scy ipf_main_softc_t *softc; 1812255332Scy caddr_t data; 1813255332Scy int getlock; 181460852Sdarrenr{ 1815255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 1816145522Sdarrenr int error, outsize; 181760852Sdarrenr ap_session_t *aps; 1818145522Sdarrenr nat_save_t *ipn, ipns; 1819145522Sdarrenr nat_t *n, *nat; 182060852Sdarrenr 1821255332Scy error = ipf_inobj(softc, data, NULL, &ipns, IPFOBJ_NATSAVE); 1822145522Sdarrenr if (error != 0) 1823145522Sdarrenr return error; 182460852Sdarrenr 1825255332Scy if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) { 1826255332Scy IPFERROR(60028); 1827145522Sdarrenr return EINVAL; 1828255332Scy } 1829145522Sdarrenr 1830145522Sdarrenr KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize); 1831255332Scy if (ipn == NULL) { 1832255332Scy IPFERROR(60029); 1833145522Sdarrenr return ENOMEM; 1834255332Scy } 1835145522Sdarrenr 1836173181Sdarrenr if (getlock) { 1837255332Scy READ_ENTER(&softc->ipf_nat); 1838173181Sdarrenr } 1839173181Sdarrenr 1840145522Sdarrenr ipn->ipn_dsize = ipns.ipn_dsize; 1841145522Sdarrenr nat = ipns.ipn_next; 1842145522Sdarrenr if (nat == NULL) { 1843255332Scy nat = softn->ipf_nat_instances; 184460852Sdarrenr if (nat == NULL) { 1845255332Scy if (softn->ipf_nat_instances == NULL) { 1846255332Scy IPFERROR(60030); 1847145522Sdarrenr error = ENOENT; 1848255332Scy } 1849145522Sdarrenr goto finished; 185060852Sdarrenr } 185160852Sdarrenr } else { 185260852Sdarrenr /* 185360852Sdarrenr * Make sure the pointer we're copying from exists in the 185460852Sdarrenr * current list of entries. Security precaution to prevent 185560852Sdarrenr * copying of random kernel data. 185660852Sdarrenr */ 1857255332Scy for (n = softn->ipf_nat_instances; n; n = n->nat_next) 185860852Sdarrenr if (n == nat) 185960852Sdarrenr break; 1860145522Sdarrenr if (n == NULL) { 1861255332Scy IPFERROR(60031); 1862145522Sdarrenr error = ESRCH; 1863145522Sdarrenr goto finished; 1864145522Sdarrenr } 186560852Sdarrenr } 1866145522Sdarrenr ipn->ipn_next = nat->nat_next; 186760852Sdarrenr 1868145522Sdarrenr /* 1869145522Sdarrenr * Copy the NAT structure. 1870145522Sdarrenr */ 1871145522Sdarrenr bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat)); 187260852Sdarrenr 1873145522Sdarrenr /* 1874145522Sdarrenr * If we have a pointer to the NAT rule it belongs to, save that too. 1875145522Sdarrenr */ 1876145522Sdarrenr if (nat->nat_ptr != NULL) 1877145522Sdarrenr bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat, 1878255332Scy ipn->ipn_ipnat.in_size); 187960852Sdarrenr 1880145522Sdarrenr /* 1881145522Sdarrenr * If we also know the NAT entry has an associated filter rule, 1882145522Sdarrenr * save that too. 1883145522Sdarrenr */ 1884145522Sdarrenr if (nat->nat_fr != NULL) 1885145522Sdarrenr bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr, 1886145522Sdarrenr sizeof(ipn->ipn_fr)); 188760852Sdarrenr 1888145522Sdarrenr /* 1889145522Sdarrenr * Last but not least, if there is an application proxy session set 1890145522Sdarrenr * up for this NAT entry, then copy that out too, including any 1891145522Sdarrenr * private data saved along side it by the proxy. 1892145522Sdarrenr */ 1893145522Sdarrenr aps = nat->nat_aps; 1894145522Sdarrenr outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data); 1895145522Sdarrenr if (aps != NULL) { 1896145522Sdarrenr char *s; 189760852Sdarrenr 1898145522Sdarrenr if (outsize < sizeof(*aps)) { 1899255332Scy IPFERROR(60032); 1900145522Sdarrenr error = ENOBUFS; 1901145522Sdarrenr goto finished; 190260852Sdarrenr } 1903145522Sdarrenr 1904145522Sdarrenr s = ipn->ipn_data; 1905145522Sdarrenr bcopy((char *)aps, s, sizeof(*aps)); 1906145522Sdarrenr s += sizeof(*aps); 1907145522Sdarrenr outsize -= sizeof(*aps); 1908145522Sdarrenr if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz)) 1909145522Sdarrenr bcopy(aps->aps_data, s, aps->aps_psiz); 1910255332Scy else { 1911255332Scy IPFERROR(60033); 1912145522Sdarrenr error = ENOBUFS; 1913255332Scy } 191460852Sdarrenr } 1915145522Sdarrenr if (error == 0) { 1916173181Sdarrenr if (getlock) { 1917255332Scy READ_ENTER(&softc->ipf_nat); 1918173181Sdarrenr getlock = 0; 1919173181Sdarrenr } 1920255332Scy error = ipf_outobjsz(softc, data, ipn, IPFOBJ_NATSAVE, 1921255332Scy ipns.ipn_dsize); 1922145522Sdarrenr } 1923145522Sdarrenr 1924145522Sdarrenrfinished: 1925173181Sdarrenr if (getlock) { 1926255332Scy READ_ENTER(&softc->ipf_nat); 1927173181Sdarrenr } 1928145522Sdarrenr if (ipn != NULL) { 1929145522Sdarrenr KFREES(ipn, ipns.ipn_dsize); 1930145522Sdarrenr } 193164580Sdarrenr return error; 193260852Sdarrenr} 193360852Sdarrenr 193460852Sdarrenr 1935145522Sdarrenr/* ------------------------------------------------------------------------ */ 1936255332Scy/* Function: ipf_nat_putent */ 1937145522Sdarrenr/* Returns: int - 0 == success, != 0 is the error value. */ 1938255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1939255332Scy/* data(I) - pointer to natget structure with NAT */ 1940255332Scy/* structure information to load into the kernel */ 1941145522Sdarrenr/* getlock(I) - flag indicating whether or not a write lock */ 1942255332Scy/* on is already held. */ 1943145522Sdarrenr/* */ 1944145522Sdarrenr/* Handle SIOCSTPUT. */ 1945145522Sdarrenr/* Loads a NAT table entry from user space, including a NAT rule, proxy and */ 1946145522Sdarrenr/* firewall rule data structures, if pointers to them indicate so. */ 1947145522Sdarrenr/* ------------------------------------------------------------------------ */ 1948255332Scystatic int 1949255332Scyipf_nat_putent(softc, data, getlock) 1950255332Scy ipf_main_softc_t *softc; 1951255332Scy caddr_t data; 1952255332Scy int getlock; 195360852Sdarrenr{ 1954255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 1955145522Sdarrenr nat_save_t ipn, *ipnn; 195660852Sdarrenr ap_session_t *aps; 1957145522Sdarrenr nat_t *n, *nat; 195860852Sdarrenr frentry_t *fr; 1959145522Sdarrenr fr_info_t fin; 196060852Sdarrenr ipnat_t *in; 196160852Sdarrenr int error; 196260852Sdarrenr 1963255332Scy error = ipf_inobj(softc, data, NULL, &ipn, IPFOBJ_NATSAVE); 1964145522Sdarrenr if (error != 0) 1965145522Sdarrenr return error; 1966145522Sdarrenr 1967145522Sdarrenr /* 1968145522Sdarrenr * Initialise early because of code at junkput label. 1969145522Sdarrenr */ 1970255332Scy n = NULL; 1971145522Sdarrenr in = NULL; 1972145522Sdarrenr aps = NULL; 197364580Sdarrenr nat = NULL; 1974145522Sdarrenr ipnn = NULL; 1975170268Sdarrenr fr = NULL; 1976145522Sdarrenr 1977145522Sdarrenr /* 1978145522Sdarrenr * New entry, copy in the rest of the NAT entry if it's size is more 1979145522Sdarrenr * than just the nat_t structure. 1980145522Sdarrenr */ 1981145522Sdarrenr if (ipn.ipn_dsize > sizeof(ipn)) { 1982145522Sdarrenr if (ipn.ipn_dsize > 81920) { 1983255332Scy IPFERROR(60034); 1984145522Sdarrenr error = ENOMEM; 1985145522Sdarrenr goto junkput; 1986145522Sdarrenr } 1987145522Sdarrenr 1988145522Sdarrenr KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize); 1989255332Scy if (ipnn == NULL) { 1990255332Scy IPFERROR(60035); 199160852Sdarrenr return ENOMEM; 1992255332Scy } 1993145522Sdarrenr 1994255332Scy bzero(ipnn, ipn.ipn_dsize); 1995255332Scy error = ipf_inobjsz(softc, data, ipnn, IPFOBJ_NATSAVE, 1996255332Scy ipn.ipn_dsize); 1997145522Sdarrenr if (error != 0) { 199864580Sdarrenr goto junkput; 199964580Sdarrenr } 200060852Sdarrenr } else 2001145522Sdarrenr ipnn = &ipn; 200260852Sdarrenr 200360852Sdarrenr KMALLOC(nat, nat_t *); 200464580Sdarrenr if (nat == NULL) { 2005255332Scy IPFERROR(60037); 2006145522Sdarrenr error = ENOMEM; 200764580Sdarrenr goto junkput; 200864580Sdarrenr } 200960852Sdarrenr 2010145522Sdarrenr bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat)); 2011255332Scy 2012255332Scy switch (nat->nat_v[0]) 2013255332Scy { 2014255332Scy case 4: 2015255332Scy#ifdef USE_INET6 2016255332Scy case 6 : 2017255332Scy#endif 2018255332Scy break; 2019255332Scy default : 2020255332Scy IPFERROR(60061); 2021255332Scy error = EPROTONOSUPPORT; 2022255332Scy goto junkput; 2023255332Scy /*NOTREACHED*/ 2024255332Scy } 2025255332Scy 202660852Sdarrenr /* 2027255332Scy * Initialize all these so that ipf_nat_delete() doesn't cause a crash. 202860852Sdarrenr */ 2029145522Sdarrenr bzero((char *)nat, offsetof(struct nat, nat_tqe)); 2030145522Sdarrenr nat->nat_tqe.tqe_pnext = NULL; 2031145522Sdarrenr nat->nat_tqe.tqe_next = NULL; 2032145522Sdarrenr nat->nat_tqe.tqe_ifq = NULL; 2033145522Sdarrenr nat->nat_tqe.tqe_parent = nat; 203460852Sdarrenr 203560852Sdarrenr /* 203660852Sdarrenr * Restore the rule associated with this nat session 203760852Sdarrenr */ 2038145522Sdarrenr in = ipnn->ipn_nat.nat_ptr; 2039145522Sdarrenr if (in != NULL) { 2040255332Scy KMALLOCS(in, ipnat_t *, ipnn->ipn_ipnat.in_size); 2041145522Sdarrenr nat->nat_ptr = in; 204260852Sdarrenr if (in == NULL) { 2043255332Scy IPFERROR(60038); 204460852Sdarrenr error = ENOMEM; 204560852Sdarrenr goto junkput; 204660852Sdarrenr } 2047255332Scy bcopy((char *)&ipnn->ipn_ipnat, (char *)in, 2048255332Scy ipnn->ipn_ipnat.in_size); 204960852Sdarrenr in->in_use = 1; 205060852Sdarrenr in->in_flags |= IPN_DELETE; 2051145522Sdarrenr 2052255332Scy ATOMIC_INC32(softn->ipf_nat_stats.ns_rules); 2053145522Sdarrenr 2054255332Scy if (ipf_nat_resolverule(softc, in) != 0) { 2055255332Scy IPFERROR(60039); 2056161356Sguido error = ESRCH; 2057161356Sguido goto junkput; 2058161356Sguido } 2059145522Sdarrenr } 2060145522Sdarrenr 2061145522Sdarrenr /* 2062145522Sdarrenr * Check that the NAT entry doesn't already exist in the kernel. 2063161356Sguido * 2064161356Sguido * For NAT_OUTBOUND, we're lookup for a duplicate MAP entry. To do 2065161356Sguido * this, we check to see if the inbound combination of addresses and 2066161356Sguido * ports is already known. Similar logic is applied for NAT_INBOUND. 2067255332Scy * 2068145522Sdarrenr */ 2069145522Sdarrenr bzero((char *)&fin, sizeof(fin)); 2070255332Scy fin.fin_v = nat->nat_v[0]; 2071255332Scy fin.fin_p = nat->nat_pr[0]; 2072255332Scy fin.fin_rev = nat->nat_rev; 2073255332Scy fin.fin_ifp = nat->nat_ifps[0]; 2074255332Scy fin.fin_data[0] = ntohs(nat->nat_ndport); 2075255332Scy fin.fin_data[1] = ntohs(nat->nat_nsport); 2076255332Scy 2077255332Scy switch (nat->nat_dir) 2078255332Scy { 2079255332Scy case NAT_OUTBOUND : 2080255332Scy case NAT_DIVERTOUT : 2081153876Sguido if (getlock) { 2082255332Scy READ_ENTER(&softc->ipf_nat); 2083153876Sguido } 2084255332Scy 2085255332Scy fin.fin_v = nat->nat_v[1]; 2086255332Scy if (nat->nat_v[1] == 4) { 2087255332Scy n = ipf_nat_inlookup(&fin, nat->nat_flags, fin.fin_p, 2088255332Scy nat->nat_ndstip, nat->nat_nsrcip); 2089255332Scy#ifdef USE_INET6 2090255332Scy } else if (nat->nat_v[1] == 6) { 2091255332Scy n = ipf_nat6_inlookup(&fin, nat->nat_flags, fin.fin_p, 2092255332Scy &nat->nat_ndst6.in6, 2093255332Scy &nat->nat_nsrc6.in6); 2094255332Scy#endif 2095255332Scy } 2096255332Scy 2097153876Sguido if (getlock) { 2098255332Scy RWLOCK_EXIT(&softc->ipf_nat); 2099153876Sguido } 2100153876Sguido if (n != NULL) { 2101255332Scy IPFERROR(60040); 2102145522Sdarrenr error = EEXIST; 2103145522Sdarrenr goto junkput; 210460852Sdarrenr } 2105255332Scy break; 2106255332Scy 2107255332Scy case NAT_INBOUND : 2108255332Scy case NAT_DIVERTIN : 2109153876Sguido if (getlock) { 2110255332Scy READ_ENTER(&softc->ipf_nat); 2111153876Sguido } 2112255332Scy 2113255332Scy if (fin.fin_v == 4) { 2114255332Scy n = ipf_nat_outlookup(&fin, nat->nat_flags, fin.fin_p, 2115255332Scy nat->nat_ndstip, 2116255332Scy nat->nat_nsrcip); 2117255332Scy#ifdef USE_INET6 2118255332Scy } else if (fin.fin_v == 6) { 2119255332Scy n = ipf_nat6_outlookup(&fin, nat->nat_flags, fin.fin_p, 2120255332Scy &nat->nat_ndst6.in6, 2121255332Scy &nat->nat_nsrc6.in6); 2122255332Scy#endif 2123255332Scy } 2124255332Scy 2125153876Sguido if (getlock) { 2126255332Scy RWLOCK_EXIT(&softc->ipf_nat); 2127153876Sguido } 2128153876Sguido if (n != NULL) { 2129255332Scy IPFERROR(60041); 2130145522Sdarrenr error = EEXIST; 2131145522Sdarrenr goto junkput; 2132145522Sdarrenr } 2133255332Scy break; 2134255332Scy 2135255332Scy default : 2136255332Scy IPFERROR(60042); 2137145522Sdarrenr error = EINVAL; 2138145522Sdarrenr goto junkput; 213960852Sdarrenr } 214060852Sdarrenr 214160852Sdarrenr /* 214260852Sdarrenr * Restore ap_session_t structure. Include the private data allocated 214360852Sdarrenr * if it was there. 214460852Sdarrenr */ 2145145522Sdarrenr aps = nat->nat_aps; 2146145522Sdarrenr if (aps != NULL) { 214760852Sdarrenr KMALLOC(aps, ap_session_t *); 2148145522Sdarrenr nat->nat_aps = aps; 214960852Sdarrenr if (aps == NULL) { 2150255332Scy IPFERROR(60043); 215160852Sdarrenr error = ENOMEM; 215260852Sdarrenr goto junkput; 215360852Sdarrenr } 215460852Sdarrenr bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps)); 2155145522Sdarrenr if (in != NULL) 215660852Sdarrenr aps->aps_apr = in->in_apr; 2157145522Sdarrenr else 2158145522Sdarrenr aps->aps_apr = NULL; 2159145522Sdarrenr if (aps->aps_psiz != 0) { 2160145522Sdarrenr if (aps->aps_psiz > 81920) { 2161255332Scy IPFERROR(60044); 2162145522Sdarrenr error = ENOMEM; 2163145522Sdarrenr goto junkput; 2164145522Sdarrenr } 216560852Sdarrenr KMALLOCS(aps->aps_data, void *, aps->aps_psiz); 216660852Sdarrenr if (aps->aps_data == NULL) { 2167255332Scy IPFERROR(60045); 216860852Sdarrenr error = ENOMEM; 216960852Sdarrenr goto junkput; 217060852Sdarrenr } 217160852Sdarrenr bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data, 217260852Sdarrenr aps->aps_psiz); 217360852Sdarrenr } else { 217460852Sdarrenr aps->aps_psiz = 0; 217560852Sdarrenr aps->aps_data = NULL; 217660852Sdarrenr } 217760852Sdarrenr } 217860852Sdarrenr 217960852Sdarrenr /* 218060852Sdarrenr * If there was a filtering rule associated with this entry then 218160852Sdarrenr * build up a new one. 218260852Sdarrenr */ 2183145522Sdarrenr fr = nat->nat_fr; 218460852Sdarrenr if (fr != NULL) { 2185145522Sdarrenr if ((nat->nat_flags & SI_NEWFR) != 0) { 218660852Sdarrenr KMALLOC(fr, frentry_t *); 218760852Sdarrenr nat->nat_fr = fr; 218860852Sdarrenr if (fr == NULL) { 2189255332Scy IPFERROR(60046); 219060852Sdarrenr error = ENOMEM; 219160852Sdarrenr goto junkput; 219260852Sdarrenr } 2193145522Sdarrenr ipnn->ipn_nat.nat_fr = fr; 2194145522Sdarrenr fr->fr_ref = 1; 2195255332Scy (void) ipf_outobj(softc, data, ipnn, IPFOBJ_NATSAVE); 2196145522Sdarrenr bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr)); 2197161356Sguido 2198161356Sguido fr->fr_ref = 1; 2199161356Sguido fr->fr_dsize = 0; 2200161356Sguido fr->fr_data = NULL; 2201161356Sguido fr->fr_type = FR_T_NONE; 2202161356Sguido 2203145522Sdarrenr MUTEX_NUKE(&fr->fr_lock); 2204145522Sdarrenr MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock"); 220560852Sdarrenr } else { 2206153876Sguido if (getlock) { 2207255332Scy READ_ENTER(&softc->ipf_nat); 2208153876Sguido } 2209255332Scy for (n = softn->ipf_nat_instances; n; n = n->nat_next) 221060852Sdarrenr if (n->nat_fr == fr) 221160852Sdarrenr break; 2212145522Sdarrenr 2213145522Sdarrenr if (n != NULL) { 2214145522Sdarrenr MUTEX_ENTER(&fr->fr_lock); 2215145522Sdarrenr fr->fr_ref++; 2216145522Sdarrenr MUTEX_EXIT(&fr->fr_lock); 2217145522Sdarrenr } 2218153876Sguido if (getlock) { 2219255332Scy RWLOCK_EXIT(&softc->ipf_nat); 2220153876Sguido } 2221145522Sdarrenr 2222255332Scy if (n == NULL) { 2223255332Scy IPFERROR(60047); 222460852Sdarrenr error = ESRCH; 222560852Sdarrenr goto junkput; 222660852Sdarrenr } 222760852Sdarrenr } 222860852Sdarrenr } 222960852Sdarrenr 2230145522Sdarrenr if (ipnn != &ipn) { 2231145522Sdarrenr KFREES(ipnn, ipn.ipn_dsize); 2232145522Sdarrenr ipnn = NULL; 2233145522Sdarrenr } 2234145522Sdarrenr 2235145522Sdarrenr if (getlock) { 2236255332Scy WRITE_ENTER(&softc->ipf_nat); 2237145522Sdarrenr } 2238255332Scy 2239255332Scy if (fin.fin_v == 4) 2240255332Scy error = ipf_nat_finalise(&fin, nat); 2241255332Scy#ifdef USE_INET6 2242255332Scy else 2243255332Scy error = ipf_nat6_finalise(&fin, nat); 2244255332Scy#endif 2245255332Scy 2246145522Sdarrenr if (getlock) { 2247255332Scy RWLOCK_EXIT(&softc->ipf_nat); 2248145522Sdarrenr } 2249145522Sdarrenr 2250145522Sdarrenr if (error == 0) 2251145522Sdarrenr return 0; 2252145522Sdarrenr 2253255332Scy IPFERROR(60048); 2254145522Sdarrenr error = ENOMEM; 2255145522Sdarrenr 225660852Sdarrenrjunkput: 2257255332Scy if (fr != NULL) { 2258255332Scy (void) ipf_derefrule(softc, &fr); 2259255332Scy } 2260145522Sdarrenr 2261145522Sdarrenr if ((ipnn != NULL) && (ipnn != &ipn)) { 2262145522Sdarrenr KFREES(ipnn, ipn.ipn_dsize); 2263145522Sdarrenr } 2264145522Sdarrenr if (nat != NULL) { 2265145522Sdarrenr if (aps != NULL) { 2266145522Sdarrenr if (aps->aps_data != NULL) { 2267145522Sdarrenr KFREES(aps->aps_data, aps->aps_psiz); 2268145522Sdarrenr } 2269145522Sdarrenr KFREE(aps); 2270145522Sdarrenr } 2271145522Sdarrenr if (in != NULL) { 2272145522Sdarrenr if (in->in_apr) 2273255332Scy ipf_proxy_deref(in->in_apr); 2274255332Scy KFREES(in, in->in_size); 2275145522Sdarrenr } 2276145522Sdarrenr KFREE(nat); 2277145522Sdarrenr } 227860852Sdarrenr return error; 227960852Sdarrenr} 228060852Sdarrenr 228160852Sdarrenr 2282145522Sdarrenr/* ------------------------------------------------------------------------ */ 2283255332Scy/* Function: ipf_nat_delete */ 2284145522Sdarrenr/* Returns: Nil */ 2285255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 2286255332Scy/* nat(I) - pointer to NAT structure to delete */ 2287145522Sdarrenr/* logtype(I) - type of LOG record to create before deleting */ 2288145522Sdarrenr/* Write Lock: ipf_nat */ 2289145522Sdarrenr/* */ 2290145522Sdarrenr/* Delete a nat entry from the various lists and table. If NAT logging is */ 2291145522Sdarrenr/* enabled then generate a NAT log record for this event. */ 2292145522Sdarrenr/* ------------------------------------------------------------------------ */ 2293255332Scyvoid 2294255332Scyipf_nat_delete(softc, nat, logtype) 2295255332Scy ipf_main_softc_t *softc; 2296255332Scy struct nat *nat; 2297255332Scy int logtype; 229853642Sguido{ 2299255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 2300255332Scy int madeorphan = 0, bkt, removed = 0; 2301255332Scy nat_stat_side_t *nss; 230253642Sguido struct ipnat *ipn; 230353642Sguido 2304255332Scy if (logtype != 0 && softn->ipf_nat_logging != 0) 2305255332Scy ipf_nat_log(softc, softn, nat, logtype); 230653642Sguido 2307145522Sdarrenr /* 2308145522Sdarrenr * Take it as a general indication that all the pointers are set if 2309145522Sdarrenr * nat_pnext is set. 2310145522Sdarrenr */ 2311145522Sdarrenr if (nat->nat_pnext != NULL) { 2312172776Sdarrenr removed = 1; 2313172776Sdarrenr 2314255332Scy bkt = nat->nat_hv[0] % softn->ipf_nat_table_sz; 2315255332Scy nss = &softn->ipf_nat_stats.ns_side[0]; 2316255332Scy nss->ns_bucketlen[bkt]--; 2317255332Scy if (nss->ns_bucketlen[bkt] == 0) { 2318255332Scy nss->ns_inuse--; 2319255332Scy } 2320145522Sdarrenr 2321255332Scy bkt = nat->nat_hv[1] % softn->ipf_nat_table_sz; 2322255332Scy nss = &softn->ipf_nat_stats.ns_side[1]; 2323255332Scy nss->ns_bucketlen[bkt]--; 2324255332Scy if (nss->ns_bucketlen[bkt] == 0) { 2325255332Scy nss->ns_inuse--; 2326255332Scy } 2327255332Scy 2328145522Sdarrenr *nat->nat_pnext = nat->nat_next; 2329145522Sdarrenr if (nat->nat_next != NULL) { 2330145522Sdarrenr nat->nat_next->nat_pnext = nat->nat_pnext; 2331145522Sdarrenr nat->nat_next = NULL; 2332145522Sdarrenr } 2333145522Sdarrenr nat->nat_pnext = NULL; 2334145522Sdarrenr 2335145522Sdarrenr *nat->nat_phnext[0] = nat->nat_hnext[0]; 2336145522Sdarrenr if (nat->nat_hnext[0] != NULL) { 2337145522Sdarrenr nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 2338145522Sdarrenr nat->nat_hnext[0] = NULL; 2339145522Sdarrenr } 2340145522Sdarrenr nat->nat_phnext[0] = NULL; 2341145522Sdarrenr 2342145522Sdarrenr *nat->nat_phnext[1] = nat->nat_hnext[1]; 2343145522Sdarrenr if (nat->nat_hnext[1] != NULL) { 2344145522Sdarrenr nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 2345145522Sdarrenr nat->nat_hnext[1] = NULL; 2346145522Sdarrenr } 2347145522Sdarrenr nat->nat_phnext[1] = NULL; 2348145522Sdarrenr 2349255332Scy if ((nat->nat_flags & SI_WILDP) != 0) { 2350255332Scy ATOMIC_DEC32(softn->ipf_nat_stats.ns_wilds); 2351255332Scy } 2352255332Scy madeorphan = 1; 235353642Sguido } 235460852Sdarrenr 2355145522Sdarrenr if (nat->nat_me != NULL) { 2356145522Sdarrenr *nat->nat_me = NULL; 2357145522Sdarrenr nat->nat_me = NULL; 2358255332Scy nat->nat_ref--; 2359255332Scy ASSERT(nat->nat_ref >= 0); 2360145522Sdarrenr } 236160852Sdarrenr 2362255332Scy if (nat->nat_tqe.tqe_ifq != NULL) { 2363255332Scy /* 2364255332Scy * No call to ipf_freetimeoutqueue() is made here, they are 2365255332Scy * garbage collected in ipf_nat_expire(). 2366255332Scy */ 2367255332Scy (void) ipf_deletequeueentry(&nat->nat_tqe); 2368255332Scy } 2369145522Sdarrenr 2370255332Scy if (nat->nat_sync) { 2371255332Scy ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); 2372255332Scy nat->nat_sync = NULL; 2373255332Scy } 2374255332Scy 2375170268Sdarrenr if (logtype == NL_EXPIRE) 2376255332Scy softn->ipf_nat_stats.ns_expire++; 2377170268Sdarrenr 2378172776Sdarrenr MUTEX_ENTER(&nat->nat_lock); 2379172776Sdarrenr /* 2380172776Sdarrenr * NL_DESTROY should only be passed in when we've got nat_ref >= 2. 2381172776Sdarrenr * This happens when a nat'd packet is blocked and we want to throw 2382172776Sdarrenr * away the NAT session. 2383172776Sdarrenr */ 2384172776Sdarrenr if (logtype == NL_DESTROY) { 2385172776Sdarrenr if (nat->nat_ref > 2) { 2386172776Sdarrenr nat->nat_ref -= 2; 2387172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 2388172776Sdarrenr if (removed) 2389255332Scy softn->ipf_nat_stats.ns_orphans++; 2390172776Sdarrenr return; 2391172776Sdarrenr } 2392172776Sdarrenr } else if (nat->nat_ref > 1) { 2393172776Sdarrenr nat->nat_ref--; 2394172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 2395255332Scy if (madeorphan == 1) 2396255332Scy softn->ipf_nat_stats.ns_orphans++; 2397145522Sdarrenr return; 2398145522Sdarrenr } 2399255332Scy ASSERT(nat->nat_ref >= 0); 2400172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 2401170268Sdarrenr 2402255332Scy nat->nat_ref = 0; 2403255332Scy 2404255332Scy if (madeorphan == 0) 2405255332Scy softn->ipf_nat_stats.ns_orphans--; 2406255332Scy 2407161356Sguido /* 2408255332Scy * At this point, nat_ref can be either 0 or -1 2409161356Sguido */ 2410255332Scy softn->ipf_nat_stats.ns_proto[nat->nat_pr[0]]--; 2411145522Sdarrenr 2412255332Scy if (nat->nat_fr != NULL) { 2413255332Scy (void) ipf_derefrule(softc, &nat->nat_fr); 2414255332Scy } 2415145522Sdarrenr 2416255332Scy if (nat->nat_hm != NULL) { 2417255332Scy ipf_nat_hostmapdel(softc, &nat->nat_hm); 2418255332Scy } 2419145522Sdarrenr 242053642Sguido /* 242153642Sguido * If there is an active reference from the nat entry to its parent 242253642Sguido * rule, decrement the rule's reference count and free it too if no 242353642Sguido * longer being used. 242453642Sguido */ 2425145522Sdarrenr ipn = nat->nat_ptr; 2426255332Scy nat->nat_ptr = NULL; 2427255332Scy 242853642Sguido if (ipn != NULL) { 2429255332Scy ipn->in_space++; 2430255332Scy ipf_nat_rule_deref(softc, &ipn); 243153642Sguido } 243253642Sguido 2433255332Scy if (nat->nat_aps != NULL) { 2434255332Scy ipf_proxy_free(softc, nat->nat_aps); 2435255332Scy nat->nat_aps = NULL; 2436255332Scy } 2437255332Scy 2438145522Sdarrenr MUTEX_DESTROY(&nat->nat_lock); 2439145522Sdarrenr 2440255332Scy softn->ipf_nat_stats.ns_active--; 2441145522Sdarrenr 244253642Sguido /* 244353642Sguido * If there's a fragment table entry too for this nat entry, then 2444145522Sdarrenr * dereference that as well. This is after nat_lock is released 2445145522Sdarrenr * because of Tru64. 244653642Sguido */ 2447255332Scy ipf_frag_natforget(softc, (void *)nat); 2448145522Sdarrenr 2449145522Sdarrenr KFREE(nat); 245053642Sguido} 245153642Sguido 245253642Sguido 2453145522Sdarrenr/* ------------------------------------------------------------------------ */ 2454255332Scy/* Function: ipf_nat_flushtable */ 2455145522Sdarrenr/* Returns: int - number of NAT rules deleted */ 2456255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 2457255332Scy/* softn(I) - pointer to NAT context structure */ 2458255332Scy/* Write Lock: ipf_nat */ 2459145522Sdarrenr/* */ 2460145522Sdarrenr/* Deletes all currently active NAT sessions. In deleting each NAT entry a */ 2461255332Scy/* log record should be emitted in ipf_nat_delete() if NAT logging is */ 2462255332Scy/* enabled. */ 2463145522Sdarrenr/* ------------------------------------------------------------------------ */ 246453642Sguido/* 246553642Sguido * nat_flushtable - clear the NAT table of all mapping entries. 246653642Sguido */ 2467255332Scystatic int 2468255332Scyipf_nat_flushtable(softc, softn) 2469255332Scy ipf_main_softc_t *softc; 2470255332Scy ipf_nat_softc_t *softn; 247153642Sguido{ 2472145522Sdarrenr nat_t *nat; 2473145522Sdarrenr int j = 0; 247467614Sdarrenr 247553642Sguido /* 247653642Sguido * ALL NAT mappings deleted, so lets just make the deletions 247753642Sguido * quicker. 247853642Sguido */ 2479255332Scy if (softn->ipf_nat_table[0] != NULL) 2480255332Scy bzero((char *)softn->ipf_nat_table[0], 2481255332Scy sizeof(softn->ipf_nat_table[0]) * 2482255332Scy softn->ipf_nat_table_sz); 2483255332Scy if (softn->ipf_nat_table[1] != NULL) 2484255332Scy bzero((char *)softn->ipf_nat_table[1], 2485255332Scy sizeof(softn->ipf_nat_table[1]) * 2486255332Scy softn->ipf_nat_table_sz); 248753642Sguido 2488255332Scy while ((nat = softn->ipf_nat_instances) != NULL) { 2489255332Scy ipf_nat_delete(softc, nat, NL_FLUSH); 249053642Sguido j++; 249153642Sguido } 2492145522Sdarrenr 249353642Sguido return j; 249453642Sguido} 249553642Sguido 249653642Sguido 2497145522Sdarrenr/* ------------------------------------------------------------------------ */ 2498255332Scy/* Function: ipf_nat_clearlist */ 2499145522Sdarrenr/* Returns: int - number of NAT/RDR rules deleted */ 2500255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 2501255332Scy/* softn(I) - pointer to NAT context structure */ 2502145522Sdarrenr/* */ 2503145522Sdarrenr/* Delete all rules in the current list of rules. There is nothing elegant */ 2504145522Sdarrenr/* about this cleanup: simply free all entries on the list of rules and */ 2505145522Sdarrenr/* clear out the tables used for hashed NAT rule lookups. */ 2506145522Sdarrenr/* ------------------------------------------------------------------------ */ 2507255332Scystatic int 2508255332Scyipf_nat_clearlist(softc, softn) 2509255332Scy ipf_main_softc_t *softc; 2510255332Scy ipf_nat_softc_t *softn; 251153642Sguido{ 2512255332Scy ipnat_t *n; 251353642Sguido int i = 0; 251453642Sguido 2515255332Scy if (softn->ipf_nat_map_rules != NULL) { 2516255332Scy bzero((char *)softn->ipf_nat_map_rules, 2517255332Scy sizeof(*softn->ipf_nat_map_rules) * 2518255332Scy softn->ipf_nat_maprules_sz); 2519255332Scy } 2520255332Scy if (softn->ipf_nat_rdr_rules != NULL) { 2521255332Scy bzero((char *)softn->ipf_nat_rdr_rules, 2522255332Scy sizeof(*softn->ipf_nat_rdr_rules) * 2523255332Scy softn->ipf_nat_rdrrules_sz); 2524255332Scy } 252553642Sguido 2526255332Scy while ((n = softn->ipf_nat_list) != NULL) { 2527255332Scy ipf_nat_delrule(softc, softn, n, 0); 252853642Sguido i++; 252953642Sguido } 2530255332Scy#if SOLARIS && !defined(INSTANCES) 2531145522Sdarrenr pfil_delayed_copy = 1; 2532145522Sdarrenr#endif 253353642Sguido return i; 253453642Sguido} 253553642Sguido 253653642Sguido 2537145522Sdarrenr/* ------------------------------------------------------------------------ */ 2538255332Scy/* Function: ipf_nat_delrule */ 2539255332Scy/* Returns: Nil */ 2540255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 2541255332Scy/* softn(I) - pointer to NAT context structure */ 2542255332Scy/* np(I) - pointer to NAT rule to delete */ 2543255332Scy/* purge(I) - 1 == allow purge, 0 == prevent purge */ 2544255332Scy/* Locks: WRITE(ipf_nat) */ 2545255332Scy/* */ 2546255332Scy/* Preventing "purge" from occuring is allowed because when all of the NAT */ 2547255332Scy/* rules are being removed, allowing the "purge" to walk through the list */ 2548255332Scy/* of NAT sessions, possibly multiple times, would be a large performance */ 2549255332Scy/* hit, on the order of O(N^2). */ 2550255332Scy/* ------------------------------------------------------------------------ */ 2551255332Scystatic void 2552255332Scyipf_nat_delrule(softc, softn, np, purge) 2553255332Scy ipf_main_softc_t *softc; 2554255332Scy ipf_nat_softc_t *softn; 2555255332Scy ipnat_t *np; 2556255332Scy int purge; 2557255332Scy{ 2558255332Scy 2559255332Scy if (np->in_pnext != NULL) { 2560255332Scy *np->in_pnext = np->in_next; 2561255332Scy if (np->in_next != NULL) 2562255332Scy np->in_next->in_pnext = np->in_pnext; 2563255332Scy if (softn->ipf_nat_list_tail == &np->in_next) 2564255332Scy softn->ipf_nat_list_tail = np->in_pnext; 2565255332Scy } 2566255332Scy 2567255332Scy if ((purge == 1) && ((np->in_flags & IPN_PURGE) != 0)) { 2568255332Scy nat_t *next; 2569255332Scy nat_t *nat; 2570255332Scy 2571255332Scy for (next = softn->ipf_nat_instances; (nat = next) != NULL;) { 2572255332Scy next = nat->nat_next; 2573255332Scy if (nat->nat_ptr == np) 2574255332Scy ipf_nat_delete(softc, nat, NL_PURGE); 2575255332Scy } 2576255332Scy } 2577255332Scy 2578255332Scy if ((np->in_flags & IPN_DELETE) == 0) { 2579255332Scy if (np->in_redir & NAT_REDIRECT) { 2580255332Scy switch (np->in_v[0]) 2581255332Scy { 2582255332Scy case 4 : 2583255332Scy ipf_nat_delrdr(softn, np); 2584255332Scy break; 2585255332Scy#ifdef USE_INET6 2586255332Scy case 6 : 2587255332Scy ipf_nat6_delrdr(softn, np); 2588255332Scy break; 2589255332Scy#endif 2590255332Scy } 2591255332Scy } 2592255332Scy if (np->in_redir & (NAT_MAPBLK|NAT_MAP)) { 2593255332Scy switch (np->in_v[0]) 2594255332Scy { 2595255332Scy case 4 : 2596255332Scy ipf_nat_delmap(softn, np); 2597255332Scy break; 2598255332Scy#ifdef USE_INET6 2599255332Scy case 6 : 2600255332Scy ipf_nat6_delmap(softn, np); 2601255332Scy break; 2602255332Scy#endif 2603255332Scy } 2604255332Scy } 2605255332Scy } 2606255332Scy 2607255332Scy np->in_flags |= IPN_DELETE; 2608255332Scy ipf_nat_rule_deref(softc, &np); 2609255332Scy} 2610255332Scy 2611255332Scy 2612255332Scy/* ------------------------------------------------------------------------ */ 2613255332Scy/* Function: ipf_nat_newmap */ 2614145522Sdarrenr/* Returns: int - -1 == error, 0 == success */ 2615145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2616145522Sdarrenr/* nat(I) - pointer to NAT entry */ 2617145522Sdarrenr/* ni(I) - pointer to structure with misc. information needed */ 2618145522Sdarrenr/* to create new NAT entry. */ 2619145522Sdarrenr/* */ 2620145522Sdarrenr/* Given an empty NAT structure, populate it with new information about a */ 2621145522Sdarrenr/* new NAT session, as defined by the matching NAT rule. */ 2622145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 2623145522Sdarrenr/* to the new IP address for the translation. */ 2624145522Sdarrenr/* ------------------------------------------------------------------------ */ 2625255332Scystatic int 2626255332Scyipf_nat_newmap(fin, nat, ni) 2627255332Scy fr_info_t *fin; 2628255332Scy nat_t *nat; 2629255332Scy natinfo_t *ni; 2630145522Sdarrenr{ 2631255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 2632255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 2633145522Sdarrenr u_short st_port, dport, sport, port, sp, dp; 2634145522Sdarrenr struct in_addr in, inb; 2635145522Sdarrenr hostmap_t *hm; 2636145522Sdarrenr u_32_t flags; 2637145522Sdarrenr u_32_t st_ip; 2638145522Sdarrenr ipnat_t *np; 2639145522Sdarrenr nat_t *natl; 2640145522Sdarrenr int l; 2641145522Sdarrenr 2642145522Sdarrenr /* 2643145522Sdarrenr * If it's an outbound packet which doesn't match any existing 2644145522Sdarrenr * record, then create a new port 2645145522Sdarrenr */ 2646145522Sdarrenr l = 0; 2647145522Sdarrenr hm = NULL; 2648145522Sdarrenr np = ni->nai_np; 2649255332Scy st_ip = np->in_snip; 2650255332Scy st_port = np->in_spnext; 2651255332Scy flags = nat->nat_flags; 2652145522Sdarrenr 2653255332Scy if (flags & IPN_ICMPQUERY) { 2654255332Scy sport = fin->fin_data[1]; 2655255332Scy dport = 0; 2656255332Scy } else { 2657255332Scy sport = htons(fin->fin_data[0]); 2658255332Scy dport = htons(fin->fin_data[1]); 2659255332Scy } 2660255332Scy 2661145522Sdarrenr /* 2662145522Sdarrenr * Do a loop until we either run out of entries to try or we find 2663145522Sdarrenr * a NAT mapping that isn't currently being used. This is done 2664145522Sdarrenr * because the change to the source is not (usually) being fixed. 2665145522Sdarrenr */ 2666145522Sdarrenr do { 2667145522Sdarrenr port = 0; 2668255332Scy in.s_addr = htonl(np->in_snip); 2669145522Sdarrenr if (l == 0) { 2670145522Sdarrenr /* 2671145522Sdarrenr * Check to see if there is an existing NAT 2672145522Sdarrenr * setup for this IP address pair. 2673145522Sdarrenr */ 2674255332Scy hm = ipf_nat_hostmap(softn, np, fin->fin_src, 2675255332Scy fin->fin_dst, in, 0); 2676145522Sdarrenr if (hm != NULL) 2677255332Scy in.s_addr = hm->hm_nsrcip.s_addr; 2678145522Sdarrenr } else if ((l == 1) && (hm != NULL)) { 2679255332Scy ipf_nat_hostmapdel(softc, &hm); 2680145522Sdarrenr } 2681145522Sdarrenr in.s_addr = ntohl(in.s_addr); 2682145522Sdarrenr 2683145522Sdarrenr nat->nat_hm = hm; 2684145522Sdarrenr 2685255332Scy if ((np->in_nsrcmsk == 0xffffffff) && (np->in_spnext == 0)) { 2686255332Scy if (l > 0) { 2687255332Scy NBUMPSIDEX(1, ns_exhausted, ns_exhausted_1); 2688145522Sdarrenr return -1; 2689255332Scy } 2690145522Sdarrenr } 2691145522Sdarrenr 2692145522Sdarrenr if (np->in_redir == NAT_BIMAP && 2693255332Scy np->in_osrcmsk == np->in_nsrcmsk) { 2694145522Sdarrenr /* 2695145522Sdarrenr * map the address block in a 1:1 fashion 2696145522Sdarrenr */ 2697255332Scy in.s_addr = np->in_nsrcaddr; 2698255332Scy in.s_addr |= fin->fin_saddr & ~np->in_osrcmsk; 2699145522Sdarrenr in.s_addr = ntohl(in.s_addr); 2700145522Sdarrenr 2701145522Sdarrenr } else if (np->in_redir & NAT_MAPBLK) { 2702145522Sdarrenr if ((l >= np->in_ppip) || ((l > 0) && 2703255332Scy !(flags & IPN_TCPUDP))) { 2704255332Scy NBUMPSIDEX(1, ns_exhausted, ns_exhausted_2); 2705145522Sdarrenr return -1; 2706255332Scy } 2707145522Sdarrenr /* 2708145522Sdarrenr * map-block - Calculate destination address. 2709145522Sdarrenr */ 2710145522Sdarrenr in.s_addr = ntohl(fin->fin_saddr); 2711255332Scy in.s_addr &= ntohl(~np->in_osrcmsk); 2712145522Sdarrenr inb.s_addr = in.s_addr; 2713145522Sdarrenr in.s_addr /= np->in_ippip; 2714255332Scy in.s_addr &= ntohl(~np->in_nsrcmsk); 2715255332Scy in.s_addr += ntohl(np->in_nsrcaddr); 2716145522Sdarrenr /* 2717145522Sdarrenr * Calculate destination port. 2718145522Sdarrenr */ 2719145522Sdarrenr if ((flags & IPN_TCPUDP) && 2720145522Sdarrenr (np->in_ppip != 0)) { 2721145522Sdarrenr port = ntohs(sport) + l; 2722145522Sdarrenr port %= np->in_ppip; 2723145522Sdarrenr port += np->in_ppip * 2724145522Sdarrenr (inb.s_addr % np->in_ippip); 2725145522Sdarrenr port += MAPBLK_MINPORT; 2726145522Sdarrenr port = htons(port); 2727145522Sdarrenr } 2728145522Sdarrenr 2729255332Scy } else if ((np->in_nsrcaddr == 0) && 2730255332Scy (np->in_nsrcmsk == 0xffffffff)) { 2731255332Scy i6addr_t in6; 2732255332Scy 2733145522Sdarrenr /* 2734145522Sdarrenr * 0/32 - use the interface's IP address. 2735145522Sdarrenr */ 2736145522Sdarrenr if ((l > 0) || 2737255332Scy ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp, 2738255332Scy &in6, NULL) == -1) { 2739255332Scy NBUMPSIDEX(1, ns_new_ifpaddr, ns_new_ifpaddr_1); 2740145522Sdarrenr return -1; 2741255332Scy } 2742255332Scy in.s_addr = ntohl(in6.in4.s_addr); 2743145522Sdarrenr 2744255332Scy } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) { 2745145522Sdarrenr /* 2746145522Sdarrenr * 0/0 - use the original source address/port. 2747145522Sdarrenr */ 2748255332Scy if (l > 0) { 2749255332Scy NBUMPSIDEX(1, ns_exhausted, ns_exhausted_3); 2750145522Sdarrenr return -1; 2751255332Scy } 2752145522Sdarrenr in.s_addr = ntohl(fin->fin_saddr); 2753145522Sdarrenr 2754255332Scy } else if ((np->in_nsrcmsk != 0xffffffff) && 2755255332Scy (np->in_spnext == 0) && ((l > 0) || (hm == NULL))) 2756255332Scy np->in_snip++; 2757145522Sdarrenr 2758145522Sdarrenr natl = NULL; 2759145522Sdarrenr 2760145522Sdarrenr if ((flags & IPN_TCPUDP) && 2761145522Sdarrenr ((np->in_redir & NAT_MAPBLK) == 0) && 2762145522Sdarrenr (np->in_flags & IPN_AUTOPORTMAP)) { 2763145522Sdarrenr /* 2764145522Sdarrenr * "ports auto" (without map-block) 2765145522Sdarrenr */ 2766145522Sdarrenr if ((l > 0) && (l % np->in_ppip == 0)) { 2767255332Scy if ((l > np->in_ppip) && 2768255332Scy np->in_nsrcmsk != 0xffffffff) 2769255332Scy np->in_snip++; 2770145522Sdarrenr } 2771145522Sdarrenr if (np->in_ppip != 0) { 2772145522Sdarrenr port = ntohs(sport); 2773145522Sdarrenr port += (l % np->in_ppip); 2774145522Sdarrenr port %= np->in_ppip; 2775145522Sdarrenr port += np->in_ppip * 2776145522Sdarrenr (ntohl(fin->fin_saddr) % 2777145522Sdarrenr np->in_ippip); 2778145522Sdarrenr port += MAPBLK_MINPORT; 2779145522Sdarrenr port = htons(port); 2780145522Sdarrenr } 2781145522Sdarrenr 2782145522Sdarrenr } else if (((np->in_redir & NAT_MAPBLK) == 0) && 2783255332Scy (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) { 2784145522Sdarrenr /* 2785145522Sdarrenr * Standard port translation. Select next port. 2786145522Sdarrenr */ 2787180778Sdarrenr if (np->in_flags & IPN_SEQUENTIAL) { 2788255332Scy port = np->in_spnext; 2789180778Sdarrenr } else { 2790255332Scy port = ipf_random() % (np->in_spmax - 2791255332Scy np->in_spmin + 1); 2792255332Scy port += np->in_spmin; 2793180778Sdarrenr } 2794180832Sdarrenr port = htons(port); 2795255332Scy np->in_spnext++; 2796145522Sdarrenr 2797255332Scy if (np->in_spnext > np->in_spmax) { 2798255332Scy np->in_spnext = np->in_spmin; 2799255332Scy if (np->in_nsrcmsk != 0xffffffff) 2800255332Scy np->in_snip++; 2801145522Sdarrenr } 2802145522Sdarrenr } 2803145522Sdarrenr 2804255332Scy if (np->in_flags & IPN_SIPRANGE) { 2805255332Scy if (np->in_snip > ntohl(np->in_nsrcmsk)) 2806255332Scy np->in_snip = ntohl(np->in_nsrcaddr); 2807145522Sdarrenr } else { 2808255332Scy if ((np->in_nsrcmsk != 0xffffffff) && 2809255332Scy ((np->in_snip + 1) & ntohl(np->in_nsrcmsk)) > 2810255332Scy ntohl(np->in_nsrcaddr)) 2811255332Scy np->in_snip = ntohl(np->in_nsrcaddr) + 1; 2812145522Sdarrenr } 2813145522Sdarrenr 2814145522Sdarrenr if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) 2815145522Sdarrenr port = sport; 2816145522Sdarrenr 2817145522Sdarrenr /* 2818145522Sdarrenr * Here we do a lookup of the connection as seen from 2819145522Sdarrenr * the outside. If an IP# pair already exists, try 2820145522Sdarrenr * again. So if you have A->B becomes C->B, you can 2821145522Sdarrenr * also have D->E become C->E but not D->B causing 2822145522Sdarrenr * another C->B. Also take protocol and ports into 2823145522Sdarrenr * account when determining whether a pre-existing 2824145522Sdarrenr * NAT setup will cause an external conflict where 2825145522Sdarrenr * this is appropriate. 2826145522Sdarrenr */ 2827145522Sdarrenr inb.s_addr = htonl(in.s_addr); 2828145522Sdarrenr sp = fin->fin_data[0]; 2829145522Sdarrenr dp = fin->fin_data[1]; 2830145522Sdarrenr fin->fin_data[0] = fin->fin_data[1]; 2831255332Scy fin->fin_data[1] = ntohs(port); 2832255332Scy natl = ipf_nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 2833255332Scy (u_int)fin->fin_p, fin->fin_dst, inb); 2834145522Sdarrenr fin->fin_data[0] = sp; 2835145522Sdarrenr fin->fin_data[1] = dp; 2836145522Sdarrenr 2837145522Sdarrenr /* 2838145522Sdarrenr * Has the search wrapped around and come back to the 2839145522Sdarrenr * start ? 2840145522Sdarrenr */ 2841145522Sdarrenr if ((natl != NULL) && 2842255332Scy (np->in_spnext != 0) && (st_port == np->in_spnext) && 2843255332Scy (np->in_snip != 0) && (st_ip == np->in_snip)) { 2844255332Scy NBUMPSIDED(1, ns_wrap); 2845145522Sdarrenr return -1; 2846255332Scy } 2847145522Sdarrenr l++; 2848145522Sdarrenr } while (natl != NULL); 2849145522Sdarrenr 2850145522Sdarrenr /* Setup the NAT table */ 2851255332Scy nat->nat_osrcip = fin->fin_src; 2852255332Scy nat->nat_nsrcaddr = htonl(in.s_addr); 2853255332Scy nat->nat_odstip = fin->fin_dst; 2854255332Scy nat->nat_ndstip = fin->fin_dst; 2855145522Sdarrenr if (nat->nat_hm == NULL) 2856255332Scy nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src, 2857255332Scy fin->fin_dst, nat->nat_nsrcip, 2858255332Scy 0); 2859145522Sdarrenr 2860145522Sdarrenr if (flags & IPN_TCPUDP) { 2861255332Scy nat->nat_osport = sport; 2862255332Scy nat->nat_nsport = port; /* sport */ 2863255332Scy nat->nat_odport = dport; 2864255332Scy nat->nat_ndport = dport; 2865145522Sdarrenr ((tcphdr_t *)fin->fin_dp)->th_sport = port; 2866145522Sdarrenr } else if (flags & IPN_ICMPQUERY) { 2867255332Scy nat->nat_oicmpid = fin->fin_data[1]; 2868145522Sdarrenr ((icmphdr_t *)fin->fin_dp)->icmp_id = port; 2869255332Scy nat->nat_nicmpid = port; 2870145522Sdarrenr } 2871145522Sdarrenr return 0; 2872145522Sdarrenr} 2873145522Sdarrenr 2874145522Sdarrenr 2875145522Sdarrenr/* ------------------------------------------------------------------------ */ 2876255332Scy/* Function: ipf_nat_newrdr */ 2877145522Sdarrenr/* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ 2878145522Sdarrenr/* allow rule to be moved if IPN_ROUNDR is set. */ 2879145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2880145522Sdarrenr/* nat(I) - pointer to NAT entry */ 2881145522Sdarrenr/* ni(I) - pointer to structure with misc. information needed */ 2882145522Sdarrenr/* to create new NAT entry. */ 2883145522Sdarrenr/* */ 2884145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 2885145522Sdarrenr/* to the new IP address for the translation. */ 2886145522Sdarrenr/* ------------------------------------------------------------------------ */ 2887255332Scystatic int 2888255332Scyipf_nat_newrdr(fin, nat, ni) 2889255332Scy fr_info_t *fin; 2890255332Scy nat_t *nat; 2891255332Scy natinfo_t *ni; 2892145522Sdarrenr{ 2893255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 2894255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 2895145522Sdarrenr u_short nport, dport, sport; 2896170268Sdarrenr struct in_addr in, inb; 2897170268Sdarrenr u_short sp, dp; 2898145522Sdarrenr hostmap_t *hm; 2899145522Sdarrenr u_32_t flags; 2900145522Sdarrenr ipnat_t *np; 2901170268Sdarrenr nat_t *natl; 2902145522Sdarrenr int move; 2903145522Sdarrenr 2904145522Sdarrenr move = 1; 2905145522Sdarrenr hm = NULL; 2906145522Sdarrenr in.s_addr = 0; 2907145522Sdarrenr np = ni->nai_np; 2908255332Scy flags = nat->nat_flags; 2909145522Sdarrenr 2910255332Scy if (flags & IPN_ICMPQUERY) { 2911255332Scy dport = fin->fin_data[1]; 2912255332Scy sport = 0; 2913255332Scy } else { 2914255332Scy sport = htons(fin->fin_data[0]); 2915255332Scy dport = htons(fin->fin_data[1]); 2916255332Scy } 2917255332Scy 2918255332Scy /* TRACE sport, dport */ 2919255332Scy 2920255332Scy 2921145522Sdarrenr /* 2922145522Sdarrenr * If the matching rule has IPN_STICKY set, then we want to have the 2923145522Sdarrenr * same rule kick in as before. Why would this happen? If you have 2924145522Sdarrenr * a collection of rdr rules with "round-robin sticky", the current 2925145522Sdarrenr * packet might match a different one to the previous connection but 2926145522Sdarrenr * we want the same destination to be used. 2927145522Sdarrenr */ 2928153876Sguido if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) && 2929153876Sguido ((np->in_flags & IPN_STICKY) != 0)) { 2930255332Scy hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, fin->fin_dst, 2931255332Scy in, (u_32_t)dport); 2932145522Sdarrenr if (hm != NULL) { 2933255332Scy in.s_addr = ntohl(hm->hm_ndstip.s_addr); 2934145522Sdarrenr np = hm->hm_ipnat; 2935145522Sdarrenr ni->nai_np = np; 2936145522Sdarrenr move = 0; 2937255332Scy ipf_nat_hostmapdel(softc, &hm); 2938145522Sdarrenr } 2939145522Sdarrenr } 2940145522Sdarrenr 2941145522Sdarrenr /* 2942145522Sdarrenr * Otherwise, it's an inbound packet. Most likely, we don't 2943145522Sdarrenr * want to rewrite source ports and source addresses. Instead, 2944145522Sdarrenr * we want to rewrite to a fixed internal address and fixed 2945145522Sdarrenr * internal port. 2946145522Sdarrenr */ 2947145522Sdarrenr if (np->in_flags & IPN_SPLIT) { 2948255332Scy in.s_addr = np->in_dnip; 2949145522Sdarrenr 2950145522Sdarrenr if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { 2951255332Scy hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, 2952255332Scy fin->fin_dst, in, (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); 2979145522Sdarrenr return -1; 2980255332Scy } 2981255332Scy in.s_addr = ntohl(in6.in4.s_addr); 2982145522Sdarrenr 2983255332Scy } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk== 0)) { 2984145522Sdarrenr /* 2985145522Sdarrenr * 0/0 - use the original destination address/port. 2986145522Sdarrenr */ 2987145522Sdarrenr in.s_addr = ntohl(fin->fin_daddr); 2988145522Sdarrenr 2989145522Sdarrenr } else if (np->in_redir == NAT_BIMAP && 2990255332Scy np->in_ndstmsk == np->in_odstmsk) { 2991145522Sdarrenr /* 2992145522Sdarrenr * map the address block in a 1:1 fashion 2993145522Sdarrenr */ 2994255332Scy in.s_addr = np->in_ndstaddr; 2995255332Scy in.s_addr |= fin->fin_daddr & ~np->in_ndstmsk; 2996145522Sdarrenr in.s_addr = ntohl(in.s_addr); 2997145522Sdarrenr } else { 2998255332Scy in.s_addr = ntohl(np->in_ndstaddr); 2999145522Sdarrenr } 3000145522Sdarrenr 3001255332Scy if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) 3002145522Sdarrenr nport = dport; 3003145522Sdarrenr else { 3004145522Sdarrenr /* 3005145522Sdarrenr * Whilst not optimized for the case where 3006145522Sdarrenr * pmin == pmax, the gain is not significant. 3007145522Sdarrenr */ 3008145522Sdarrenr if (((np->in_flags & IPN_FIXEDDPORT) == 0) && 3009255332Scy (np->in_odport != np->in_dtop)) { 3010255332Scy nport = ntohs(dport) - np->in_odport + np->in_dpmax; 3011145522Sdarrenr nport = htons(nport); 3012255332Scy } else { 3013255332Scy nport = htons(np->in_dpnext); 3014255332Scy np->in_dpnext++; 3015255332Scy if (np->in_dpnext > np->in_dpmax) 3016255332Scy np->in_dpnext = np->in_dpmin; 3017255332Scy } 3018145522Sdarrenr } 3019145522Sdarrenr 3020145522Sdarrenr /* 3021145522Sdarrenr * When the redirect-to address is set to 0.0.0.0, just 3022145522Sdarrenr * assume a blank `forwarding' of the packet. We don't 3023145522Sdarrenr * setup any translation for this either. 3024145522Sdarrenr */ 3025145522Sdarrenr if (in.s_addr == 0) { 3026255332Scy if (nport == dport) { 3027255332Scy NBUMPSIDED(0, ns_xlate_null); 3028145522Sdarrenr return -1; 3029255332Scy } 3030145522Sdarrenr in.s_addr = ntohl(fin->fin_daddr); 3031145522Sdarrenr } 3032145522Sdarrenr 3033170268Sdarrenr /* 3034170268Sdarrenr * Check to see if this redirect mapping already exists and if 3035170268Sdarrenr * it does, return "failure" (allowing it to be created will just 3036170268Sdarrenr * cause one or both of these "connections" to stop working.) 3037170268Sdarrenr */ 3038170268Sdarrenr inb.s_addr = htonl(in.s_addr); 3039170268Sdarrenr sp = fin->fin_data[0]; 3040170268Sdarrenr dp = fin->fin_data[1]; 3041170268Sdarrenr fin->fin_data[1] = fin->fin_data[0]; 3042170268Sdarrenr fin->fin_data[0] = ntohs(nport); 3043255332Scy natl = ipf_nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 3044170268Sdarrenr (u_int)fin->fin_p, inb, fin->fin_src); 3045170268Sdarrenr fin->fin_data[0] = sp; 3046170268Sdarrenr fin->fin_data[1] = dp; 3047255332Scy if (natl != NULL) { 3048255332Scy DT2(ns_new_xlate_exists, fr_info_t *, fin, nat_t *, natl); 3049255332Scy NBUMPSIDE(0, ns_xlate_exists); 3050170268Sdarrenr return -1; 3051255332Scy } 3052170268Sdarrenr 3053255332Scy nat->nat_ndstaddr = htonl(in.s_addr); 3054255332Scy nat->nat_odstip = fin->fin_dst; 3055255332Scy nat->nat_nsrcip = fin->fin_src; 3056255332Scy nat->nat_osrcip = fin->fin_src; 3057153876Sguido if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0)) 3058255332Scy nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src, 3059255332Scy fin->fin_dst, in, (u_32_t)dport); 3060145522Sdarrenr 3061145522Sdarrenr if (flags & IPN_TCPUDP) { 3062255332Scy nat->nat_odport = dport; 3063255332Scy nat->nat_ndport = nport; 3064255332Scy nat->nat_osport = sport; 3065255332Scy nat->nat_nsport = sport; 3066145522Sdarrenr ((tcphdr_t *)fin->fin_dp)->th_dport = nport; 3067145522Sdarrenr } else if (flags & IPN_ICMPQUERY) { 3068255332Scy nat->nat_oicmpid = fin->fin_data[1]; 3069145522Sdarrenr ((icmphdr_t *)fin->fin_dp)->icmp_id = nport; 3070255332Scy nat->nat_nicmpid = nport; 3071145522Sdarrenr } 3072145522Sdarrenr 3073145522Sdarrenr return move; 3074145522Sdarrenr} 3075145522Sdarrenr 3076145522Sdarrenr/* ------------------------------------------------------------------------ */ 3077255332Scy/* Function: ipf_nat_add */ 3078145522Sdarrenr/* Returns: nat_t* - NULL == failure to create new NAT structure, */ 3079145522Sdarrenr/* else pointer to new NAT structure */ 3080145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3081145522Sdarrenr/* np(I) - pointer to NAT rule */ 3082145522Sdarrenr/* natsave(I) - pointer to where to store NAT struct pointer */ 3083145522Sdarrenr/* flags(I) - flags describing the current packet */ 3084145522Sdarrenr/* direction(I) - direction of packet (in/out) */ 3085145522Sdarrenr/* Write Lock: ipf_nat */ 3086145522Sdarrenr/* */ 3087145522Sdarrenr/* Attempts to create a new NAT entry. Does not actually change the packet */ 3088145522Sdarrenr/* in any way. */ 3089145522Sdarrenr/* */ 3090145522Sdarrenr/* This fucntion is in three main parts: (1) deal with creating a new NAT */ 3091145522Sdarrenr/* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */ 3092145522Sdarrenr/* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */ 3093145522Sdarrenr/* and (3) building that structure and putting it into the NAT table(s). */ 3094161356Sguido/* */ 3095161356Sguido/* NOTE: natsave should NOT be used top point back to an ipstate_t struct */ 3096161356Sguido/* as it can result in memory being corrupted. */ 3097145522Sdarrenr/* ------------------------------------------------------------------------ */ 3098255332Scynat_t * 3099255332Scyipf_nat_add(fin, np, natsave, flags, direction) 3100255332Scy fr_info_t *fin; 3101255332Scy ipnat_t *np; 3102255332Scy nat_t **natsave; 3103255332Scy u_int flags; 3104255332Scy int direction; 310553642Sguido{ 3106255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3107255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 310860852Sdarrenr hostmap_t *hm = NULL; 310960852Sdarrenr nat_t *nat, *natl; 3110255332Scy natstat_t *nsp; 3111145522Sdarrenr u_int nflags; 3112145522Sdarrenr natinfo_t ni; 3113145522Sdarrenr int move; 311453642Sguido 3115255332Scy nsp = &softn->ipf_nat_stats; 3116255332Scy 3117255332Scy if ((nsp->ns_active * 100 / softn->ipf_nat_table_max) > 3118255332Scy softn->ipf_nat_table_wm_high) { 3119255332Scy softn->ipf_nat_doflush = 1; 3120255332Scy } 3121255332Scy 3122255332Scy if (nsp->ns_active >= softn->ipf_nat_table_max) { 3123255332Scy NBUMPSIDED(fin->fin_out, ns_table_max); 3124130886Sdarrenr return NULL; 3125130886Sdarrenr } 3126130886Sdarrenr 3127145522Sdarrenr move = 1; 3128145522Sdarrenr nflags = np->in_flags & flags; 3129145522Sdarrenr nflags &= NAT_FROMRULE; 313053642Sguido 3131145522Sdarrenr ni.nai_np = np; 3132170268Sdarrenr ni.nai_dport = 0; 3133170268Sdarrenr ni.nai_sport = 0; 3134145522Sdarrenr 313553642Sguido /* Give me a new nat */ 313653642Sguido KMALLOC(nat, nat_t *); 313760852Sdarrenr if (nat == NULL) { 3138255332Scy NBUMPSIDED(fin->fin_out, ns_memfail); 3139130886Sdarrenr /* 3140130886Sdarrenr * Try to automatically tune the max # of entries in the 3141130886Sdarrenr * table allowed to be less than what will cause kmem_alloc() 3142130886Sdarrenr * to fail and try to eliminate panics due to out of memory 3143130886Sdarrenr * conditions arising. 3144130886Sdarrenr */ 3145255332Scy if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) && 3146255332Scy (nsp->ns_active > 100)) { 3147255332Scy softn->ipf_nat_table_max = nsp->ns_active - 100; 3148255332Scy printf("table_max reduced to %d\n", 3149255332Scy softn->ipf_nat_table_max); 3150130886Sdarrenr } 315153642Sguido return NULL; 315260852Sdarrenr } 315353642Sguido 3154255332Scy if (flags & IPN_ICMPQUERY) { 3155145522Sdarrenr /* 3156145522Sdarrenr * In the ICMP query NAT code, we translate the ICMP id fields 3157145522Sdarrenr * to make them unique. This is indepedent of the ICMP type 3158145522Sdarrenr * (e.g. in the unlikely event that a host sends an echo and 3159145522Sdarrenr * an tstamp request with the same id, both packets will have 3160145522Sdarrenr * their ip address/id field changed in the same way). 3161145522Sdarrenr */ 3162145522Sdarrenr /* The icmp_id field is used by the sender to identify the 3163145522Sdarrenr * process making the icmp request. (the receiver justs 3164145522Sdarrenr * copies it back in its response). So, it closely matches 3165145522Sdarrenr * the concept of source port. We overlay sport, so we can 3166145522Sdarrenr * maximally reuse the existing code. 3167145522Sdarrenr */ 3168255332Scy ni.nai_sport = fin->fin_data[1]; 3169255332Scy ni.nai_dport = 0; 3170145522Sdarrenr } 3171145522Sdarrenr 317253642Sguido bzero((char *)nat, sizeof(*nat)); 317353642Sguido nat->nat_flags = flags; 3174170268Sdarrenr nat->nat_redir = np->in_redir; 3175255332Scy nat->nat_dir = direction; 3176255332Scy nat->nat_pr[0] = fin->fin_p; 3177255332Scy nat->nat_pr[1] = fin->fin_p; 3178145522Sdarrenr 317953642Sguido /* 3180255332Scy * Search the current table for a match and create a new mapping 3181255332Scy * if there is none found. 318253642Sguido */ 3183255332Scy if (np->in_redir & NAT_DIVERTUDP) { 3184255332Scy move = ipf_nat_newdivert(fin, nat, &ni); 3185255332Scy 3186255332Scy } else if (np->in_redir & NAT_REWRITE) { 3187255332Scy move = ipf_nat_newrewrite(fin, nat, &ni); 3188255332Scy 3189255332Scy } else if (direction == NAT_OUTBOUND) { 319053642Sguido /* 3191145522Sdarrenr * We can now arrange to call this for the same connection 3192145522Sdarrenr * because ipf_nat_new doesn't protect the code path into 3193145522Sdarrenr * this function. 319453642Sguido */ 3195255332Scy natl = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p, 3196145522Sdarrenr fin->fin_src, fin->fin_dst); 3197145522Sdarrenr if (natl != NULL) { 3198161356Sguido KFREE(nat); 3199145522Sdarrenr nat = natl; 3200145522Sdarrenr goto done; 3201145522Sdarrenr } 320253642Sguido 3203255332Scy move = ipf_nat_newmap(fin, nat, &ni); 320453642Sguido } else { 320553642Sguido /* 3206255332Scy * NAT_INBOUND is used for redirects rules 320753642Sguido */ 3208255332Scy natl = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p, 3209255332Scy fin->fin_src, fin->fin_dst); 3210145522Sdarrenr if (natl != NULL) { 3211161356Sguido KFREE(nat); 3212145522Sdarrenr nat = natl; 3213145522Sdarrenr goto done; 321460852Sdarrenr } 321553642Sguido 3216255332Scy move = ipf_nat_newrdr(fin, nat, &ni); 3217145522Sdarrenr } 3218255332Scy if (move == -1) 3219255332Scy goto badnat; 322053642Sguido 3221255332Scy np = ni.nai_np; 3222255332Scy 3223255332Scy nat->nat_mssclamp = np->in_mssclamp; 3224255332Scy nat->nat_me = natsave; 3225255332Scy nat->nat_fr = fin->fin_fr; 3226255332Scy nat->nat_rev = fin->fin_rev; 3227255332Scy nat->nat_ptr = np; 3228255332Scy nat->nat_dlocal = np->in_dlocal; 3229255332Scy 3230255332Scy if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) { 3231255332Scy if (ipf_proxy_new(fin, nat) == -1) { 3232255332Scy NBUMPSIDED(fin->fin_out, ns_appr_fail); 3233255332Scy goto badnat; 323453642Sguido } 323553642Sguido } 323653642Sguido 3237255332Scy nat->nat_ifps[0] = np->in_ifps[0]; 3238255332Scy if (np->in_ifps[0] != NULL) { 3239255332Scy COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]); 3240145522Sdarrenr } 3241145522Sdarrenr 3242255332Scy nat->nat_ifps[1] = np->in_ifps[1]; 3243255332Scy if (np->in_ifps[1] != NULL) { 3244255332Scy COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]); 3245255332Scy } 324653642Sguido 3247255332Scy if (ipf_nat_finalise(fin, nat) == -1) { 3248255332Scy goto badnat; 3249255332Scy } 325053642Sguido 3251255332Scy np->in_use++; 325253642Sguido 3253255332Scy if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { 3254255332Scy if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) { 3255255332Scy ipf_nat_delrdr(softn, np); 3256255332Scy ipf_nat_addrdr(softn, np); 3257255332Scy } else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) { 3258255332Scy ipf_nat_delmap(softn, np); 3259255332Scy ipf_nat_addmap(softn, np); 3260145522Sdarrenr } 3261145522Sdarrenr } 326253642Sguido 3263145522Sdarrenr if (flags & SI_WILDP) 3264255332Scy nsp->ns_wilds++; 3265255332Scy nsp->ns_proto[nat->nat_pr[0]]++; 3266255332Scy 3267145522Sdarrenr goto done; 3268145522Sdarrenrbadnat: 3269255332Scy DT2(ns_badnatnew, fr_info_t *, fin, nat_t *, nat); 3270255332Scy NBUMPSIDE(fin->fin_out, ns_badnatnew); 3271145522Sdarrenr if ((hm = nat->nat_hm) != NULL) 3272255332Scy ipf_nat_hostmapdel(softc, &hm); 3273145522Sdarrenr KFREE(nat); 3274145522Sdarrenr nat = NULL; 3275145522Sdarrenrdone: 3276255332Scy if (nat != NULL && np != NULL) 3277255332Scy np->in_hits++; 3278255332Scy if (natsave != NULL) 3279255332Scy *natsave = nat; 3280145522Sdarrenr return nat; 3281145522Sdarrenr} 328260852Sdarrenr 328360852Sdarrenr 3284145522Sdarrenr/* ------------------------------------------------------------------------ */ 3285255332Scy/* Function: ipf_nat_finalise */ 3286145522Sdarrenr/* Returns: int - 0 == sucess, -1 == failure */ 3287145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3288145522Sdarrenr/* nat(I) - pointer to NAT entry */ 3289145522Sdarrenr/* Write Lock: ipf_nat */ 3290145522Sdarrenr/* */ 3291145522Sdarrenr/* This is the tail end of constructing a new NAT entry and is the same */ 3292145522Sdarrenr/* for both IPv4 and IPv6. */ 3293145522Sdarrenr/* ------------------------------------------------------------------------ */ 3294145522Sdarrenr/*ARGSUSED*/ 3295255332Scystatic int 3296255332Scyipf_nat_finalise(fin, nat) 3297255332Scy fr_info_t *fin; 3298255332Scy nat_t *nat; 3299145522Sdarrenr{ 3300255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3301255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 3302255332Scy u_32_t sum1, sum2, sumd; 3303145522Sdarrenr frentry_t *fr; 3304255332Scy u_32_t flags; 3305255332Scy#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC) 3306255332Scy qpktinfo_t *qpi = fin->fin_qpi; 3307255332Scy#endif 3308145522Sdarrenr 3309255332Scy flags = nat->nat_flags; 3310145522Sdarrenr 3311255332Scy switch (nat->nat_pr[0]) 3312255332Scy { 3313255332Scy case IPPROTO_ICMP : 3314255332Scy sum1 = LONG_SUM(ntohs(nat->nat_oicmpid)); 3315255332Scy sum2 = LONG_SUM(ntohs(nat->nat_nicmpid)); 3316255332Scy CALC_SUMD(sum1, sum2, sumd); 3317255332Scy nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 3318255332Scy 3319255332Scy break; 3320255332Scy 3321255332Scy default : 3322255332Scy sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr) + \ 3323255332Scy ntohs(nat->nat_osport)); 3324255332Scy sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr) + \ 3325255332Scy ntohs(nat->nat_nsport)); 3326255332Scy CALC_SUMD(sum1, sum2, sumd); 3327255332Scy nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 3328255332Scy 3329255332Scy sum1 = LONG_SUM(ntohl(nat->nat_odstaddr) + \ 3330255332Scy ntohs(nat->nat_odport)); 3331255332Scy sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr) + \ 3332255332Scy ntohs(nat->nat_ndport)); 3333255332Scy CALC_SUMD(sum1, sum2, sumd); 3334255332Scy nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); 3335255332Scy break; 3336161356Sguido } 3337255332Scy 3338255332Scy /* 3339255332Scy * Compute the partial checksum, just in case. 3340255332Scy * This is only ever placed into outbound packets so care needs 3341255332Scy * to be taken over which pair of addresses are used. 3342255332Scy */ 3343255332Scy if (nat->nat_dir == NAT_OUTBOUND) { 3344255332Scy sum1 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); 3345255332Scy sum1 += LONG_SUM(ntohl(nat->nat_ndstaddr)); 3346255332Scy } else { 3347255332Scy sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr)); 3348255332Scy sum1 += LONG_SUM(ntohl(nat->nat_odstaddr)); 3349161356Sguido } 3350255332Scy sum1 += nat->nat_pr[1]; 3351255332Scy nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16); 3352145522Sdarrenr 3353255332Scy sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr)); 3354255332Scy sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); 3355255332Scy CALC_SUMD(sum1, sum2, sumd); 3356255332Scy nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); 335792685Sdarrenr 3358255332Scy sum1 = LONG_SUM(ntohl(nat->nat_odstaddr)); 3359255332Scy sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr)); 3360255332Scy CALC_SUMD(sum1, sum2, sumd); 3361255332Scy nat->nat_ipsumd += (sumd & 0xffff) + (sumd >> 16); 336292685Sdarrenr 3363255332Scy nat->nat_v[0] = 4; 3364255332Scy nat->nat_v[1] = 4; 3365255332Scy 3366255332Scy if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { 3367255332Scy nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); 3368255332Scy } 3369255332Scy 3370255332Scy if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { 3371255332Scy nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); 3372255332Scy } 3373255332Scy 3374255332Scy if ((nat->nat_flags & SI_CLONE) == 0) 3375255332Scy nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat); 3376255332Scy 3377255332Scy if (ipf_nat_insert(softc, softn, nat) == 0) { 3378255332Scy if (softn->ipf_nat_logging) 3379255332Scy ipf_nat_log(softc, softn, nat, NL_NEW); 3380255332Scy fr = nat->nat_fr; 3381145522Sdarrenr if (fr != NULL) { 3382145522Sdarrenr MUTEX_ENTER(&fr->fr_lock); 3383145522Sdarrenr fr->fr_ref++; 3384145522Sdarrenr MUTEX_EXIT(&fr->fr_lock); 3385145522Sdarrenr } 3386145522Sdarrenr return 0; 3387145522Sdarrenr } 338892685Sdarrenr 3389255332Scy NBUMPSIDED(fin->fin_out, ns_unfinalised); 3390145522Sdarrenr /* 3391145522Sdarrenr * nat_insert failed, so cleanup time... 3392145522Sdarrenr */ 3393255332Scy if (nat->nat_sync != NULL) 3394255332Scy ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); 3395145522Sdarrenr return -1; 339653642Sguido} 339753642Sguido 339853642Sguido 3399145522Sdarrenr/* ------------------------------------------------------------------------ */ 3400255332Scy/* Function: ipf_nat_insert */ 3401255332Scy/* Returns: int - 0 == sucess, -1 == failure */ 3402255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 3403255332Scy/* softn(I) - pointer to NAT context structure */ 3404255332Scy/* nat(I) - pointer to NAT structure */ 3405255332Scy/* Write Lock: ipf_nat */ 3406145522Sdarrenr/* */ 3407145522Sdarrenr/* Insert a NAT entry into the hash tables for searching and add it to the */ 3408145522Sdarrenr/* list of active NAT entries. Adjust global counters when complete. */ 3409145522Sdarrenr/* ------------------------------------------------------------------------ */ 3410255332Scyint 3411255332Scyipf_nat_insert(softc, softn, nat) 3412255332Scy ipf_main_softc_t *softc; 3413255332Scy ipf_nat_softc_t *softn; 3414255332Scy nat_t *nat; 341560852Sdarrenr{ 3416255332Scy u_int hv0, hv1; 3417255332Scy u_int sp, dp; 3418255332Scy ipnat_t *in; 341960852Sdarrenr 3420145522Sdarrenr /* 3421145522Sdarrenr * Try and return an error as early as possible, so calculate the hash 3422145522Sdarrenr * entry numbers first and then proceed. 3423145522Sdarrenr */ 3424145522Sdarrenr if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { 3425255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 3426255332Scy sp = nat->nat_osport; 3427255332Scy dp = nat->nat_odport; 3428255332Scy } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { 3429255332Scy sp = 0; 3430255332Scy dp = nat->nat_oicmpid; 3431255332Scy } else { 3432255332Scy sp = 0; 3433255332Scy dp = 0; 3434255332Scy } 3435255332Scy hv0 = NAT_HASH_FN(nat->nat_osrcaddr, sp, 0xffffffff); 3436255332Scy hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0 + dp, 0xffffffff); 3437255332Scy /* 3438255332Scy * TRACE nat_osrcaddr, nat_osport, nat_odstaddr, 3439255332Scy * nat_odport, hv0 3440255332Scy */ 3441255332Scy 3442255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 3443255332Scy sp = nat->nat_nsport; 3444255332Scy dp = nat->nat_ndport; 3445255332Scy } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { 3446255332Scy sp = 0; 3447255332Scy dp = nat->nat_nicmpid; 3448255332Scy } else { 3449255332Scy sp = 0; 3450255332Scy dp = 0; 3451255332Scy } 3452255332Scy hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, sp, 0xffffffff); 3453255332Scy hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1 + dp, 0xffffffff); 3454255332Scy /* 3455255332Scy * TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, 3456255332Scy * nat_ndport, hv1 3457255332Scy */ 345880482Sdarrenr } else { 3459255332Scy hv0 = NAT_HASH_FN(nat->nat_osrcaddr, 0, 0xffffffff); 3460255332Scy hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0, 0xffffffff); 3461255332Scy /* TRACE nat_osrcaddr, nat_odstaddr, hv0 */ 346280482Sdarrenr 3463255332Scy hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, 0, 0xffffffff); 3464255332Scy hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1, 0xffffffff); 3465255332Scy /* TRACE nat_nsrcaddr, nat_ndstaddr, hv1 */ 3466145522Sdarrenr } 3467145522Sdarrenr 3468255332Scy nat->nat_hv[0] = hv0; 3469255332Scy nat->nat_hv[1] = hv1; 3470145522Sdarrenr 3471145522Sdarrenr MUTEX_INIT(&nat->nat_lock, "nat entry lock"); 3472145522Sdarrenr 3473255332Scy in = nat->nat_ptr; 3474255332Scy nat->nat_ref = nat->nat_me ? 2 : 1; 3475145522Sdarrenr 3476145522Sdarrenr nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; 3477255332Scy nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0], 4); 3478145522Sdarrenr 3479161356Sguido if (nat->nat_ifnames[1][0] != '\0') { 3480145522Sdarrenr nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 3481255332Scy nat->nat_ifps[1] = ipf_resolvenic(softc, 3482255332Scy nat->nat_ifnames[1], 4); 3483255332Scy } else if (in->in_ifnames[1] != -1) { 3484255332Scy char *name; 3485255332Scy 3486255332Scy name = in->in_names + in->in_ifnames[1]; 3487255332Scy if (name[1] != '\0' && name[0] != '-' && name[0] != '*') { 3488255332Scy (void) strncpy(nat->nat_ifnames[1], 3489255332Scy nat->nat_ifnames[0], LIFNAMSIZ); 3490255332Scy nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 3491255332Scy nat->nat_ifps[1] = nat->nat_ifps[0]; 3492255332Scy } 3493145522Sdarrenr } 3494255332Scy if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { 3495255332Scy nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); 3496255332Scy } 3497255332Scy if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { 3498255332Scy nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); 3499255332Scy } 3500145522Sdarrenr 3501255332Scy return ipf_nat_hashtab_add(softc, softn, nat); 3502255332Scy} 3503145522Sdarrenr 3504255332Scy 3505255332Scy/* ------------------------------------------------------------------------ */ 3506255332Scy/* Function: ipf_nat_hashtab_add */ 3507255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 3508255332Scy/* softn(I) - pointer to NAT context structure */ 3509255332Scy/* nat(I) - pointer to NAT structure */ 3510255332Scy/* */ 3511255332Scy/* Handle the insertion of a NAT entry into the table/list. */ 3512255332Scy/* ------------------------------------------------------------------------ */ 3513255332Scyint 3514255332Scyipf_nat_hashtab_add(softc, softn, nat) 3515255332Scy ipf_main_softc_t *softc; 3516255332Scy ipf_nat_softc_t *softn; 3517255332Scy nat_t *nat; 3518255332Scy{ 3519255332Scy nat_t **natp; 3520255332Scy u_int hv0; 3521255332Scy u_int hv1; 3522255332Scy 3523255332Scy hv0 = nat->nat_hv[0] % softn->ipf_nat_table_sz; 3524255332Scy hv1 = nat->nat_hv[1] % softn->ipf_nat_table_sz; 3525255332Scy 3526255332Scy if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) { 3527255332Scy u_int swap; 3528255332Scy 3529255332Scy swap = hv0; 3530255332Scy hv0 = hv1; 3531255332Scy hv1 = swap; 3532255332Scy } 3533255332Scy 3534255332Scy if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0] >= 3535255332Scy softn->ipf_nat_maxbucket) { 3536255332Scy DT1(ns_bucket_max_0, int, 3537255332Scy softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]); 3538255332Scy NBUMPSIDE(0, ns_bucket_max); 3539255332Scy return -1; 3540255332Scy } 3541255332Scy 3542255332Scy if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1] >= 3543255332Scy softn->ipf_nat_maxbucket) { 3544255332Scy DT1(ns_bucket_max_1, int, 3545255332Scy softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]); 3546255332Scy NBUMPSIDE(1, ns_bucket_max); 3547255332Scy return -1; 3548255332Scy } 3549255332Scy 3550255332Scy /* 3551255332Scy * The ordering of operations in the list and hash table insertion 3552255332Scy * is very important. The last operation for each task should be 3553255332Scy * to update the top of the list, after all the "nexts" have been 3554255332Scy * done so that walking the list while it is being done does not 3555255332Scy * find strange pointers. 3556255332Scy * 3557255332Scy * Global list of NAT instances 3558255332Scy */ 3559255332Scy nat->nat_next = softn->ipf_nat_instances; 3560255332Scy nat->nat_pnext = &softn->ipf_nat_instances; 3561255332Scy if (softn->ipf_nat_instances) 3562255332Scy softn->ipf_nat_instances->nat_pnext = &nat->nat_next; 3563255332Scy softn->ipf_nat_instances = nat; 3564255332Scy 3565255332Scy /* 3566255332Scy * Inbound hash table. 3567255332Scy */ 3568255332Scy natp = &softn->ipf_nat_table[0][hv0]; 356967614Sdarrenr nat->nat_phnext[0] = natp; 357060852Sdarrenr nat->nat_hnext[0] = *natp; 3571255332Scy if (*natp) { 3572255332Scy (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 3573255332Scy } else { 3574255332Scy NBUMPSIDE(0, ns_inuse); 3575255332Scy } 357660852Sdarrenr *natp = nat; 3577255332Scy NBUMPSIDE(0, ns_bucketlen[hv0]); 357867614Sdarrenr 3579255332Scy /* 3580255332Scy * Outbound hash table. 3581255332Scy */ 3582255332Scy natp = &softn->ipf_nat_table[1][hv1]; 3583255332Scy nat->nat_phnext[1] = natp; 3584255332Scy nat->nat_hnext[1] = *natp; 358567614Sdarrenr if (*natp) 358667614Sdarrenr (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 3587255332Scy else { 3588255332Scy NBUMPSIDE(1, ns_inuse); 3589255332Scy } 359060852Sdarrenr *natp = nat; 3591255332Scy NBUMPSIDE(1, ns_bucketlen[hv1]); 359260852Sdarrenr 3593255332Scy ipf_nat_setqueue(softc, softn, nat); 3594145522Sdarrenr 3595255332Scy if (nat->nat_dir & NAT_OUTBOUND) { 3596255332Scy NBUMPSIDE(1, ns_added); 3597255332Scy } else { 3598255332Scy NBUMPSIDE(0, ns_added); 3599255332Scy } 3600255332Scy softn->ipf_nat_stats.ns_active++; 3601145522Sdarrenr return 0; 360260852Sdarrenr} 360360852Sdarrenr 360460852Sdarrenr 3605145522Sdarrenr/* ------------------------------------------------------------------------ */ 3606255332Scy/* Function: ipf_nat_icmperrorlookup */ 3607145522Sdarrenr/* Returns: nat_t* - point to matching NAT structure */ 3608145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3609145522Sdarrenr/* dir(I) - direction of packet (in/out) */ 3610145522Sdarrenr/* */ 3611145522Sdarrenr/* Check if the ICMP error message is related to an existing TCP, UDP or */ 3612145522Sdarrenr/* ICMP query nat entry. It is assumed that the packet is already of the */ 3613145522Sdarrenr/* the required length. */ 3614145522Sdarrenr/* ------------------------------------------------------------------------ */ 3615255332Scynat_t * 3616255332Scyipf_nat_icmperrorlookup(fin, dir) 3617255332Scy fr_info_t *fin; 3618255332Scy int dir; 361953642Sguido{ 3620255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3621255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 3622145522Sdarrenr int flags = 0, type, minlen; 3623145522Sdarrenr icmphdr_t *icmp, *orgicmp; 3624255332Scy nat_stat_side_t *nside; 362553642Sguido tcphdr_t *tcp = NULL; 3626145522Sdarrenr u_short data[2]; 3627145522Sdarrenr nat_t *nat; 362853642Sguido ip_t *oip; 3629145522Sdarrenr u_int p; 363053642Sguido 3631145522Sdarrenr icmp = fin->fin_dp; 3632145522Sdarrenr type = icmp->icmp_type; 3633255332Scy nside = &softn->ipf_nat_stats.ns_side[fin->fin_out]; 363453642Sguido /* 363553642Sguido * Does it at least have the return (basic) IP header ? 363653642Sguido * Only a basic IP header (no options) should be with an ICMP error 3637145522Sdarrenr * header. Also, if it's not an error type, then return. 363853642Sguido */ 3639255332Scy if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) { 3640255332Scy ATOMIC_INCL(nside->ns_icmp_basic); 364153642Sguido return NULL; 3642255332Scy } 3643145522Sdarrenr 364453642Sguido /* 3645145522Sdarrenr * Check packet size 364653642Sguido */ 364753642Sguido oip = (ip_t *)((char *)fin->fin_dp + 8); 3648145522Sdarrenr minlen = IP_HL(oip) << 2; 3649145522Sdarrenr if ((minlen < sizeof(ip_t)) || 3650255332Scy (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) { 3651255332Scy ATOMIC_INCL(nside->ns_icmp_size); 365253642Sguido return NULL; 3653255332Scy } 3654255332Scy 365564580Sdarrenr /* 365664580Sdarrenr * Is the buffer big enough for all of it ? It's the size of the IP 365764580Sdarrenr * header claimed in the encapsulated part which is of concern. It 365864580Sdarrenr * may be too big to be in this buffer but not so big that it's 365964580Sdarrenr * outside the ICMP packet, leading to TCP deref's causing problems. 366064580Sdarrenr * This is possible because we don't know how big oip_hl is when we 3661255332Scy * do the pullup early in ipf_check() and thus can't gaurantee it is 366264580Sdarrenr * all here now. 366364580Sdarrenr */ 3664255332Scy#ifdef ipf_nat_KERNEL 366564580Sdarrenr { 366664580Sdarrenr mb_t *m; 366764580Sdarrenr 3668145522Sdarrenr m = fin->fin_m; 3669145522Sdarrenr# if defined(MENTAT) 3670255332Scy if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > 3671255332Scy (char *)m->b_wptr) { 3672255332Scy ATOMIC_INCL(nside->ns_icmp_mbuf); 367364580Sdarrenr return NULL; 3674255332Scy } 367564580Sdarrenr# else 367664580Sdarrenr if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > 3677255332Scy (char *)fin->fin_ip + M_LEN(m)) { 3678255332Scy ATOMIC_INCL(nside->ns_icmp_mbuf); 367964580Sdarrenr return NULL; 3680255332Scy } 368164580Sdarrenr# endif 368264580Sdarrenr } 368364580Sdarrenr#endif 368464580Sdarrenr 3685255332Scy if (fin->fin_daddr != oip->ip_src.s_addr) { 3686255332Scy ATOMIC_INCL(nside->ns_icmp_address); 3687145522Sdarrenr return NULL; 3688255332Scy } 3689145522Sdarrenr 3690145522Sdarrenr p = oip->ip_p; 3691145522Sdarrenr if (p == IPPROTO_TCP) 369253642Sguido flags = IPN_TCP; 3693145522Sdarrenr else if (p == IPPROTO_UDP) 369453642Sguido flags = IPN_UDP; 3695145522Sdarrenr else if (p == IPPROTO_ICMP) { 3696145522Sdarrenr orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 3697145522Sdarrenr 3698145522Sdarrenr /* see if this is related to an ICMP query */ 3699255332Scy if (ipf_nat_icmpquerytype(orgicmp->icmp_type)) { 3700145522Sdarrenr data[0] = fin->fin_data[0]; 3701145522Sdarrenr data[1] = fin->fin_data[1]; 3702145522Sdarrenr fin->fin_data[0] = 0; 3703145522Sdarrenr fin->fin_data[1] = orgicmp->icmp_id; 3704145522Sdarrenr 3705145522Sdarrenr flags = IPN_ICMPERR|IPN_ICMPQUERY; 3706145522Sdarrenr /* 3707145522Sdarrenr * NOTE : dir refers to the direction of the original 3708145522Sdarrenr * ip packet. By definition the icmp error 3709145522Sdarrenr * message flows in the opposite direction. 3710145522Sdarrenr */ 3711145522Sdarrenr if (dir == NAT_INBOUND) 3712255332Scy nat = ipf_nat_inlookup(fin, flags, p, 3713255332Scy oip->ip_dst, 3714255332Scy oip->ip_src); 3715145522Sdarrenr else 3716255332Scy nat = ipf_nat_outlookup(fin, flags, p, 3717255332Scy oip->ip_dst, 3718255332Scy oip->ip_src); 3719145522Sdarrenr fin->fin_data[0] = data[0]; 3720145522Sdarrenr fin->fin_data[1] = data[1]; 3721145522Sdarrenr return nat; 3722145522Sdarrenr } 3723145522Sdarrenr } 3724255332Scy 372553642Sguido if (flags & IPN_TCPUDP) { 372664580Sdarrenr minlen += 8; /* + 64bits of data to get ports */ 3727255332Scy /* TRACE (fin,minlen) */ 3728255332Scy if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) { 3729255332Scy ATOMIC_INCL(nside->ns_icmp_short); 373064580Sdarrenr return NULL; 3731255332Scy } 373292685Sdarrenr 373392685Sdarrenr data[0] = fin->fin_data[0]; 373492685Sdarrenr data[1] = fin->fin_data[1]; 3735145522Sdarrenr tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 373692685Sdarrenr fin->fin_data[0] = ntohs(tcp->th_dport); 373792685Sdarrenr fin->fin_data[1] = ntohs(tcp->th_sport); 373892685Sdarrenr 373992685Sdarrenr if (dir == NAT_INBOUND) { 3740255332Scy nat = ipf_nat_inlookup(fin, flags, p, oip->ip_dst, 3741255332Scy oip->ip_src); 374292685Sdarrenr } else { 3743255332Scy nat = ipf_nat_outlookup(fin, flags, p, oip->ip_dst, 3744145522Sdarrenr oip->ip_src); 374592685Sdarrenr } 374692685Sdarrenr fin->fin_data[0] = data[0]; 374792685Sdarrenr fin->fin_data[1] = data[1]; 374892685Sdarrenr return nat; 374953642Sguido } 375060852Sdarrenr if (dir == NAT_INBOUND) 3751255332Scy nat = ipf_nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src); 375260852Sdarrenr else 3753255332Scy nat = ipf_nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src); 3754255332Scy 3755255332Scy return nat; 375653642Sguido} 375753642Sguido 375853642Sguido 3759145522Sdarrenr/* ------------------------------------------------------------------------ */ 3760255332Scy/* Function: ipf_nat_icmperror */ 3761145522Sdarrenr/* Returns: nat_t* - point to matching NAT structure */ 3762145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3763145522Sdarrenr/* nflags(I) - NAT flags for this packet */ 3764145522Sdarrenr/* dir(I) - direction of packet (in/out) */ 3765145522Sdarrenr/* */ 3766145522Sdarrenr/* Fix up an ICMP packet which is an error message for an existing NAT */ 3767145522Sdarrenr/* session. This will correct both packet header data and checksums. */ 3768145522Sdarrenr/* */ 3769145522Sdarrenr/* This should *ONLY* be used for incoming ICMP error packets to make sure */ 3770145522Sdarrenr/* a NAT'd ICMP packet gets correctly recognised. */ 3771145522Sdarrenr/* ------------------------------------------------------------------------ */ 3772255332Scynat_t * 3773255332Scyipf_nat_icmperror(fin, nflags, dir) 3774255332Scy fr_info_t *fin; 3775255332Scy u_int *nflags; 3776255332Scy int dir; 377753642Sguido{ 3778255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3779255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 3780145522Sdarrenr u_32_t sum1, sum2, sumd, sumd2; 3781255332Scy struct in_addr a1, a2, a3, a4; 3782170268Sdarrenr int flags, dlen, odst; 3783145522Sdarrenr icmphdr_t *icmp; 3784145522Sdarrenr u_short *csump; 378595418Sdarrenr tcphdr_t *tcp; 378653642Sguido nat_t *nat; 378753642Sguido ip_t *oip; 3788145522Sdarrenr void *dp; 378953642Sguido 3790255332Scy if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { 3791255332Scy NBUMPSIDED(fin->fin_out, ns_icmp_short); 379263523Sdarrenr return NULL; 3793255332Scy } 3794255332Scy 379567614Sdarrenr /* 3796255332Scy * ipf_nat_icmperrorlookup() will return NULL for `defective' packets. 379767614Sdarrenr */ 3798255332Scy if ((fin->fin_v != 4) || !(nat = ipf_nat_icmperrorlookup(fin, dir))) { 3799255332Scy NBUMPSIDED(fin->fin_out, ns_icmp_notfound); 380053642Sguido return NULL; 3801255332Scy } 380292685Sdarrenr 3803145522Sdarrenr tcp = NULL; 3804145522Sdarrenr csump = NULL; 380592685Sdarrenr flags = 0; 3806130886Sdarrenr sumd2 = 0; 380753642Sguido *nflags = IPN_ICMPERR; 3808145522Sdarrenr icmp = fin->fin_dp; 380953642Sguido oip = (ip_t *)&icmp->icmp_ip; 3810145522Sdarrenr dp = (((char *)oip) + (IP_HL(oip) << 2)); 3811145522Sdarrenr if (oip->ip_p == IPPROTO_TCP) { 3812145522Sdarrenr tcp = (tcphdr_t *)dp; 3813145522Sdarrenr csump = (u_short *)&tcp->th_sum; 381453642Sguido flags = IPN_TCP; 3815145522Sdarrenr } else if (oip->ip_p == IPPROTO_UDP) { 3816145522Sdarrenr udphdr_t *udp; 3817145522Sdarrenr 3818145522Sdarrenr udp = (udphdr_t *)dp; 3819145522Sdarrenr tcp = (tcphdr_t *)dp; 3820145522Sdarrenr csump = (u_short *)&udp->uh_sum; 382153642Sguido flags = IPN_UDP; 3822145522Sdarrenr } else if (oip->ip_p == IPPROTO_ICMP) 3823145522Sdarrenr flags = IPN_ICMPQUERY; 3824145522Sdarrenr dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip); 382595418Sdarrenr 382695418Sdarrenr /* 382753642Sguido * Need to adjust ICMP header to include the real IP#'s and 382853642Sguido * port #'s. Only apply a checksum change relative to the 3829255332Scy * IP address change as it will be modified again in ipf_nat_checkout 383053642Sguido * for both address and port. Two checksum changes are 383153642Sguido * necessary for the two header address changes. Be careful 383253642Sguido * to only modify the checksum once for the port # and twice 383353642Sguido * for the IP#. 383453642Sguido */ 383560852Sdarrenr 383667614Sdarrenr /* 383767614Sdarrenr * Step 1 383867614Sdarrenr * Fix the IP addresses in the offending IP packet. You also need 3839170268Sdarrenr * to adjust the IP header checksum of that offending IP packet. 384067614Sdarrenr * 3841145522Sdarrenr * Normally, you would expect that the ICMP checksum of the 3842130886Sdarrenr * ICMP error message needs to be adjusted as well for the 3843130886Sdarrenr * IP address change in oip. 3844145522Sdarrenr * However, this is a NOP, because the ICMP checksum is 3845130886Sdarrenr * calculated over the complete ICMP packet, which includes the 3846145522Sdarrenr * changed oip IP addresses and oip->ip_sum. However, these 3847130886Sdarrenr * two changes cancel each other out (if the delta for 3848145522Sdarrenr * the IP address is x, then the delta for ip_sum is minus x), 3849130886Sdarrenr * so no change in the icmp_cksum is necessary. 3850130886Sdarrenr * 3851170268Sdarrenr * Inbound ICMP 3852170268Sdarrenr * ------------ 3853170268Sdarrenr * MAP rule, SRC=a,DST=b -> SRC=c,DST=b 3854170268Sdarrenr * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b) 3855255332Scy * - OIP_SRC(c)=nat_newsrcip, OIP_DST(b)=nat_newdstip 3856255332Scy *=> OIP_SRC(c)=nat_oldsrcip, OIP_DST(b)=nat_olddstip 3857170268Sdarrenr * 3858170268Sdarrenr * RDR rule, SRC=a,DST=b -> SRC=a,DST=c 3859170268Sdarrenr * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a) 3860255332Scy * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip 3861255332Scy *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip 3862170268Sdarrenr * 3863255332Scy * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d 3864255332Scy * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d) 3865255332Scy * - OIP_SRC(c)=nat_newsrcip, OIP_DST(d)=nat_newdstip 3866255332Scy *=> OIP_SRC(c)=nat_oldsrcip, OIP_DST(d)=nat_olddstip 3867255332Scy * 3868255332Scy * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d 3869255332Scy * - response to outgoing packet (d,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) 3870255332Scy * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip 3871255332Scy *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip 3872255332Scy * 3873170268Sdarrenr * Outbound ICMP 3874170268Sdarrenr * ------------- 3875170268Sdarrenr * MAP rule, SRC=a,DST=b -> SRC=c,DST=b 3876170268Sdarrenr * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) 3877255332Scy * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip 3878255332Scy *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip 3879170268Sdarrenr * 3880170268Sdarrenr * RDR rule, SRC=a,DST=b -> SRC=a,DST=c 3881170268Sdarrenr * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c) 3882255332Scy * - OIP_SRC(a)=nat_newsrcip, OIP_DST(c)=nat_newdstip 3883255332Scy *=> OIP_SRC(a)=nat_oldsrcip, OIP_DST(c)=nat_olddstip 3884170268Sdarrenr * 3885255332Scy * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d 3886255332Scy * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d) 3887255332Scy * - OIP_SRC(c)=nat_olddstip, OIP_DST(d)=nat_oldsrcip 3888255332Scy *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip 3889255332Scy * 3890255332Scy * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d 3891255332Scy * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a) 3892255332Scy * - OIP_SRC(b)=nat_newsrcip, OIP_DST(a)=nat_newdstip 3893255332Scy *=> OIP_SRC(a)=nat_oldsrcip, OIP_DST(c)=nat_olddstip 3894130886Sdarrenr */ 3895255332Scy 3896255332Scy if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) || 3897255332Scy ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) { 3898255332Scy a1.s_addr = ntohl(nat->nat_osrcaddr); 3899255332Scy a4.s_addr = ntohl(oip->ip_src.s_addr); 3900255332Scy a3.s_addr = ntohl(nat->nat_odstaddr); 3901255332Scy a2.s_addr = ntohl(oip->ip_dst.s_addr); 3902170268Sdarrenr oip->ip_src.s_addr = htonl(a1.s_addr); 3903255332Scy oip->ip_dst.s_addr = htonl(a3.s_addr); 3904255332Scy odst = 1; 3905170268Sdarrenr } else { 3906255332Scy a1.s_addr = ntohl(nat->nat_ndstaddr); 3907170268Sdarrenr a2.s_addr = ntohl(oip->ip_dst.s_addr); 3908255332Scy a3.s_addr = ntohl(nat->nat_nsrcaddr); 3909255332Scy a4.s_addr = ntohl(oip->ip_src.s_addr); 3910255332Scy oip->ip_dst.s_addr = htonl(a3.s_addr); 3911255332Scy oip->ip_src.s_addr = htonl(a1.s_addr); 3912255332Scy odst = 0; 3913170268Sdarrenr } 3914255332Scy sum1 = 0; 3915255332Scy sum2 = 0; 3916255332Scy sumd = 0; 3917255332Scy CALC_SUMD(a2.s_addr, a3.s_addr, sum1); 3918255332Scy CALC_SUMD(a4.s_addr, a1.s_addr, sum2); 3919255332Scy sumd = sum2 + sum1; 3920255332Scy if (sumd != 0) 3921255332Scy ipf_fix_datacksum(&oip->ip_sum, sumd); 3922130886Sdarrenr 3923170268Sdarrenr sumd2 = sumd; 3924170268Sdarrenr sum1 = 0; 3925170268Sdarrenr sum2 = 0; 3926170268Sdarrenr 3927130886Sdarrenr /* 3928170268Sdarrenr * Fix UDP pseudo header checksum to compensate for the 3929170268Sdarrenr * IP address change. 3930130886Sdarrenr */ 3931130886Sdarrenr if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) { 3932255332Scy u_32_t sum3, sum4, sumt; 3933255332Scy 393464580Sdarrenr /* 393567614Sdarrenr * Step 2 : 393667614Sdarrenr * For offending TCP/UDP IP packets, translate the ports as 393767614Sdarrenr * well, based on the NAT specification. Of course such 3938170268Sdarrenr * a change may be reflected in the ICMP checksum as well. 393967614Sdarrenr * 394067614Sdarrenr * Since the port fields are part of the TCP/UDP checksum 394167614Sdarrenr * of the offending IP packet, you need to adjust that checksum 3942255332Scy * as well... except that the change in the port numbers should 3943170268Sdarrenr * be offset by the checksum change. However, the TCP/UDP 3944170268Sdarrenr * checksum will also need to change if there has been an 3945170268Sdarrenr * IP address change. 394667614Sdarrenr */ 3947170268Sdarrenr if (odst == 1) { 3948255332Scy sum1 = ntohs(nat->nat_osport); 3949255332Scy sum4 = ntohs(tcp->th_sport); 3950255332Scy sum3 = ntohs(nat->nat_odport); 3951255332Scy sum2 = ntohs(tcp->th_dport); 3952145522Sdarrenr 3953170268Sdarrenr tcp->th_sport = htons(sum1); 3954255332Scy tcp->th_dport = htons(sum3); 3955170268Sdarrenr } else { 3956255332Scy sum1 = ntohs(nat->nat_ndport); 3957145522Sdarrenr sum2 = ntohs(tcp->th_dport); 3958255332Scy sum3 = ntohs(nat->nat_nsport); 3959255332Scy sum4 = ntohs(tcp->th_sport); 3960170268Sdarrenr 3961255332Scy tcp->th_dport = htons(sum3); 3962255332Scy tcp->th_sport = htons(sum1); 3963145522Sdarrenr } 3964255332Scy CALC_SUMD(sum4, sum1, sumt); 3965255332Scy sumd += sumt; 3966255332Scy CALC_SUMD(sum2, sum3, sumt); 3967255332Scy sumd += sumt; 396867614Sdarrenr 3969170268Sdarrenr if (sumd != 0 || sumd2 != 0) { 3970145522Sdarrenr /* 3971170268Sdarrenr * At this point, sumd is the delta to apply to the 3972170268Sdarrenr * TCP/UDP header, given the changes in both the IP 3973170268Sdarrenr * address and the ports and sumd2 is the delta to 3974170268Sdarrenr * apply to the ICMP header, given the IP address 3975170268Sdarrenr * change delta that may need to be applied to the 3976170268Sdarrenr * TCP/UDP checksum instead. 3977145522Sdarrenr * 3978170268Sdarrenr * If we will both the IP and TCP/UDP checksums 3979170268Sdarrenr * then the ICMP checksum changes by the address 3980170268Sdarrenr * delta applied to the TCP/UDP checksum. If we 3981170268Sdarrenr * do not change the TCP/UDP checksum them we 3982170268Sdarrenr * apply the delta in ports to the ICMP checksum. 3983145522Sdarrenr */ 3984161356Sguido if (oip->ip_p == IPPROTO_UDP) { 3985161356Sguido if ((dlen >= 8) && (*csump != 0)) { 3986255332Scy ipf_fix_datacksum(csump, sumd); 3987161356Sguido } else { 3988255332Scy CALC_SUMD(sum1, sum4, sumd2); 3989255332Scy CALC_SUMD(sum3, sum2, sumt); 3990255332Scy sumd2 += sumt; 3991161356Sguido } 3992170268Sdarrenr } else if (oip->ip_p == IPPROTO_TCP) { 3993145522Sdarrenr if (dlen >= 18) { 3994255332Scy ipf_fix_datacksum(csump, sumd); 3995145522Sdarrenr } else { 3996255332Scy CALC_SUMD(sum1, sum4, sumd2); 3997255332Scy CALC_SUMD(sum3, sum2, sumt); 3998255332Scy sumd2 += sumt; 399967614Sdarrenr } 400053642Sguido } 4001170268Sdarrenr if (sumd2 != 0) { 4002170268Sdarrenr sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 4003170268Sdarrenr sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 4004170268Sdarrenr sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 4005255332Scy ipf_fix_incksum(0, &icmp->icmp_cksum, sumd2, 0); 4006130886Sdarrenr } 400753642Sguido } 4008145522Sdarrenr } else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) { 4009145522Sdarrenr icmphdr_t *orgicmp; 4010145522Sdarrenr 4011145522Sdarrenr /* 4012145522Sdarrenr * XXX - what if this is bogus hl and we go off the end ? 4013255332Scy * In this case, ipf_nat_icmperrorlookup() will have 4014255332Scy * returned NULL. 4015145522Sdarrenr */ 4016145522Sdarrenr orgicmp = (icmphdr_t *)dp; 4017145522Sdarrenr 4018170268Sdarrenr if (odst == 1) { 4019255332Scy if (orgicmp->icmp_id != nat->nat_osport) { 4020145522Sdarrenr 4021145522Sdarrenr /* 4022145522Sdarrenr * Fix ICMP checksum (of the offening ICMP 4023145522Sdarrenr * query packet) to compensate the change 4024145522Sdarrenr * in the ICMP id of the offending ICMP 4025145522Sdarrenr * packet. 4026145522Sdarrenr * 4027145522Sdarrenr * Since you modify orgicmp->icmp_id with 4028145522Sdarrenr * a delta (say x) and you compensate that 4029145522Sdarrenr * in origicmp->icmp_cksum with a delta 4030145522Sdarrenr * minus x, you don't have to adjust the 4031145522Sdarrenr * overall icmp->icmp_cksum 4032145522Sdarrenr */ 4033145522Sdarrenr sum1 = ntohs(orgicmp->icmp_id); 4034255332Scy sum2 = ntohs(nat->nat_oicmpid); 4035145522Sdarrenr CALC_SUMD(sum1, sum2, sumd); 4036255332Scy orgicmp->icmp_id = nat->nat_oicmpid; 4037255332Scy ipf_fix_datacksum(&orgicmp->icmp_cksum, sumd); 4038145522Sdarrenr } 4039145522Sdarrenr } /* nat_dir == NAT_INBOUND is impossible for icmp queries */ 404053642Sguido } 404153642Sguido return nat; 404253642Sguido} 404353642Sguido 404453642Sguido 404553642Sguido/* 4046255332Scy * MAP-IN MAP-OUT RDR-IN RDR-OUT 4047255332Scy * osrc X == src == src X 4048255332Scy * odst X == dst == dst X 4049255332Scy * nsrc == dst X X == dst 4050255332Scy * ndst == src X X == src 4051255332Scy * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND 4052255332Scy */ 4053255332Scy/* 4054145522Sdarrenr * NB: these lookups don't lock access to the list, it assumed that it has 4055145522Sdarrenr * already been done! 405653642Sguido */ 4057145522Sdarrenr/* ------------------------------------------------------------------------ */ 4058255332Scy/* Function: ipf_nat_inlookup */ 4059145522Sdarrenr/* Returns: nat_t* - NULL == no match, */ 4060145522Sdarrenr/* else pointer to matching NAT entry */ 4061145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4062145522Sdarrenr/* flags(I) - NAT flags for this packet */ 4063145522Sdarrenr/* p(I) - protocol for this packet */ 4064145522Sdarrenr/* src(I) - source IP address */ 4065145522Sdarrenr/* mapdst(I) - destination IP address */ 4066145522Sdarrenr/* */ 4067145522Sdarrenr/* Lookup a nat entry based on the mapped destination ip address/port and */ 4068145522Sdarrenr/* real source address/port. We use this lookup when receiving a packet, */ 4069145522Sdarrenr/* we're looking for a table entry, based on the destination address. */ 4070145522Sdarrenr/* */ 4071145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 4072145522Sdarrenr/* */ 4073255332Scy/* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ 4074145522Sdarrenr/* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 4075145522Sdarrenr/* */ 4076145522Sdarrenr/* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 4077145522Sdarrenr/* the packet is of said protocol */ 4078145522Sdarrenr/* ------------------------------------------------------------------------ */ 4079255332Scynat_t * 4080255332Scyipf_nat_inlookup(fin, flags, p, src, mapdst) 4081255332Scy fr_info_t *fin; 4082255332Scy u_int flags, p; 4083255332Scy struct in_addr src , mapdst; 408453642Sguido{ 4085255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 4086255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 4087145522Sdarrenr u_short sport, dport; 4088145522Sdarrenr grehdr_t *gre; 408992685Sdarrenr ipnat_t *ipn; 4090145522Sdarrenr u_int sflags; 4091145522Sdarrenr nat_t *nat; 4092145522Sdarrenr int nflags; 4093145522Sdarrenr u_32_t dst; 409492685Sdarrenr void *ifp; 4095255332Scy u_int hv, rhv; 409653642Sguido 4097161356Sguido ifp = fin->fin_ifp; 4098145522Sdarrenr gre = NULL; 409967614Sdarrenr dst = mapdst.s_addr; 4100145522Sdarrenr sflags = flags & NAT_TCPUDPICMP; 4101145522Sdarrenr 4102145522Sdarrenr switch (p) 4103145522Sdarrenr { 4104145522Sdarrenr case IPPROTO_TCP : 4105145522Sdarrenr case IPPROTO_UDP : 410692685Sdarrenr sport = htons(fin->fin_data[0]); 410792685Sdarrenr dport = htons(fin->fin_data[1]); 4108145522Sdarrenr break; 4109145522Sdarrenr case IPPROTO_ICMP : 4110255332Scy if (flags & IPN_ICMPERR) { 4111145522Sdarrenr sport = fin->fin_data[1]; 4112255332Scy dport = 0; 4113255332Scy } else { 4114145522Sdarrenr dport = fin->fin_data[1]; 4115255332Scy sport = 0; 4116255332Scy } 4117145522Sdarrenr break; 4118145522Sdarrenr default : 4119255332Scy sport = 0; 4120255332Scy dport = 0; 4121145522Sdarrenr break; 412292685Sdarrenr } 412353642Sguido 4124145522Sdarrenr 4125145522Sdarrenr if ((flags & SI_WILDP) != 0) 4126145522Sdarrenr goto find_in_wild_ports; 4127145522Sdarrenr 4128255332Scy rhv = NAT_HASH_FN(dst, dport, 0xffffffff); 4129255332Scy rhv = NAT_HASH_FN(src.s_addr, rhv + sport, 0xffffffff); 4130255332Scy hv = rhv % softn->ipf_nat_table_sz; 4131255332Scy nat = softn->ipf_nat_table[1][hv]; 4132255332Scy /* TRACE dst, dport, src, sport, hv, nat */ 4133255332Scy 413453642Sguido for (; nat; nat = nat->nat_hnext[1]) { 4135161356Sguido if (nat->nat_ifps[0] != NULL) { 4136161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 4137161356Sguido continue; 4138255332Scy } 4139161356Sguido 4140255332Scy if (nat->nat_pr[0] != p) 4141255332Scy continue; 4142145522Sdarrenr 4143255332Scy switch (nat->nat_dir) 4144255332Scy { 4145255332Scy case NAT_INBOUND : 4146255332Scy case NAT_DIVERTIN : 4147255332Scy if (nat->nat_v[0] != 4) 4148255332Scy continue; 4149255332Scy if (nat->nat_osrcaddr != src.s_addr || 4150255332Scy nat->nat_odstaddr != dst) 4151255332Scy continue; 4152255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 4153255332Scy if (nat->nat_osport != sport) 4154145522Sdarrenr continue; 4155255332Scy if (nat->nat_odport != dport) 4156255332Scy continue; 4157255332Scy 4158255332Scy } else if (p == IPPROTO_ICMP) { 4159255332Scy if (nat->nat_osport != dport) { 4160255332Scy continue; 4161145522Sdarrenr } 4162255332Scy } 4163255332Scy break; 4164255332Scy case NAT_DIVERTOUT : 4165255332Scy if (nat->nat_dlocal) 4166255332Scy continue; 4167255332Scy case NAT_OUTBOUND : 4168255332Scy if (nat->nat_v[1] != 4) 4169255332Scy continue; 4170255332Scy if (nat->nat_dlocal) 4171255332Scy continue; 4172255332Scy if (nat->nat_dlocal) 4173255332Scy continue; 4174255332Scy if (nat->nat_ndstaddr != src.s_addr || 4175255332Scy nat->nat_nsrcaddr != dst) 4176255332Scy continue; 4177255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 4178255332Scy if (nat->nat_ndport != sport) 417992685Sdarrenr continue; 4180255332Scy if (nat->nat_nsport != dport) 418192685Sdarrenr continue; 4182255332Scy 4183255332Scy } else if (p == IPPROTO_ICMP) { 4184255332Scy if (nat->nat_osport != dport) { 4185255332Scy continue; 4186255332Scy } 418792685Sdarrenr } 4188255332Scy break; 4189255332Scy } 419092685Sdarrenr 4191255332Scy 4192255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 419392685Sdarrenr ipn = nat->nat_ptr; 419492685Sdarrenr if ((ipn != NULL) && (nat->nat_aps != NULL)) 4195255332Scy if (ipf_proxy_match(fin, nat) != 0) 419692685Sdarrenr continue; 419792685Sdarrenr } 4198255332Scy if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { 4199255332Scy nat->nat_ifps[0] = ifp; 4200255332Scy nat->nat_mtu[0] = GETIFMTU_4(ifp); 4201255332Scy } 4202255332Scy return nat; 420353642Sguido } 4204145522Sdarrenr 4205145522Sdarrenr /* 4206145522Sdarrenr * So if we didn't find it but there are wildcard members in the hash 4207145522Sdarrenr * table, go back and look for them. We do this search and update here 4208145522Sdarrenr * because it is modifying the NAT table and we want to do this only 4209145522Sdarrenr * for the first packet that matches. The exception, of course, is 4210145522Sdarrenr * for "dummy" (FI_IGNORE) lookups. 4211145522Sdarrenr */ 4212145522Sdarrenrfind_in_wild_ports: 4213255332Scy if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { 4214255332Scy NBUMPSIDEX(0, ns_lookup_miss, ns_lookup_miss_0); 421567614Sdarrenr return NULL; 4216255332Scy } 4217255332Scy if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { 4218255332Scy NBUMPSIDEX(0, ns_lookup_nowild, ns_lookup_nowild_0); 4219145522Sdarrenr return NULL; 4220255332Scy } 4221145522Sdarrenr 4222255332Scy RWLOCK_EXIT(&softc->ipf_nat); 4223145522Sdarrenr 422480482Sdarrenr hv = NAT_HASH_FN(dst, 0, 0xffffffff); 4225255332Scy hv = NAT_HASH_FN(src.s_addr, hv, softn->ipf_nat_table_sz); 4226255332Scy WRITE_ENTER(&softc->ipf_nat); 4227145522Sdarrenr 4228255332Scy nat = softn->ipf_nat_table[1][hv]; 4229255332Scy /* TRACE dst, src, hv, nat */ 423067614Sdarrenr for (; nat; nat = nat->nat_hnext[1]) { 4231161356Sguido if (nat->nat_ifps[0] != NULL) { 4232161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 4233161356Sguido continue; 4234255332Scy } 4235145522Sdarrenr 4236255332Scy if (nat->nat_pr[0] != fin->fin_p) 423767614Sdarrenr continue; 4238145522Sdarrenr 4239255332Scy switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)) 4240255332Scy { 4241255332Scy case NAT_INBOUND : 4242255332Scy if (nat->nat_v[0] != 4) 4243255332Scy continue; 4244255332Scy if (nat->nat_osrcaddr != src.s_addr || 4245255332Scy nat->nat_odstaddr != dst) 4246255332Scy continue; 4247255332Scy break; 4248255332Scy case NAT_OUTBOUND : 4249255332Scy if (nat->nat_v[1] != 4) 4250255332Scy continue; 4251255332Scy if (nat->nat_ndstaddr != src.s_addr || 4252255332Scy nat->nat_nsrcaddr != dst) 4253255332Scy continue; 4254255332Scy break; 4255255332Scy } 4256255332Scy 4257145522Sdarrenr nflags = nat->nat_flags; 4258145522Sdarrenr if (!(nflags & (NAT_TCPUDP|SI_WILDP))) 4259145522Sdarrenr continue; 4260145522Sdarrenr 4261255332Scy if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags, 4262255332Scy NAT_INBOUND) == 1) { 4263145522Sdarrenr if ((fin->fin_flx & FI_IGNORE) != 0) 4264145522Sdarrenr break; 4265145522Sdarrenr if ((nflags & SI_CLONE) != 0) { 4266255332Scy nat = ipf_nat_clone(fin, nat); 4267145522Sdarrenr if (nat == NULL) 4268145522Sdarrenr break; 4269145522Sdarrenr } else { 4270255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 4271255332Scy softn->ipf_nat_stats.ns_wilds--; 4272255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 4273145522Sdarrenr } 4274255332Scy 4275255332Scy if (nat->nat_dir == NAT_INBOUND) { 4276255332Scy if (nat->nat_osport == 0) { 4277255332Scy nat->nat_osport = sport; 4278255332Scy nat->nat_nsport = sport; 4279255332Scy } 4280255332Scy if (nat->nat_odport == 0) { 4281255332Scy nat->nat_odport = dport; 4282255332Scy nat->nat_ndport = dport; 4283255332Scy } 4284255332Scy } else if (nat->nat_dir == NAT_OUTBOUND) { 4285255332Scy if (nat->nat_osport == 0) { 4286255332Scy nat->nat_osport = dport; 4287255332Scy nat->nat_nsport = dport; 4288255332Scy } 4289255332Scy if (nat->nat_odport == 0) { 4290255332Scy nat->nat_odport = sport; 4291255332Scy nat->nat_ndport = sport; 4292255332Scy } 4293255332Scy } 4294255332Scy if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { 4295255332Scy nat->nat_ifps[0] = ifp; 4296255332Scy nat->nat_mtu[0] = GETIFMTU_4(ifp); 4297255332Scy } 4298145522Sdarrenr nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 4299255332Scy ipf_nat_tabmove(softn, nat); 430067614Sdarrenr break; 430167614Sdarrenr } 430267614Sdarrenr } 4303145522Sdarrenr 4304255332Scy MUTEX_DOWNGRADE(&softc->ipf_nat); 4305145522Sdarrenr 4306255332Scy if (nat == NULL) { 4307255332Scy NBUMPSIDE(0, ns_lookup_miss); 4308255332Scy } 430967614Sdarrenr return nat; 431053642Sguido} 431153642Sguido 431253642Sguido 4313145522Sdarrenr/* ------------------------------------------------------------------------ */ 4314255332Scy/* Function: ipf_nat_tabmove */ 4315145522Sdarrenr/* Returns: Nil */ 4316255332Scy/* Parameters: softn(I) - pointer to NAT context structure */ 4317255332Scy/* nat(I) - pointer to NAT structure */ 4318145522Sdarrenr/* Write Lock: ipf_nat */ 4319145522Sdarrenr/* */ 4320145522Sdarrenr/* This function is only called for TCP/UDP NAT table entries where the */ 4321145522Sdarrenr/* original was placed in the table without hashing on the ports and we now */ 4322145522Sdarrenr/* want to include hashing on port numbers. */ 4323145522Sdarrenr/* ------------------------------------------------------------------------ */ 4324255332Scystatic void 4325255332Scyipf_nat_tabmove(softn, nat) 4326255332Scy ipf_nat_softc_t *softn; 4327255332Scy nat_t *nat; 432867614Sdarrenr{ 4329255332Scy u_int hv0, hv1, rhv0, rhv1; 4330255332Scy natstat_t *nsp; 433167614Sdarrenr nat_t **natp; 433267614Sdarrenr 4333145522Sdarrenr if (nat->nat_flags & SI_CLONE) 4334145522Sdarrenr return; 433572006Sdarrenr 4336255332Scy nsp = &softn->ipf_nat_stats; 433767614Sdarrenr /* 433867614Sdarrenr * Remove the NAT entry from the old location 433967614Sdarrenr */ 434067614Sdarrenr if (nat->nat_hnext[0]) 434167614Sdarrenr nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 434267614Sdarrenr *nat->nat_phnext[0] = nat->nat_hnext[0]; 4343255332Scy nsp->ns_side[0].ns_bucketlen[nat->nat_hv[0] % 4344255332Scy softn->ipf_nat_table_sz]--; 434567614Sdarrenr 434667614Sdarrenr if (nat->nat_hnext[1]) 434767853Sdarrenr nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 434867614Sdarrenr *nat->nat_phnext[1] = nat->nat_hnext[1]; 4349255332Scy nsp->ns_side[1].ns_bucketlen[nat->nat_hv[1] % 4350255332Scy softn->ipf_nat_table_sz]--; 435167614Sdarrenr 435267853Sdarrenr /* 435367853Sdarrenr * Add into the NAT table in the new position 435467853Sdarrenr */ 4355255332Scy rhv0 = NAT_HASH_FN(nat->nat_osrcaddr, nat->nat_osport, 0xffffffff); 4356255332Scy rhv0 = NAT_HASH_FN(nat->nat_odstaddr, rhv0 + nat->nat_odport, 4357255332Scy 0xffffffff); 4358255332Scy rhv1 = NAT_HASH_FN(nat->nat_nsrcaddr, nat->nat_nsport, 0xffffffff); 4359255332Scy rhv1 = NAT_HASH_FN(nat->nat_ndstaddr, rhv1 + nat->nat_ndport, 4360255332Scy 0xffffffff); 4361255332Scy 4362255332Scy hv0 = rhv0 % softn->ipf_nat_table_sz; 4363255332Scy hv1 = rhv1 % softn->ipf_nat_table_sz; 4364255332Scy 4365255332Scy if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) { 4366255332Scy u_int swap; 4367255332Scy 4368255332Scy swap = hv0; 4369255332Scy hv0 = hv1; 4370255332Scy hv1 = swap; 4371255332Scy } 4372255332Scy 4373255332Scy /* TRACE nat_osrcaddr, nat_osport, nat_odstaddr, nat_odport, hv0 */ 4374255332Scy /* TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, nat_ndport, hv1 */ 4375255332Scy 4376255332Scy nat->nat_hv[0] = rhv0; 4377255332Scy natp = &softn->ipf_nat_table[0][hv0]; 437867614Sdarrenr if (*natp) 437967614Sdarrenr (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 438067614Sdarrenr nat->nat_phnext[0] = natp; 438167614Sdarrenr nat->nat_hnext[0] = *natp; 438267614Sdarrenr *natp = nat; 4383255332Scy nsp->ns_side[0].ns_bucketlen[hv0]++; 438467614Sdarrenr 4385255332Scy nat->nat_hv[1] = rhv1; 4386255332Scy natp = &softn->ipf_nat_table[1][hv1]; 438767614Sdarrenr if (*natp) 438867614Sdarrenr (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 438967614Sdarrenr nat->nat_phnext[1] = natp; 439067614Sdarrenr nat->nat_hnext[1] = *natp; 439167614Sdarrenr *natp = nat; 4392255332Scy nsp->ns_side[1].ns_bucketlen[hv1]++; 439367614Sdarrenr} 439467614Sdarrenr 439567614Sdarrenr 4396145522Sdarrenr/* ------------------------------------------------------------------------ */ 4397255332Scy/* Function: ipf_nat_outlookup */ 4398145522Sdarrenr/* Returns: nat_t* - NULL == no match, */ 4399145522Sdarrenr/* else pointer to matching NAT entry */ 4400145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4401145522Sdarrenr/* flags(I) - NAT flags for this packet */ 4402145522Sdarrenr/* p(I) - protocol for this packet */ 4403145522Sdarrenr/* src(I) - source IP address */ 4404145522Sdarrenr/* dst(I) - destination IP address */ 4405255332Scy/* rw(I) - 1 == write lock on held, 0 == read lock. */ 4406145522Sdarrenr/* */ 4407145522Sdarrenr/* Lookup a nat entry based on the source 'real' ip address/port and */ 4408145522Sdarrenr/* destination address/port. We use this lookup when sending a packet out, */ 4409145522Sdarrenr/* we're looking for a table entry, based on the source address. */ 4410145522Sdarrenr/* */ 4411145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 4412145522Sdarrenr/* */ 4413255332Scy/* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ 4414145522Sdarrenr/* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 4415145522Sdarrenr/* */ 4416145522Sdarrenr/* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 4417145522Sdarrenr/* the packet is of said protocol */ 4418145522Sdarrenr/* ------------------------------------------------------------------------ */ 4419255332Scynat_t * 4420255332Scyipf_nat_outlookup(fin, flags, p, src, dst) 4421255332Scy fr_info_t *fin; 4422255332Scy u_int flags, p; 4423255332Scy struct in_addr src , dst; 442453642Sguido{ 4425255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 4426255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 4427145522Sdarrenr u_short sport, dport; 4428145522Sdarrenr u_int sflags; 442992685Sdarrenr ipnat_t *ipn; 4430145522Sdarrenr nat_t *nat; 443192685Sdarrenr void *ifp; 443253642Sguido u_int hv; 443353642Sguido 443492685Sdarrenr ifp = fin->fin_ifp; 4435145522Sdarrenr sflags = flags & IPN_TCPUDPICMP; 4436145522Sdarrenr sport = 0; 4437145522Sdarrenr dport = 0; 4438145522Sdarrenr 4439145522Sdarrenr switch (p) 4440145522Sdarrenr { 4441145522Sdarrenr case IPPROTO_TCP : 4442145522Sdarrenr case IPPROTO_UDP : 4443145522Sdarrenr sport = htons(fin->fin_data[0]); 4444145522Sdarrenr dport = htons(fin->fin_data[1]); 4445145522Sdarrenr break; 4446145522Sdarrenr case IPPROTO_ICMP : 4447145522Sdarrenr if (flags & IPN_ICMPERR) 4448145522Sdarrenr sport = fin->fin_data[1]; 4449145522Sdarrenr else 4450145522Sdarrenr dport = fin->fin_data[1]; 4451145522Sdarrenr break; 4452145522Sdarrenr default : 4453145522Sdarrenr break; 445492685Sdarrenr } 445553642Sguido 4456145522Sdarrenr if ((flags & SI_WILDP) != 0) 4457145522Sdarrenr goto find_out_wild_ports; 4458145522Sdarrenr 4459255332Scy hv = NAT_HASH_FN(src.s_addr, sport, 0xffffffff); 4460255332Scy hv = NAT_HASH_FN(dst.s_addr, hv + dport, softn->ipf_nat_table_sz); 4461255332Scy nat = softn->ipf_nat_table[0][hv]; 4462255332Scy 4463255332Scy /* TRACE src, sport, dst, dport, hv, nat */ 4464255332Scy 446553642Sguido for (; nat; nat = nat->nat_hnext[0]) { 4466161356Sguido if (nat->nat_ifps[1] != NULL) { 4467161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 4468161356Sguido continue; 4469255332Scy } 4470161356Sguido 4471255332Scy if (nat->nat_pr[1] != p) 4472255332Scy continue; 447353642Sguido 4474255332Scy switch (nat->nat_dir) 4475255332Scy { 4476255332Scy case NAT_INBOUND : 4477255332Scy case NAT_DIVERTIN : 4478255332Scy if (nat->nat_v[1] != 4) 4479255332Scy continue; 4480255332Scy if (nat->nat_ndstaddr != src.s_addr || 4481255332Scy nat->nat_nsrcaddr != dst.s_addr) 4482255332Scy continue; 4483255332Scy 4484255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 4485255332Scy if (nat->nat_ndport != sport) 4486145522Sdarrenr continue; 4487255332Scy if (nat->nat_nsport != dport) 448892685Sdarrenr continue; 4489255332Scy 4490255332Scy } else if (p == IPPROTO_ICMP) { 4491255332Scy if (nat->nat_osport != dport) { 449292685Sdarrenr continue; 4493255332Scy } 449492685Sdarrenr } 4495255332Scy break; 4496255332Scy case NAT_OUTBOUND : 4497255332Scy case NAT_DIVERTOUT : 4498255332Scy if (nat->nat_v[0] != 4) 4499255332Scy continue; 4500255332Scy if (nat->nat_osrcaddr != src.s_addr || 4501255332Scy nat->nat_odstaddr != dst.s_addr) 4502255332Scy continue; 450392685Sdarrenr 4504255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 4505255332Scy if (nat->nat_odport != dport) 450692685Sdarrenr continue; 4507255332Scy if (nat->nat_osport != sport) 4508255332Scy continue; 4509255332Scy 4510255332Scy } else if (p == IPPROTO_ICMP) { 4511255332Scy if (nat->nat_osport != dport) { 4512255332Scy continue; 4513255332Scy } 4514255332Scy } 4515255332Scy break; 451692685Sdarrenr } 4517255332Scy 4518255332Scy ipn = nat->nat_ptr; 4519255332Scy if ((ipn != NULL) && (nat->nat_aps != NULL)) 4520255332Scy if (ipf_proxy_match(fin, nat) != 0) 4521255332Scy continue; 4522255332Scy 4523255332Scy if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { 4524255332Scy nat->nat_ifps[1] = ifp; 4525255332Scy nat->nat_mtu[1] = GETIFMTU_4(ifp); 4526255332Scy } 4527255332Scy return nat; 452853642Sguido } 4529145522Sdarrenr 4530145522Sdarrenr /* 4531145522Sdarrenr * So if we didn't find it but there are wildcard members in the hash 4532145522Sdarrenr * table, go back and look for them. We do this search and update here 4533145522Sdarrenr * because it is modifying the NAT table and we want to do this only 4534145522Sdarrenr * for the first packet that matches. The exception, of course, is 4535145522Sdarrenr * for "dummy" (FI_IGNORE) lookups. 4536145522Sdarrenr */ 4537145522Sdarrenrfind_out_wild_ports: 4538255332Scy if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { 4539255332Scy NBUMPSIDEX(1, ns_lookup_miss, ns_lookup_miss_1); 454067614Sdarrenr return NULL; 4541255332Scy } 4542255332Scy if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { 4543255332Scy NBUMPSIDEX(1, ns_lookup_nowild, ns_lookup_nowild_1); 4544145522Sdarrenr return NULL; 4545255332Scy } 454692685Sdarrenr 4547255332Scy RWLOCK_EXIT(&softc->ipf_nat); 4548145522Sdarrenr 4549255332Scy hv = NAT_HASH_FN(src.s_addr, 0, 0xffffffff); 4550255332Scy hv = NAT_HASH_FN(dst.s_addr, hv, softn->ipf_nat_table_sz); 4551145522Sdarrenr 4552255332Scy WRITE_ENTER(&softc->ipf_nat); 4553145522Sdarrenr 4554255332Scy nat = softn->ipf_nat_table[0][hv]; 455567614Sdarrenr for (; nat; nat = nat->nat_hnext[0]) { 4556161356Sguido if (nat->nat_ifps[1] != NULL) { 4557161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 4558161356Sguido continue; 4559255332Scy } 4560145522Sdarrenr 4561255332Scy if (nat->nat_pr[1] != fin->fin_p) 456267614Sdarrenr continue; 4563145522Sdarrenr 4564255332Scy switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)) 4565255332Scy { 4566255332Scy case NAT_INBOUND : 4567255332Scy if (nat->nat_v[1] != 4) 4568255332Scy continue; 4569255332Scy if (nat->nat_ndstaddr != src.s_addr || 4570255332Scy nat->nat_nsrcaddr != dst.s_addr) 4571255332Scy continue; 4572255332Scy break; 4573255332Scy case NAT_OUTBOUND : 4574255332Scy if (nat->nat_v[0] != 4) 4575255332Scy continue; 4576255332Scy if (nat->nat_osrcaddr != src.s_addr || 4577255332Scy nat->nat_odstaddr != dst.s_addr) 4578255332Scy continue; 4579255332Scy break; 4580255332Scy } 4581255332Scy 4582255332Scy if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP))) 4583145522Sdarrenr continue; 4584145522Sdarrenr 4585255332Scy if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags, 4586255332Scy NAT_OUTBOUND) == 1) { 4587145522Sdarrenr if ((fin->fin_flx & FI_IGNORE) != 0) 4588145522Sdarrenr break; 4589255332Scy if ((nat->nat_flags & SI_CLONE) != 0) { 4590255332Scy nat = ipf_nat_clone(fin, nat); 4591145522Sdarrenr if (nat == NULL) 4592145522Sdarrenr break; 4593145522Sdarrenr } else { 4594255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 4595255332Scy softn->ipf_nat_stats.ns_wilds--; 4596255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 4597145522Sdarrenr } 4598255332Scy 4599255332Scy if (nat->nat_dir == NAT_OUTBOUND) { 4600255332Scy if (nat->nat_osport == 0) { 4601255332Scy nat->nat_osport = sport; 4602255332Scy nat->nat_nsport = sport; 4603255332Scy } 4604255332Scy if (nat->nat_odport == 0) { 4605255332Scy nat->nat_odport = dport; 4606255332Scy nat->nat_ndport = dport; 4607255332Scy } 4608255332Scy } else if (nat->nat_dir == NAT_INBOUND) { 4609255332Scy if (nat->nat_osport == 0) { 4610255332Scy nat->nat_osport = dport; 4611255332Scy nat->nat_nsport = dport; 4612255332Scy } 4613255332Scy if (nat->nat_odport == 0) { 4614255332Scy nat->nat_odport = sport; 4615255332Scy nat->nat_ndport = sport; 4616255332Scy } 4617255332Scy } 4618255332Scy if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { 4619255332Scy nat->nat_ifps[1] = ifp; 4620255332Scy nat->nat_mtu[1] = GETIFMTU_4(ifp); 4621255332Scy } 4622145522Sdarrenr nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 4623255332Scy ipf_nat_tabmove(softn, nat); 462467614Sdarrenr break; 462567614Sdarrenr } 462667614Sdarrenr } 4627145522Sdarrenr 4628255332Scy MUTEX_DOWNGRADE(&softc->ipf_nat); 4629145522Sdarrenr 4630255332Scy if (nat == NULL) { 4631255332Scy NBUMPSIDE(1, ns_lookup_miss); 4632255332Scy } 463367614Sdarrenr return nat; 463453642Sguido} 463553642Sguido 463653642Sguido 4637145522Sdarrenr/* ------------------------------------------------------------------------ */ 4638255332Scy/* Function: ipf_nat_lookupredir */ 4639145522Sdarrenr/* Returns: nat_t* - NULL == no match, */ 4640145522Sdarrenr/* else pointer to matching NAT entry */ 4641145522Sdarrenr/* Parameters: np(I) - pointer to description of packet to find NAT table */ 4642145522Sdarrenr/* entry for. */ 4643145522Sdarrenr/* */ 4644145522Sdarrenr/* Lookup the NAT tables to search for a matching redirect */ 4645161356Sguido/* The contents of natlookup_t should imitate those found in a packet that */ 4646161356Sguido/* would be translated - ie a packet coming in for RDR or going out for MAP.*/ 4647161356Sguido/* We can do the lookup in one of two ways, imitating an inbound or */ 4648161356Sguido/* outbound packet. By default we assume outbound, unless IPN_IN is set. */ 4649161356Sguido/* For IN, the fields are set as follows: */ 4650161356Sguido/* nl_real* = source information */ 4651161356Sguido/* nl_out* = destination information (translated) */ 4652161356Sguido/* For an out packet, the fields are set like this: */ 4653161356Sguido/* nl_in* = source information (untranslated) */ 4654161356Sguido/* nl_out* = destination information (translated) */ 4655145522Sdarrenr/* ------------------------------------------------------------------------ */ 4656255332Scynat_t * 4657255332Scyipf_nat_lookupredir(np) 4658255332Scy natlookup_t *np; 465953642Sguido{ 4660145522Sdarrenr fr_info_t fi; 466153642Sguido nat_t *nat; 466253642Sguido 466392685Sdarrenr bzero((char *)&fi, sizeof(fi)); 4664145522Sdarrenr if (np->nl_flags & IPN_IN) { 4665145522Sdarrenr fi.fin_data[0] = ntohs(np->nl_realport); 4666145522Sdarrenr fi.fin_data[1] = ntohs(np->nl_outport); 4667145522Sdarrenr } else { 4668145522Sdarrenr fi.fin_data[0] = ntohs(np->nl_inport); 4669145522Sdarrenr fi.fin_data[1] = ntohs(np->nl_outport); 4670145522Sdarrenr } 4671145522Sdarrenr if (np->nl_flags & IPN_TCP) 4672145522Sdarrenr fi.fin_p = IPPROTO_TCP; 4673145522Sdarrenr else if (np->nl_flags & IPN_UDP) 4674145522Sdarrenr fi.fin_p = IPPROTO_UDP; 4675145522Sdarrenr else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) 4676145522Sdarrenr fi.fin_p = IPPROTO_ICMP; 467792685Sdarrenr 467853642Sguido /* 4679145522Sdarrenr * We can do two sorts of lookups: 4680145522Sdarrenr * - IPN_IN: we have the `real' and `out' address, look for `in'. 4681145522Sdarrenr * - default: we have the `in' and `out' address, look for `real'. 468253642Sguido */ 4683145522Sdarrenr if (np->nl_flags & IPN_IN) { 4684255332Scy if ((nat = ipf_nat_inlookup(&fi, np->nl_flags, fi.fin_p, 4685255332Scy np->nl_realip, np->nl_outip))) { 4686255332Scy np->nl_inip = nat->nat_odstip; 4687255332Scy np->nl_inport = nat->nat_odport; 4688145522Sdarrenr } 4689145522Sdarrenr } else { 4690145522Sdarrenr /* 4691145522Sdarrenr * If nl_inip is non null, this is a lookup based on the real 4692145522Sdarrenr * ip address. Else, we use the fake. 4693145522Sdarrenr */ 4694255332Scy if ((nat = ipf_nat_outlookup(&fi, np->nl_flags, fi.fin_p, 4695145522Sdarrenr np->nl_inip, np->nl_outip))) { 4696145522Sdarrenr 4697145522Sdarrenr if ((np->nl_flags & IPN_FINDFORWARD) != 0) { 4698145522Sdarrenr fr_info_t fin; 4699145522Sdarrenr bzero((char *)&fin, sizeof(fin)); 4700255332Scy fin.fin_p = nat->nat_pr[0]; 4701255332Scy fin.fin_data[0] = ntohs(nat->nat_ndport); 4702255332Scy fin.fin_data[1] = ntohs(nat->nat_nsport); 4703255332Scy if (ipf_nat_inlookup(&fin, np->nl_flags, 4704255332Scy fin.fin_p, nat->nat_ndstip, 4705255332Scy nat->nat_nsrcip) != NULL) { 4706145522Sdarrenr np->nl_flags &= ~IPN_FINDFORWARD; 4707145522Sdarrenr } 4708145522Sdarrenr } 4709145522Sdarrenr 4710255332Scy np->nl_realip = nat->nat_ndstip; 4711255332Scy np->nl_realport = nat->nat_ndport; 4712145522Sdarrenr } 4713145522Sdarrenr } 4714145522Sdarrenr 471553642Sguido return nat; 471653642Sguido} 471753642Sguido 471853642Sguido 4719145522Sdarrenr/* ------------------------------------------------------------------------ */ 4720255332Scy/* Function: ipf_nat_match */ 4721145522Sdarrenr/* Returns: int - 0 == no match, 1 == match */ 4722145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4723145522Sdarrenr/* np(I) - pointer to NAT rule */ 4724145522Sdarrenr/* */ 4725145522Sdarrenr/* Pull the matching of a packet against a NAT rule out of that complex */ 4726255332Scy/* loop inside ipf_nat_checkin() and lay it out properly in its own function. */ 4727145522Sdarrenr/* ------------------------------------------------------------------------ */ 4728255332Scystatic int 4729255332Scyipf_nat_match(fin, np) 4730255332Scy fr_info_t *fin; 4731255332Scy ipnat_t *np; 473260852Sdarrenr{ 4733255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 473460852Sdarrenr frtuc_t *ft; 4735255332Scy int match; 473660852Sdarrenr 4737255332Scy match = 0; 4738255332Scy switch (np->in_osrcatype) 4739255332Scy { 4740255332Scy case FRI_NORMAL : 4741255332Scy match = ((fin->fin_saddr & np->in_osrcmsk) != np->in_osrcaddr); 4742255332Scy break; 4743255332Scy case FRI_LOOKUP : 4744255332Scy match = (*np->in_osrcfunc)(softc, np->in_osrcptr, 4745255332Scy 4, &fin->fin_saddr, fin->fin_plen); 4746255332Scy break; 4747255332Scy } 4748255332Scy match ^= ((np->in_flags & IPN_NOTSRC) != 0); 4749255332Scy if (match) 475060852Sdarrenr return 0; 475160852Sdarrenr 4752255332Scy match = 0; 4753255332Scy switch (np->in_odstatype) 4754255332Scy { 4755255332Scy case FRI_NORMAL : 4756255332Scy match = ((fin->fin_daddr & np->in_odstmsk) != np->in_odstaddr); 4757255332Scy break; 4758255332Scy case FRI_LOOKUP : 4759255332Scy match = (*np->in_odstfunc)(softc, np->in_odstptr, 4760255332Scy 4, &fin->fin_daddr, fin->fin_plen); 4761255332Scy break; 4762255332Scy } 4763255332Scy 4764255332Scy match ^= ((np->in_flags & IPN_NOTDST) != 0); 4765255332Scy if (match) 476660852Sdarrenr return 0; 4767145522Sdarrenr 476860852Sdarrenr ft = &np->in_tuc; 4769145522Sdarrenr if (!(fin->fin_flx & FI_TCPUDP) || 4770145522Sdarrenr (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { 477160852Sdarrenr if (ft->ftu_scmp || ft->ftu_dcmp) 477260852Sdarrenr return 0; 477360852Sdarrenr return 1; 477460852Sdarrenr } 477560852Sdarrenr 4776255332Scy return ipf_tcpudpchk(&fin->fin_fi, ft); 477760852Sdarrenr} 477860852Sdarrenr 477960852Sdarrenr 4780145522Sdarrenr/* ------------------------------------------------------------------------ */ 4781255332Scy/* Function: ipf_nat_update */ 4782145522Sdarrenr/* Returns: Nil */ 4783255332Scy/* Parameters: fin(I) - pointer to packet information */ 4784255332Scy/* nat(I) - pointer to NAT structure */ 4785145522Sdarrenr/* */ 4786145522Sdarrenr/* Updates the lifetime of a NAT table entry for non-TCP packets. Must be */ 4787255332Scy/* called with fin_rev updated - i.e. after calling ipf_nat_proto(). */ 4788255332Scy/* */ 4789255332Scy/* This *MUST* be called after ipf_nat_proto() as it expects fin_rev to */ 4790255332Scy/* already be set. */ 4791145522Sdarrenr/* ------------------------------------------------------------------------ */ 4792255332Scyvoid 4793255332Scyipf_nat_update(fin, nat) 4794255332Scy fr_info_t *fin; 4795255332Scy nat_t *nat; 479653642Sguido{ 4797255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 4798255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 4799145522Sdarrenr ipftq_t *ifq, *ifq2; 4800145522Sdarrenr ipftqent_t *tqe; 4801255332Scy ipnat_t *np = nat->nat_ptr; 4802145522Sdarrenr 4803145522Sdarrenr tqe = &nat->nat_tqe; 4804145522Sdarrenr ifq = tqe->tqe_ifq; 4805145522Sdarrenr 4806145522Sdarrenr /* 4807145522Sdarrenr * We allow over-riding of NAT timeouts from NAT rules, even for 4808145522Sdarrenr * TCP, however, if it is TCP and there is no rule timeout set, 4809145522Sdarrenr * then do not update the timeout here. 4810145522Sdarrenr */ 4811255332Scy if (np != NULL) { 4812255332Scy np->in_bytes[fin->fin_rev] += fin->fin_plen; 4813145522Sdarrenr ifq2 = np->in_tqehead[fin->fin_rev]; 4814255332Scy } else { 4815145522Sdarrenr ifq2 = NULL; 4816255332Scy } 4817145522Sdarrenr 4818255332Scy if (nat->nat_pr[0] == IPPROTO_TCP && ifq2 == NULL) { 4819255332Scy (void) ipf_tcp_age(&nat->nat_tqe, fin, softn->ipf_nat_tcptq, 4820255332Scy 0, 2); 4821145522Sdarrenr } else { 4822145522Sdarrenr if (ifq2 == NULL) { 4823255332Scy if (nat->nat_pr[0] == IPPROTO_UDP) 4824255332Scy ifq2 = fin->fin_rev ? &softn->ipf_nat_udpacktq : 4825255332Scy &softn->ipf_nat_udptq; 4826255332Scy else if (nat->nat_pr[0] == IPPROTO_ICMP || 4827255332Scy nat->nat_pr[0] == IPPROTO_ICMPV6) 4828255332Scy ifq2 = fin->fin_rev ? &softn->ipf_nat_icmpacktq: 4829255332Scy &softn->ipf_nat_icmptq; 4830145522Sdarrenr else 4831255332Scy ifq2 = &softn->ipf_nat_iptq; 4832145522Sdarrenr } 4833145522Sdarrenr 4834255332Scy ipf_movequeue(softc->ipf_ticks, tqe, ifq, ifq2); 4835145522Sdarrenr } 4836145522Sdarrenr} 4837145522Sdarrenr 4838145522Sdarrenr 4839145522Sdarrenr/* ------------------------------------------------------------------------ */ 4840255332Scy/* Function: ipf_nat_checkout */ 4841145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 4842145522Sdarrenr/* 0 == no packet translation occurred, */ 4843145522Sdarrenr/* 1 == packet was successfully translated. */ 4844145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4845145522Sdarrenr/* passp(I) - pointer to filtering result flags */ 4846145522Sdarrenr/* */ 4847145522Sdarrenr/* Check to see if an outcoming packet should be changed. ICMP packets are */ 4848145522Sdarrenr/* first checked to see if they match an existing entry (if an error), */ 4849145522Sdarrenr/* otherwise a search of the current NAT table is made. If neither results */ 4850145522Sdarrenr/* in a match then a search for a matching NAT rule is made. Create a new */ 4851145522Sdarrenr/* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 4852145522Sdarrenr/* packet header(s) as required. */ 4853145522Sdarrenr/* ------------------------------------------------------------------------ */ 4854255332Scyint 4855255332Scyipf_nat_checkout(fin, passp) 4856255332Scy fr_info_t *fin; 4857255332Scy u_32_t *passp; 4858145522Sdarrenr{ 4859255332Scy ipnat_t *np = NULL, *npnext; 4860145522Sdarrenr struct ifnet *ifp, *sifp; 4861255332Scy ipf_main_softc_t *softc; 4862255332Scy ipf_nat_softc_t *softn; 4863145522Sdarrenr icmphdr_t *icmp = NULL; 486453642Sguido tcphdr_t *tcp = NULL; 4865145522Sdarrenr int rval, natfailed; 4866145522Sdarrenr u_int nflags = 0; 4867145522Sdarrenr u_32_t ipa, iph; 4868145522Sdarrenr int natadd = 1; 486953642Sguido frentry_t *fr; 487053642Sguido nat_t *nat; 487153642Sguido 4872255332Scy if (fin->fin_v == 6) { 4873255332Scy#ifdef USE_INET6 4874255332Scy return ipf_nat6_checkout(fin, passp); 4875255332Scy#else 487653642Sguido return 0; 4877255332Scy#endif 4878255332Scy } 487953642Sguido 4880255332Scy softc = fin->fin_main_soft; 4881255332Scy softn = softc->ipf_nat_soft; 4882255332Scy 4883255332Scy if (softn->ipf_nat_lock != 0) 4884255332Scy return 0; 4885255332Scy if (softn->ipf_nat_stats.ns_rules == 0 && 4886255332Scy softn->ipf_nat_instances == NULL) 4887255332Scy return 0; 4888255332Scy 4889145522Sdarrenr natfailed = 0; 4890145522Sdarrenr fr = fin->fin_fr; 4891145522Sdarrenr sifp = fin->fin_ifp; 4892170268Sdarrenr if (fr != NULL) { 4893255332Scy ifp = fr->fr_tifs[fin->fin_rev].fd_ptr; 4894170268Sdarrenr if ((ifp != NULL) && (ifp != (void *)-1)) 4895170268Sdarrenr fin->fin_ifp = ifp; 4896170268Sdarrenr } 489792685Sdarrenr ifp = fin->fin_ifp; 489853642Sguido 4899145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 4900145522Sdarrenr switch (fin->fin_p) 4901145522Sdarrenr { 4902145522Sdarrenr case IPPROTO_TCP : 490353642Sguido nflags = IPN_TCP; 4904145522Sdarrenr break; 4905145522Sdarrenr case IPPROTO_UDP : 490653642Sguido nflags = IPN_UDP; 4907145522Sdarrenr break; 4908145522Sdarrenr case IPPROTO_ICMP : 4909145522Sdarrenr icmp = fin->fin_dp; 4910145522Sdarrenr 4911145522Sdarrenr /* 4912145522Sdarrenr * This is an incoming packet, so the destination is 4913145522Sdarrenr * the icmp_id and the source port equals 0 4914145522Sdarrenr */ 4915255332Scy if ((fin->fin_flx & FI_ICMPQUERY) != 0) 4916145522Sdarrenr nflags = IPN_ICMPQUERY; 4917145522Sdarrenr break; 4918145522Sdarrenr default : 4919145522Sdarrenr break; 492053642Sguido } 4921255332Scy 4922145522Sdarrenr if ((nflags & IPN_TCPUDP)) 4923145522Sdarrenr tcp = fin->fin_dp; 492453642Sguido } 492553642Sguido 492692685Sdarrenr ipa = fin->fin_saddr; 492753642Sguido 4928255332Scy READ_ENTER(&softc->ipf_nat); 492960852Sdarrenr 4930255332Scy if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && 4931255332Scy (nat = ipf_nat_icmperror(fin, &nflags, NAT_OUTBOUND))) 4932145522Sdarrenr /*EMPTY*/; 4933255332Scy else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) 493453642Sguido natadd = 0; 4935255332Scy else if ((nat = ipf_nat_outlookup(fin, nflags|NAT_SEARCH, 4936255332Scy (u_int)fin->fin_p, fin->fin_src, 4937255332Scy fin->fin_dst))) { 493853642Sguido nflags = nat->nat_flags; 4939255332Scy } else if (fin->fin_off == 0) { 4940255332Scy u_32_t hv, msk, nmsk = 0; 494192685Sdarrenr 494253642Sguido /* 494353642Sguido * If there is no current entry in the nat table for this IP#, 494453642Sguido * create one for it (if there is a matching rule). 494553642Sguido */ 494653642Sguidomaskloop: 4947255332Scy msk = softn->ipf_nat_map_active_masks[nmsk]; 4948255332Scy iph = ipa & msk; 4949255332Scy hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_maprules_sz); 4950255332Scyretry_roundrobin: 4951255332Scy for (np = softn->ipf_nat_map_rules[hv]; np; np = npnext) { 4952255332Scy npnext = np->in_mnext; 4953161356Sguido if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) 495460852Sdarrenr continue; 4955255332Scy if (np->in_v[0] != 4) 495660852Sdarrenr continue; 4957255332Scy if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p)) 4958145522Sdarrenr continue; 4959255332Scy if ((np->in_flags & IPN_RF) && 4960255332Scy !(np->in_flags & nflags)) 4961145522Sdarrenr continue; 496260852Sdarrenr if (np->in_flags & IPN_FILTER) { 4963255332Scy switch (ipf_nat_match(fin, np)) 4964255332Scy { 4965255332Scy case 0 : 496660852Sdarrenr continue; 4967255332Scy case -1 : 4968255332Scy rval = -1; 4969255332Scy goto outmatchfail; 4970255332Scy case 1 : 4971255332Scy default : 4972255332Scy break; 4973255332Scy } 4974255332Scy } else if ((ipa & np->in_osrcmsk) != np->in_osrcaddr) 497560852Sdarrenr continue; 4976145522Sdarrenr 4977145522Sdarrenr if ((fr != NULL) && 4978255332Scy !ipf_matchtag(&np->in_tag, &fr->fr_nattag)) 497992685Sdarrenr continue; 4980145522Sdarrenr 4981255332Scy if (np->in_plabel != -1) { 4982145522Sdarrenr if (((np->in_flags & IPN_FILTER) == 0) && 4983255332Scy (np->in_odport != fin->fin_data[1])) 4984145522Sdarrenr continue; 4985255332Scy if (ipf_proxy_ok(fin, tcp, np) == 0) 4986145522Sdarrenr continue; 4987145522Sdarrenr } 4988145522Sdarrenr 4989255332Scy if (np->in_flags & IPN_NO) { 499092685Sdarrenr np->in_hits++; 499192685Sdarrenr break; 4992145522Sdarrenr } 4993255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 4994255332Scy /* 4995255332Scy * If we've matched a round-robin rule but it has 4996255332Scy * moved in the list since we got it, start over as 4997255332Scy * this is now no longer correct. 4998255332Scy */ 4999255332Scy if (npnext != np->in_mnext) { 5000255332Scy if ((np->in_flags & IPN_ROUNDR) != 0) { 5001255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 5002255332Scy goto retry_roundrobin; 5003255332Scy } 5004255332Scy npnext = np->in_mnext; 5005145522Sdarrenr } 5006255332Scy 5007255332Scy nat = ipf_nat_add(fin, np, NULL, nflags, NAT_OUTBOUND); 5008255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 5009255332Scy if (nat != NULL) { 5010255332Scy natfailed = 0; 5011255332Scy break; 5012255332Scy } 5013255332Scy natfailed = -1; 501453642Sguido } 5015255332Scy if ((np == NULL) && (nmsk < softn->ipf_nat_map_max)) { 5016255332Scy nmsk++; 5017255332Scy goto maskloop; 5018255332Scy } 501953642Sguido } 502053642Sguido 5021145522Sdarrenr if (nat != NULL) { 5022255332Scy rval = ipf_nat_out(fin, nat, natadd, nflags); 5023145522Sdarrenr if (rval == 1) { 5024145522Sdarrenr MUTEX_ENTER(&nat->nat_lock); 5025255332Scy ipf_nat_update(fin, nat); 5026255332Scy nat->nat_bytes[1] += fin->fin_plen; 5027255332Scy nat->nat_pkts[1]++; 5028255332Scy fin->fin_pktnum = nat->nat_pkts[1]; 5029145522Sdarrenr MUTEX_EXIT(&nat->nat_lock); 5030145522Sdarrenr } 5031145522Sdarrenr } else 5032145522Sdarrenr rval = natfailed; 5033255332Scyoutmatchfail: 5034255332Scy RWLOCK_EXIT(&softc->ipf_nat); 5035145522Sdarrenr 5036255332Scy switch (rval) 5037255332Scy { 5038255332Scy case -1 : 5039255332Scy if (passp != NULL) { 5040255332Scy DT1(frb_natv4out, fr_info_t *, fin); 5041255332Scy NBUMPSIDED(1, ns_drop); 5042145522Sdarrenr *passp = FR_BLOCK; 5043255332Scy fin->fin_reason = FRB_NATV4; 5044255332Scy } 5045145522Sdarrenr fin->fin_flx |= FI_BADNAT; 5046255332Scy NBUMPSIDED(1, ns_badnat); 5047255332Scy break; 5048255332Scy case 0 : 5049255332Scy NBUMPSIDE(1, ns_ignored); 5050255332Scy break; 5051255332Scy case 1 : 5052255332Scy NBUMPSIDE(1, ns_translated); 5053255332Scy break; 5054145522Sdarrenr } 5055145522Sdarrenr fin->fin_ifp = sifp; 5056145522Sdarrenr return rval; 5057145522Sdarrenr} 5058145522Sdarrenr 5059145522Sdarrenr/* ------------------------------------------------------------------------ */ 5060255332Scy/* Function: ipf_nat_out */ 5061145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 5062145522Sdarrenr/* 1 == packet was successfully translated. */ 5063145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 5064145522Sdarrenr/* nat(I) - pointer to NAT structure */ 5065145522Sdarrenr/* natadd(I) - flag indicating if it is safe to add frag cache */ 5066145522Sdarrenr/* nflags(I) - NAT flags set for this packet */ 5067145522Sdarrenr/* */ 5068145522Sdarrenr/* Translate a packet coming "out" on an interface. */ 5069145522Sdarrenr/* ------------------------------------------------------------------------ */ 5070255332Scyint 5071255332Scyipf_nat_out(fin, nat, natadd, nflags) 5072255332Scy fr_info_t *fin; 5073255332Scy nat_t *nat; 5074255332Scy int natadd; 5075255332Scy u_32_t nflags; 5076145522Sdarrenr{ 5077255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 5078255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 5079145522Sdarrenr icmphdr_t *icmp; 5080145522Sdarrenr tcphdr_t *tcp; 5081145522Sdarrenr ipnat_t *np; 5082255332Scy int skip; 5083145522Sdarrenr int i; 5084145522Sdarrenr 5085145522Sdarrenr tcp = NULL; 5086145522Sdarrenr icmp = NULL; 5087145522Sdarrenr np = nat->nat_ptr; 5088145522Sdarrenr 5089145522Sdarrenr if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL)) 5090255332Scy (void) ipf_frag_natnew(softc, fin, 0, nat); 5091145522Sdarrenr 509272006Sdarrenr /* 5093145522Sdarrenr * Fix up checksums, not by recalculating them, but 5094145522Sdarrenr * simply computing adjustments. 5095145522Sdarrenr * This is only done for STREAMS based IP implementations where the 5096145522Sdarrenr * checksum has already been calculated by IP. In all other cases, 5097145522Sdarrenr * IPFilter is called before the checksum needs calculating so there 5098145522Sdarrenr * is no call to modify whatever is in the header now. 509972006Sdarrenr */ 5100255332Scy if (nflags == IPN_ICMPERR) { 5101255332Scy u_32_t s1, s2, sumd, msumd; 510263523Sdarrenr 5103255332Scy s1 = LONG_SUM(ntohl(fin->fin_saddr)); 5104255332Scy if (nat->nat_dir == NAT_OUTBOUND) { 5105255332Scy s2 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); 5106255332Scy } else { 5107255332Scy s2 = LONG_SUM(ntohl(nat->nat_odstaddr)); 510863523Sdarrenr } 5109255332Scy CALC_SUMD(s1, s2, sumd); 5110255332Scy msumd = sumd; 5111255332Scy 5112255332Scy s1 = LONG_SUM(ntohl(fin->fin_daddr)); 5113255332Scy if (nat->nat_dir == NAT_OUTBOUND) { 5114255332Scy s2 = LONG_SUM(ntohl(nat->nat_ndstaddr)); 5115255332Scy } else { 5116255332Scy s2 = LONG_SUM(ntohl(nat->nat_osrcaddr)); 5117255332Scy } 5118255332Scy CALC_SUMD(s1, s2, sumd); 5119255332Scy msumd += sumd; 5120255332Scy 5121255332Scy ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, msumd, 0); 5122255332Scy } 5123153876Sguido#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 5124153876Sguido defined(linux) || defined(BRIDGE_IPF) 5125255332Scy else { 5126255332Scy /* 5127255332Scy * Strictly speaking, this isn't necessary on BSD 5128255332Scy * kernels because they do checksum calculation after 5129255332Scy * this code has run BUT if ipfilter is being used 5130255332Scy * to do NAT as a bridge, that code doesn't exist. 5131255332Scy */ 5132255332Scy switch (nat->nat_dir) 5133255332Scy { 5134255332Scy case NAT_OUTBOUND : 5135255332Scy ipf_fix_outcksum(fin->fin_cksum & FI_CK_L4PART, 5136255332Scy &fin->fin_ip->ip_sum, 5137255332Scy nat->nat_ipsumd, 0); 5138255332Scy break; 5139255332Scy 5140255332Scy case NAT_INBOUND : 5141255332Scy ipf_fix_incksum(fin->fin_cksum & FI_CK_L4PART, 5142255332Scy &fin->fin_ip->ip_sum, 5143255332Scy nat->nat_ipsumd, 0); 5144255332Scy break; 5145255332Scy 5146255332Scy default : 5147255332Scy break; 514863523Sdarrenr } 5149255332Scy } 515053642Sguido#endif 5151255332Scy 5152255332Scy /* 5153255332Scy * Address assignment is after the checksum modification because 5154255332Scy * we are using the address in the packet for determining the 5155255332Scy * correct checksum offset (the ICMP error could be coming from 5156255332Scy * anyone...) 5157255332Scy */ 5158255332Scy switch (nat->nat_dir) 5159255332Scy { 5160255332Scy case NAT_OUTBOUND : 5161255332Scy fin->fin_ip->ip_src = nat->nat_nsrcip; 5162255332Scy fin->fin_saddr = nat->nat_nsrcaddr; 5163255332Scy fin->fin_ip->ip_dst = nat->nat_ndstip; 5164255332Scy fin->fin_daddr = nat->nat_ndstaddr; 5165255332Scy break; 5166255332Scy 5167255332Scy case NAT_INBOUND : 5168255332Scy fin->fin_ip->ip_src = nat->nat_odstip; 5169255332Scy fin->fin_saddr = nat->nat_ndstaddr; 5170255332Scy fin->fin_ip->ip_dst = nat->nat_osrcip; 5171255332Scy fin->fin_daddr = nat->nat_nsrcaddr; 5172255332Scy break; 5173255332Scy 5174255332Scy case NAT_DIVERTIN : 5175255332Scy { 5176255332Scy mb_t *m; 5177255332Scy 5178255332Scy skip = ipf_nat_decap(fin, nat); 5179255332Scy if (skip <= 0) { 5180255332Scy NBUMPSIDED(1, ns_decap_fail); 5181255332Scy return -1; 5182255332Scy } 5183255332Scy 5184255332Scy m = fin->fin_m; 5185255332Scy 5186255332Scy#if defined(MENTAT) && defined(_KERNEL) 5187255332Scy m->b_rptr += skip; 5188255332Scy#else 5189255332Scy m->m_data += skip; 5190255332Scy m->m_len -= skip; 5191255332Scy 5192255332Scy# ifdef M_PKTHDR 5193255332Scy if (m->m_flags & M_PKTHDR) 5194255332Scy m->m_pkthdr.len -= skip; 5195255332Scy# endif 5196255332Scy#endif 5197255332Scy 5198255332Scy MUTEX_ENTER(&nat->nat_lock); 5199255332Scy ipf_nat_update(fin, nat); 5200255332Scy MUTEX_EXIT(&nat->nat_lock); 5201255332Scy fin->fin_flx |= FI_NATED; 5202255332Scy if (np != NULL && np->in_tag.ipt_num[0] != 0) 5203255332Scy fin->fin_nattag = &np->in_tag; 5204255332Scy return 1; 5205255332Scy /* NOTREACHED */ 5206255332Scy } 5207255332Scy 5208255332Scy case NAT_DIVERTOUT : 5209255332Scy { 5210255332Scy u_32_t s1, s2, sumd; 5211255332Scy udphdr_t *uh; 5212255332Scy ip_t *ip; 5213255332Scy mb_t *m; 5214255332Scy 5215255332Scy m = M_DUP(np->in_divmp); 5216255332Scy if (m == NULL) { 5217255332Scy NBUMPSIDED(1, ns_divert_dup); 5218255332Scy return -1; 5219255332Scy } 5220255332Scy 5221255332Scy ip = MTOD(m, ip_t *); 5222255332Scy ip->ip_id = htons(ipf_nextipid(fin)); 5223255332Scy s2 = ntohs(ip->ip_id); 5224255332Scy 5225255332Scy s1 = ip->ip_len; 5226255332Scy ip->ip_len = ntohs(ip->ip_len); 5227255332Scy ip->ip_len += fin->fin_plen; 5228255332Scy ip->ip_len = htons(ip->ip_len); 5229255332Scy s2 += ntohs(ip->ip_len); 5230255332Scy CALC_SUMD(s1, s2, sumd); 5231255332Scy 5232255332Scy uh = (udphdr_t *)(ip + 1); 5233255332Scy uh->uh_ulen += fin->fin_plen; 5234255332Scy uh->uh_ulen = htons(uh->uh_ulen); 5235255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 5236255332Scy defined(linux) || defined(BRIDGE_IPF) 5237255332Scy ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0); 5238255332Scy#endif 5239255332Scy 5240255332Scy PREP_MB_T(fin, m); 5241255332Scy 5242255332Scy fin->fin_src = ip->ip_src; 5243255332Scy fin->fin_dst = ip->ip_dst; 5244255332Scy fin->fin_ip = ip; 5245255332Scy fin->fin_plen += sizeof(ip_t) + 8; /* UDP + IPv4 hdr */ 5246255332Scy fin->fin_dlen += sizeof(ip_t) + 8; /* UDP + IPv4 hdr */ 5247255332Scy 5248255332Scy nflags &= ~IPN_TCPUDPICMP; 5249255332Scy 5250255332Scy break; 5251255332Scy } 5252255332Scy 5253255332Scy default : 5254255332Scy break; 5255145522Sdarrenr } 525653642Sguido 5257145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 5258255332Scy u_short *csump; 5259255332Scy 5260255332Scy if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) { 5261145522Sdarrenr tcp = fin->fin_dp; 526253642Sguido 5263255332Scy switch (nat->nat_dir) 5264255332Scy { 5265255332Scy case NAT_OUTBOUND : 5266255332Scy tcp->th_sport = nat->nat_nsport; 5267255332Scy fin->fin_data[0] = ntohs(nat->nat_nsport); 5268255332Scy tcp->th_dport = nat->nat_ndport; 5269255332Scy fin->fin_data[1] = ntohs(nat->nat_ndport); 5270255332Scy break; 5271255332Scy 5272255332Scy case NAT_INBOUND : 5273255332Scy tcp->th_sport = nat->nat_odport; 5274255332Scy fin->fin_data[0] = ntohs(nat->nat_odport); 5275255332Scy tcp->th_dport = nat->nat_osport; 5276255332Scy fin->fin_data[1] = ntohs(nat->nat_osport); 5277255332Scy break; 5278255332Scy } 5279145522Sdarrenr } 528053642Sguido 5281255332Scy if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) { 5282145522Sdarrenr icmp = fin->fin_dp; 5283255332Scy icmp->icmp_id = nat->nat_nicmpid; 5284145522Sdarrenr } 5285110916Sdarrenr 5286255332Scy csump = ipf_nat_proto(fin, nat, nflags); 5287255332Scy 5288255332Scy /* 5289255332Scy * The above comments do not hold for layer 4 (or higher) 5290255332Scy * checksums... 5291255332Scy */ 5292255332Scy if (csump != NULL) { 5293255332Scy if (nat->nat_dir == NAT_OUTBOUND) 5294255332Scy ipf_fix_outcksum(fin->fin_cksum, csump, 5295255332Scy nat->nat_sumd[0], 5296255332Scy nat->nat_sumd[1] + 5297255332Scy fin->fin_dlen); 5298255332Scy else 5299255332Scy ipf_fix_incksum(fin->fin_cksum, csump, 5300255332Scy nat->nat_sumd[0], 5301255332Scy nat->nat_sumd[1] + 5302255332Scy fin->fin_dlen); 5303255332Scy } 5304145522Sdarrenr } 5305110916Sdarrenr 5306255332Scy ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); 5307145522Sdarrenr /* ------------------------------------------------------------- */ 5308255332Scy /* A few quick notes: */ 5309255332Scy /* Following are test conditions prior to calling the */ 5310255332Scy /* ipf_proxy_check routine. */ 5311255332Scy /* */ 5312255332Scy /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 5313255332Scy /* with a redirect rule, we attempt to match the packet's */ 5314255332Scy /* source port against in_dport, otherwise we'd compare the */ 5315255332Scy /* packet's destination. */ 5316145522Sdarrenr /* ------------------------------------------------------------- */ 5317145522Sdarrenr if ((np != NULL) && (np->in_apr != NULL)) { 5318255332Scy i = ipf_proxy_check(fin, nat); 5319255332Scy if (i == 0) { 532060852Sdarrenr i = 1; 5321255332Scy } else if (i == -1) { 5322255332Scy NBUMPSIDED(1, ns_ipf_proxy_fail); 5323255332Scy } 5324255332Scy } else { 5325145522Sdarrenr i = 1; 5326255332Scy } 5327145522Sdarrenr fin->fin_flx |= FI_NATED; 5328145522Sdarrenr return i; 532953642Sguido} 533053642Sguido 533153642Sguido 5332145522Sdarrenr/* ------------------------------------------------------------------------ */ 5333255332Scy/* Function: ipf_nat_checkin */ 5334145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 5335145522Sdarrenr/* 0 == no packet translation occurred, */ 5336145522Sdarrenr/* 1 == packet was successfully translated. */ 5337145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 5338145522Sdarrenr/* passp(I) - pointer to filtering result flags */ 5339145522Sdarrenr/* */ 5340145522Sdarrenr/* Check to see if an incoming packet should be changed. ICMP packets are */ 5341145522Sdarrenr/* first checked to see if they match an existing entry (if an error), */ 5342145522Sdarrenr/* otherwise a search of the current NAT table is made. If neither results */ 5343145522Sdarrenr/* in a match then a search for a matching NAT rule is made. Create a new */ 5344145522Sdarrenr/* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 5345145522Sdarrenr/* packet header(s) as required. */ 5346145522Sdarrenr/* ------------------------------------------------------------------------ */ 5347255332Scyint 5348255332Scyipf_nat_checkin(fin, passp) 5349255332Scy fr_info_t *fin; 5350255332Scy u_32_t *passp; 535153642Sguido{ 5352255332Scy ipf_main_softc_t *softc; 5353255332Scy ipf_nat_softc_t *softn; 5354145522Sdarrenr u_int nflags, natadd; 5355255332Scy ipnat_t *np, *npnext; 5356145522Sdarrenr int rval, natfailed; 5357145522Sdarrenr struct ifnet *ifp; 5358145522Sdarrenr struct in_addr in; 5359145522Sdarrenr icmphdr_t *icmp; 5360145522Sdarrenr tcphdr_t *tcp; 5361145522Sdarrenr u_short dport; 536253642Sguido nat_t *nat; 536353642Sguido u_32_t iph; 536453642Sguido 5365255332Scy softc = fin->fin_main_soft; 5366255332Scy softn = softc->ipf_nat_soft; 5367255332Scy 5368255332Scy if (softn->ipf_nat_lock != 0) 536953642Sguido return 0; 5370255332Scy if (softn->ipf_nat_stats.ns_rules == 0 && 5371255332Scy softn->ipf_nat_instances == NULL) 5372255332Scy return 0; 537353642Sguido 5374145522Sdarrenr tcp = NULL; 5375145522Sdarrenr icmp = NULL; 5376145522Sdarrenr dport = 0; 5377145522Sdarrenr natadd = 1; 5378145522Sdarrenr nflags = 0; 5379145522Sdarrenr natfailed = 0; 5380145522Sdarrenr ifp = fin->fin_ifp; 5381145522Sdarrenr 5382145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 5383145522Sdarrenr switch (fin->fin_p) 5384145522Sdarrenr { 5385145522Sdarrenr case IPPROTO_TCP : 538653642Sguido nflags = IPN_TCP; 5387145522Sdarrenr break; 5388145522Sdarrenr case IPPROTO_UDP : 538953642Sguido nflags = IPN_UDP; 5390145522Sdarrenr break; 5391145522Sdarrenr case IPPROTO_ICMP : 5392145522Sdarrenr icmp = fin->fin_dp; 5393145522Sdarrenr 5394145522Sdarrenr /* 5395145522Sdarrenr * This is an incoming packet, so the destination is 5396145522Sdarrenr * the icmp_id and the source port equals 0 5397145522Sdarrenr */ 5398255332Scy if ((fin->fin_flx & FI_ICMPQUERY) != 0) { 5399145522Sdarrenr nflags = IPN_ICMPQUERY; 5400255332Scy dport = icmp->icmp_id; 5401145522Sdarrenr } break; 5402145522Sdarrenr default : 5403145522Sdarrenr break; 5404145522Sdarrenr } 5405255332Scy 540653642Sguido if ((nflags & IPN_TCPUDP)) { 5407145522Sdarrenr tcp = fin->fin_dp; 5408255332Scy dport = fin->fin_data[1]; 540953642Sguido } 541053642Sguido } 541153642Sguido 541292685Sdarrenr in = fin->fin_dst; 541353642Sguido 5414255332Scy READ_ENTER(&softc->ipf_nat); 541553642Sguido 5416255332Scy if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && 5417255332Scy (nat = ipf_nat_icmperror(fin, &nflags, NAT_INBOUND))) 5418145522Sdarrenr /*EMPTY*/; 5419255332Scy else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) 542053642Sguido natadd = 0; 5421255332Scy else if ((nat = ipf_nat_inlookup(fin, nflags|NAT_SEARCH, 5422255332Scy (u_int)fin->fin_p, 5423255332Scy fin->fin_src, in))) { 542453642Sguido nflags = nat->nat_flags; 5425255332Scy } else if (fin->fin_off == 0) { 5426255332Scy u_32_t hv, msk, rmsk = 0; 5427145522Sdarrenr 542853642Sguido /* 542953642Sguido * If there is no current entry in the nat table for this IP#, 543053642Sguido * create one for it (if there is a matching rule). 543153642Sguido */ 543253642Sguidomaskloop: 5433255332Scy msk = softn->ipf_nat_rdr_active_masks[rmsk]; 5434255332Scy iph = in.s_addr & msk; 5435255332Scy hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_rdrrules_sz); 5436255332Scyretry_roundrobin: 5437255332Scy /* TRACE (iph,msk,rmsk,hv,softn->ipf_nat_rdrrules_sz) */ 5438255332Scy for (np = softn->ipf_nat_rdr_rules[hv]; np; np = npnext) { 5439255332Scy npnext = np->in_rnext; 5440145522Sdarrenr if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) 544160852Sdarrenr continue; 5442255332Scy if (np->in_v[0] != 4) 5443138947Sdarrenr continue; 5444255332Scy if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p)) 5445145522Sdarrenr continue; 5446145522Sdarrenr if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) 5447145522Sdarrenr continue; 544860852Sdarrenr if (np->in_flags & IPN_FILTER) { 5449255332Scy switch (ipf_nat_match(fin, np)) 5450255332Scy { 5451255332Scy case 0 : 545260852Sdarrenr continue; 5453255332Scy case -1 : 5454255332Scy rval = -1; 5455255332Scy goto inmatchfail; 5456255332Scy case 1 : 5457255332Scy default : 5458255332Scy break; 5459255332Scy } 5460145522Sdarrenr } else { 5461255332Scy if ((in.s_addr & np->in_odstmsk) != 5462255332Scy np->in_odstaddr) 5463145522Sdarrenr continue; 5464255332Scy if (np->in_odport && 5465255332Scy ((np->in_dtop < dport) || 5466255332Scy (dport < np->in_odport))) 5467145522Sdarrenr continue; 5468145522Sdarrenr } 5469145522Sdarrenr 5470255332Scy if (np->in_plabel != -1) { 5471255332Scy if (!ipf_proxy_ok(fin, tcp, np)) { 5472145522Sdarrenr continue; 547353642Sguido } 5474145522Sdarrenr } 5475145522Sdarrenr 5476255332Scy if (np->in_flags & IPN_NO) { 5477145522Sdarrenr np->in_hits++; 5478145522Sdarrenr break; 5479255332Scy } 548060852Sdarrenr 5481255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 5482255332Scy /* 5483255332Scy * If we've matched a round-robin rule but it has 5484255332Scy * moved in the list since we got it, start over as 5485255332Scy * this is now no longer correct. 5486255332Scy */ 5487255332Scy if (npnext != np->in_rnext) { 5488255332Scy if ((np->in_flags & IPN_ROUNDR) != 0) { 5489255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 5490255332Scy goto retry_roundrobin; 5491255332Scy } 5492255332Scy npnext = np->in_rnext; 5493145522Sdarrenr } 5494255332Scy 5495255332Scy nat = ipf_nat_add(fin, np, NULL, nflags, NAT_INBOUND); 5496255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 5497255332Scy if (nat != NULL) { 5498255332Scy natfailed = 0; 5499255332Scy break; 5500145522Sdarrenr } 5501255332Scy natfailed = -1; 550253642Sguido } 5503255332Scy if ((np == NULL) && (rmsk < softn->ipf_nat_rdr_max)) { 5504255332Scy rmsk++; 5505255332Scy goto maskloop; 5506255332Scy } 550753642Sguido } 5508255332Scy 5509145522Sdarrenr if (nat != NULL) { 5510255332Scy rval = ipf_nat_in(fin, nat, natadd, nflags); 5511145522Sdarrenr if (rval == 1) { 5512145522Sdarrenr MUTEX_ENTER(&nat->nat_lock); 5513255332Scy ipf_nat_update(fin, nat); 5514255332Scy nat->nat_bytes[0] += fin->fin_plen; 5515255332Scy nat->nat_pkts[0]++; 5516255332Scy fin->fin_pktnum = nat->nat_pkts[0]; 5517145522Sdarrenr MUTEX_EXIT(&nat->nat_lock); 5518145522Sdarrenr } 5519145522Sdarrenr } else 5520145522Sdarrenr rval = natfailed; 5521255332Scyinmatchfail: 5522255332Scy RWLOCK_EXIT(&softc->ipf_nat); 552372006Sdarrenr 5524255332Scy switch (rval) 5525255332Scy { 5526255332Scy case -1 : 5527255332Scy if (passp != NULL) { 5528255332Scy DT1(frb_natv4in, fr_info_t *, fin); 5529255332Scy NBUMPSIDED(0, ns_drop); 5530145522Sdarrenr *passp = FR_BLOCK; 5531255332Scy fin->fin_reason = FRB_NATV4; 5532255332Scy } 5533145522Sdarrenr fin->fin_flx |= FI_BADNAT; 5534255332Scy NBUMPSIDED(0, ns_badnat); 5535255332Scy break; 5536255332Scy case 0 : 5537255332Scy NBUMPSIDE(0, ns_ignored); 5538255332Scy break; 5539255332Scy case 1 : 5540255332Scy NBUMPSIDE(0, ns_translated); 5541255332Scy break; 5542145522Sdarrenr } 5543145522Sdarrenr return rval; 5544145522Sdarrenr} 5545145522Sdarrenr 5546145522Sdarrenr 5547145522Sdarrenr/* ------------------------------------------------------------------------ */ 5548255332Scy/* Function: ipf_nat_in */ 5549145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 5550145522Sdarrenr/* 1 == packet was successfully translated. */ 5551145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 5552145522Sdarrenr/* nat(I) - pointer to NAT structure */ 5553145522Sdarrenr/* natadd(I) - flag indicating if it is safe to add frag cache */ 5554145522Sdarrenr/* nflags(I) - NAT flags set for this packet */ 5555255332Scy/* Locks Held: ipf_nat(READ) */ 5556145522Sdarrenr/* */ 5557145522Sdarrenr/* Translate a packet coming "in" on an interface. */ 5558145522Sdarrenr/* ------------------------------------------------------------------------ */ 5559255332Scyint 5560255332Scyipf_nat_in(fin, nat, natadd, nflags) 5561255332Scy fr_info_t *fin; 5562255332Scy nat_t *nat; 5563255332Scy int natadd; 5564255332Scy u_32_t nflags; 5565145522Sdarrenr{ 5566255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 5567255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 5568255332Scy u_32_t sumd, ipsumd, sum1, sum2; 5569145522Sdarrenr icmphdr_t *icmp; 5570145522Sdarrenr tcphdr_t *tcp; 5571145522Sdarrenr ipnat_t *np; 5572255332Scy int skip; 5573145522Sdarrenr int i; 5574145522Sdarrenr 5575145522Sdarrenr tcp = NULL; 5576145522Sdarrenr np = nat->nat_ptr; 5577145522Sdarrenr fin->fin_fr = nat->nat_fr; 5578145522Sdarrenr 5579145522Sdarrenr if (np != NULL) { 5580145522Sdarrenr if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) 5581255332Scy (void) ipf_frag_natnew(softc, fin, 0, nat); 5582145522Sdarrenr 5583145522Sdarrenr /* ------------------------------------------------------------- */ 5584255332Scy /* A few quick notes: */ 5585255332Scy /* Following are test conditions prior to calling the */ 5586255332Scy /* ipf_proxy_check routine. */ 5587255332Scy /* */ 5588255332Scy /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 5589255332Scy /* with a map rule, we attempt to match the packet's */ 5590255332Scy /* source port against in_dport, otherwise we'd compare the */ 5591255332Scy /* packet's destination. */ 5592145522Sdarrenr /* ------------------------------------------------------------- */ 5593145522Sdarrenr if (np->in_apr != NULL) { 5594255332Scy i = ipf_proxy_check(fin, nat); 559560852Sdarrenr if (i == -1) { 5596255332Scy NBUMPSIDED(0, ns_ipf_proxy_fail); 5597145522Sdarrenr return -1; 559860852Sdarrenr } 559960852Sdarrenr } 5600145522Sdarrenr } 560153642Sguido 5602255332Scy ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); 5603145522Sdarrenr 5604255332Scy ipsumd = nat->nat_ipsumd; 5605145522Sdarrenr /* 5606145522Sdarrenr * Fix up checksums, not by recalculating them, but 5607145522Sdarrenr * simply computing adjustments. 5608145522Sdarrenr * Why only do this for some platforms on inbound packets ? 5609145522Sdarrenr * Because for those that it is done, IP processing is yet to happen 5610145522Sdarrenr * and so the IPv4 header checksum has not yet been evaluated. 5611145522Sdarrenr * Perhaps it should always be done for the benefit of things like 5612145522Sdarrenr * fast forwarding (so that it doesn't need to be recomputed) but with 5613145522Sdarrenr * header checksum offloading, perhaps it is a moot point. 5614145522Sdarrenr */ 5615255332Scy 5616255332Scy switch (nat->nat_dir) 5617255332Scy { 5618255332Scy case NAT_INBOUND : 5619255332Scy if ((fin->fin_flx & FI_ICMPERR) == 0) { 5620255332Scy fin->fin_ip->ip_src = nat->nat_nsrcip; 5621255332Scy fin->fin_saddr = nat->nat_nsrcaddr; 5622255332Scy } else { 5623255332Scy sum1 = nat->nat_osrcaddr; 5624255332Scy sum2 = nat->nat_nsrcaddr; 5625255332Scy CALC_SUMD(sum1, sum2, sumd); 5626255332Scy ipsumd -= sumd; 5627255332Scy } 5628255332Scy fin->fin_ip->ip_dst = nat->nat_ndstip; 5629255332Scy fin->fin_daddr = nat->nat_ndstaddr; 5630145522Sdarrenr#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 5631145522Sdarrenr defined(__osf__) || defined(linux) 5632255332Scy ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, ipsumd, 0); 5633145522Sdarrenr#endif 5634255332Scy break; 5635145522Sdarrenr 5636255332Scy case NAT_OUTBOUND : 5637255332Scy if ((fin->fin_flx & FI_ICMPERR) == 0) { 5638255332Scy fin->fin_ip->ip_src = nat->nat_odstip; 5639255332Scy fin->fin_saddr = nat->nat_odstaddr; 5640255332Scy } else { 5641255332Scy sum1 = nat->nat_odstaddr; 5642255332Scy sum2 = nat->nat_ndstaddr; 5643255332Scy CALC_SUMD(sum1, sum2, sumd); 5644255332Scy ipsumd -= sumd; 5645255332Scy } 5646255332Scy fin->fin_ip->ip_dst = nat->nat_osrcip; 5647255332Scy fin->fin_daddr = nat->nat_osrcaddr; 5648255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 5649255332Scy defined(__osf__) || defined(linux) 5650255332Scy ipf_fix_incksum(0, &fin->fin_ip->ip_sum, ipsumd, 0); 5651255332Scy#endif 5652255332Scy break; 5653255332Scy 5654255332Scy case NAT_DIVERTIN : 5655255332Scy { 5656255332Scy udphdr_t *uh; 5657255332Scy ip_t *ip; 5658255332Scy mb_t *m; 5659255332Scy 5660255332Scy m = M_DUP(np->in_divmp); 5661255332Scy if (m == NULL) { 5662255332Scy NBUMPSIDED(0, ns_divert_dup); 5663255332Scy return -1; 5664255332Scy } 5665255332Scy 5666255332Scy ip = MTOD(m, ip_t *); 5667255332Scy ip->ip_id = htons(ipf_nextipid(fin)); 5668255332Scy sum1 = ntohs(ip->ip_len); 5669255332Scy ip->ip_len = ntohs(ip->ip_len); 5670255332Scy ip->ip_len += fin->fin_plen; 5671255332Scy ip->ip_len = htons(ip->ip_len); 5672255332Scy 5673255332Scy uh = (udphdr_t *)(ip + 1); 5674255332Scy uh->uh_ulen += fin->fin_plen; 5675255332Scy uh->uh_ulen = htons(uh->uh_ulen); 5676255332Scy 5677255332Scy sum2 = ntohs(ip->ip_id) + ntohs(ip->ip_len); 5678255332Scy sum2 += ntohs(ip->ip_off) & IP_DF; 5679255332Scy CALC_SUMD(sum1, sum2, sumd); 5680255332Scy 5681255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 5682255332Scy defined(__osf__) || defined(linux) 5683255332Scy ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0); 5684255332Scy#endif 5685255332Scy PREP_MB_T(fin, m); 5686255332Scy 5687255332Scy fin->fin_ip = ip; 5688255332Scy fin->fin_plen += sizeof(ip_t) + 8; /* UDP + new IPv4 hdr */ 5689255332Scy fin->fin_dlen += sizeof(ip_t) + 8; /* UDP + old IPv4 hdr */ 5690255332Scy 5691255332Scy nflags &= ~IPN_TCPUDPICMP; 5692255332Scy 5693255332Scy break; 5694255332Scy } 5695255332Scy 5696255332Scy case NAT_DIVERTOUT : 5697255332Scy { 5698255332Scy mb_t *m; 5699255332Scy 5700255332Scy skip = ipf_nat_decap(fin, nat); 5701255332Scy if (skip <= 0) { 5702255332Scy NBUMPSIDED(0, ns_decap_fail); 5703255332Scy return -1; 5704255332Scy } 5705255332Scy 5706255332Scy m = fin->fin_m; 5707255332Scy 5708255332Scy#if defined(MENTAT) && defined(_KERNEL) 5709255332Scy m->b_rptr += skip; 5710255332Scy#else 5711255332Scy m->m_data += skip; 5712255332Scy m->m_len -= skip; 5713255332Scy 5714255332Scy# ifdef M_PKTHDR 5715255332Scy if (m->m_flags & M_PKTHDR) 5716255332Scy m->m_pkthdr.len -= skip; 5717255332Scy# endif 5718255332Scy#endif 5719255332Scy 5720255332Scy ipf_nat_update(fin, nat); 5721255332Scy nflags &= ~IPN_TCPUDPICMP; 5722255332Scy fin->fin_flx |= FI_NATED; 5723255332Scy if (np != NULL && np->in_tag.ipt_num[0] != 0) 5724255332Scy fin->fin_nattag = &np->in_tag; 5725255332Scy return 1; 5726255332Scy /* NOTREACHED */ 5727255332Scy } 5728255332Scy } 5729255332Scy if (nflags & IPN_TCPUDP) 5730255332Scy tcp = fin->fin_dp; 5731255332Scy 5732145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 5733255332Scy u_short *csump; 5734255332Scy 5735255332Scy if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) { 5736255332Scy switch (nat->nat_dir) 5737255332Scy { 5738255332Scy case NAT_INBOUND : 5739255332Scy tcp->th_sport = nat->nat_nsport; 5740255332Scy fin->fin_data[0] = ntohs(nat->nat_nsport); 5741255332Scy tcp->th_dport = nat->nat_ndport; 5742255332Scy fin->fin_data[1] = ntohs(nat->nat_ndport); 5743255332Scy break; 5744255332Scy 5745255332Scy case NAT_OUTBOUND : 5746255332Scy tcp->th_sport = nat->nat_odport; 5747255332Scy fin->fin_data[0] = ntohs(nat->nat_odport); 5748255332Scy tcp->th_dport = nat->nat_osport; 5749255332Scy fin->fin_data[1] = ntohs(nat->nat_osport); 5750255332Scy break; 5751255332Scy } 575292685Sdarrenr } 575353642Sguido 5754145522Sdarrenr 5755255332Scy if ((nat->nat_odport != 0) && (nflags & IPN_ICMPQUERY)) { 5756145522Sdarrenr icmp = fin->fin_dp; 5757145522Sdarrenr 5758255332Scy icmp->icmp_id = nat->nat_nicmpid; 5759145522Sdarrenr } 5760145522Sdarrenr 5761255332Scy csump = ipf_nat_proto(fin, nat, nflags); 5762255332Scy 5763255332Scy /* 5764255332Scy * The above comments do not hold for layer 4 (or higher) 5765255332Scy * checksums... 5766255332Scy */ 5767255332Scy if (csump != NULL) { 5768255332Scy if (nat->nat_dir == NAT_OUTBOUND) 5769255332Scy ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0); 5770255332Scy else 5771255332Scy ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0); 5772255332Scy } 5773145522Sdarrenr } 5774145522Sdarrenr 5775145522Sdarrenr fin->fin_flx |= FI_NATED; 5776145522Sdarrenr if (np != NULL && np->in_tag.ipt_num[0] != 0) 5777145522Sdarrenr fin->fin_nattag = &np->in_tag; 5778145522Sdarrenr return 1; 5779145522Sdarrenr} 5780130886Sdarrenr 5781130886Sdarrenr 5782145522Sdarrenr/* ------------------------------------------------------------------------ */ 5783255332Scy/* Function: ipf_nat_proto */ 5784145522Sdarrenr/* Returns: u_short* - pointer to transport header checksum to update, */ 5785145522Sdarrenr/* NULL if the transport protocol is not recognised */ 5786145522Sdarrenr/* as needing a checksum update. */ 5787145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 5788145522Sdarrenr/* nat(I) - pointer to NAT structure */ 5789145522Sdarrenr/* nflags(I) - NAT flags set for this packet */ 5790145522Sdarrenr/* */ 5791145522Sdarrenr/* Return the pointer to the checksum field for each protocol so understood.*/ 5792145522Sdarrenr/* If support for making other changes to a protocol header is required, */ 5793145522Sdarrenr/* that is not strictly 'address' translation, such as clamping the MSS in */ 5794145522Sdarrenr/* TCP down to a specific value, then do it from here. */ 5795145522Sdarrenr/* ------------------------------------------------------------------------ */ 5796255332Scyu_short * 5797255332Scyipf_nat_proto(fin, nat, nflags) 5798255332Scy fr_info_t *fin; 5799255332Scy nat_t *nat; 5800255332Scy u_int nflags; 5801145522Sdarrenr{ 5802145522Sdarrenr icmphdr_t *icmp; 5803145522Sdarrenr u_short *csump; 5804145522Sdarrenr tcphdr_t *tcp; 5805145522Sdarrenr udphdr_t *udp; 580653642Sguido 5807145522Sdarrenr csump = NULL; 5808145522Sdarrenr if (fin->fin_out == 0) { 5809255332Scy fin->fin_rev = (nat->nat_dir & NAT_OUTBOUND); 5810145522Sdarrenr } else { 5811255332Scy fin->fin_rev = ((nat->nat_dir & NAT_OUTBOUND) == 0); 5812145522Sdarrenr } 581353642Sguido 5814145522Sdarrenr switch (fin->fin_p) 5815145522Sdarrenr { 5816145522Sdarrenr case IPPROTO_TCP : 5817145522Sdarrenr tcp = fin->fin_dp; 5818110916Sdarrenr 5819255332Scy if ((nflags & IPN_TCP) != 0) 5820255332Scy csump = &tcp->th_sum; 582153642Sguido 5822145522Sdarrenr /* 5823145522Sdarrenr * Do a MSS CLAMPING on a SYN packet, 5824145522Sdarrenr * only deal IPv4 for now. 5825145522Sdarrenr */ 5826145522Sdarrenr if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0) 5827255332Scy ipf_nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump); 582860852Sdarrenr 5829145522Sdarrenr break; 5830145522Sdarrenr 5831145522Sdarrenr case IPPROTO_UDP : 5832145522Sdarrenr udp = fin->fin_dp; 5833145522Sdarrenr 5834255332Scy if ((nflags & IPN_UDP) != 0) { 5835255332Scy if (udp->uh_sum != 0) 5836255332Scy csump = &udp->uh_sum; 5837255332Scy } 5838145522Sdarrenr break; 5839145522Sdarrenr 5840145522Sdarrenr case IPPROTO_ICMP : 5841145522Sdarrenr icmp = fin->fin_dp; 5842145522Sdarrenr 5843145522Sdarrenr if ((nflags & IPN_ICMPQUERY) != 0) { 5844145522Sdarrenr if (icmp->icmp_cksum != 0) 5845145522Sdarrenr csump = &icmp->icmp_cksum; 584653642Sguido } 5847145522Sdarrenr break; 584853642Sguido 5849255332Scy#ifdef USE_INET6 5850255332Scy case IPPROTO_ICMPV6 : 5851255332Scy { 5852255332Scy struct icmp6_hdr *icmp6 = (struct icmp6_hdr *)fin->fin_dp; 585353642Sguido 5854255332Scy icmp6 = fin->fin_dp; 5855145522Sdarrenr 5856255332Scy if ((nflags & IPN_ICMPQUERY) != 0) { 5857255332Scy if (icmp6->icmp6_cksum != 0) 5858255332Scy csump = &icmp6->icmp6_cksum; 5859255332Scy } 5860255332Scy break; 5861255332Scy } 5862255332Scy#endif 5863145522Sdarrenr } 5864255332Scy return csump; 586553642Sguido} 586653642Sguido 586753642Sguido 5868145522Sdarrenr/* ------------------------------------------------------------------------ */ 5869255332Scy/* Function: ipf_nat_expire */ 5870145522Sdarrenr/* Returns: Nil */ 5871255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 5872145522Sdarrenr/* */ 5873145522Sdarrenr/* Check all of the timeout queues for entries at the top which need to be */ 5874145522Sdarrenr/* expired. */ 5875145522Sdarrenr/* ------------------------------------------------------------------------ */ 5876255332Scyvoid 5877255332Scyipf_nat_expire(softc) 5878255332Scy ipf_main_softc_t *softc; 587953642Sguido{ 5880255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 5881145522Sdarrenr ipftq_t *ifq, *ifqnext; 5882145522Sdarrenr ipftqent_t *tqe, *tqn; 5883161356Sguido int i; 5884153876Sguido SPL_INT(s); 588553642Sguido 588653642Sguido SPL_NET(s); 5887255332Scy WRITE_ENTER(&softc->ipf_nat); 5888255332Scy for (ifq = softn->ipf_nat_tcptq, i = 0; ifq != NULL; 5889255332Scy ifq = ifq->ifq_next) { 5890145522Sdarrenr for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { 5891255332Scy if (tqe->tqe_die > softc->ipf_ticks) 5892145522Sdarrenr break; 5893145522Sdarrenr tqn = tqe->tqe_next; 5894255332Scy ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE); 589553642Sguido } 589653642Sguido } 5897145522Sdarrenr 5898255332Scy for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifq->ifq_next) { 5899145522Sdarrenr for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { 5900255332Scy if (tqe->tqe_die > softc->ipf_ticks) 5901145522Sdarrenr break; 5902145522Sdarrenr tqn = tqe->tqe_next; 5903255332Scy ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE); 5904145522Sdarrenr } 5905145522Sdarrenr } 5906145522Sdarrenr 5907255332Scy for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) { 5908145522Sdarrenr ifqnext = ifq->ifq_next; 5909145522Sdarrenr 5910145522Sdarrenr if (((ifq->ifq_flags & IFQF_DELETE) != 0) && 5911145522Sdarrenr (ifq->ifq_ref == 0)) { 5912255332Scy ipf_freetimeoutqueue(softc, ifq); 5913145522Sdarrenr } 5914145522Sdarrenr } 5915145522Sdarrenr 5916255332Scy if (softn->ipf_nat_doflush != 0) { 5917255332Scy ipf_nat_extraflush(softc, softn, 2); 5918255332Scy softn->ipf_nat_doflush = 0; 5919170268Sdarrenr } 5920170268Sdarrenr 5921255332Scy RWLOCK_EXIT(&softc->ipf_nat); 592253642Sguido SPL_X(s); 592353642Sguido} 592453642Sguido 592553642Sguido 5926145522Sdarrenr/* ------------------------------------------------------------------------ */ 5927255332Scy/* Function: ipf_nat_sync */ 5928145522Sdarrenr/* Returns: Nil */ 5929255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 5930255332Scy/* ifp(I) - pointer to network interface */ 5931145522Sdarrenr/* */ 5932145522Sdarrenr/* Walk through all of the currently active NAT sessions, looking for those */ 5933145522Sdarrenr/* which need to have their translated address updated. */ 5934145522Sdarrenr/* ------------------------------------------------------------------------ */ 5935255332Scyvoid 5936255332Scyipf_nat_sync(softc, ifp) 5937255332Scy ipf_main_softc_t *softc; 5938255332Scy void *ifp; 593953642Sguido{ 5940255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 5941145522Sdarrenr u_32_t sum1, sum2, sumd; 5942255332Scy i6addr_t in; 5943145522Sdarrenr ipnat_t *n; 5944145522Sdarrenr nat_t *nat; 594553642Sguido void *ifp2; 5946255332Scy int idx; 5947153876Sguido SPL_INT(s); 594853642Sguido 5949255332Scy if (softc->ipf_running <= 0) 5950145522Sdarrenr return; 5951145522Sdarrenr 595253642Sguido /* 595353642Sguido * Change IP addresses for NAT sessions for any protocol except TCP 5954145522Sdarrenr * since it will break the TCP connection anyway. The only rules 5955145522Sdarrenr * which will get changed are those which are "map ... -> 0/32", 5956145522Sdarrenr * where the rule specifies the address is taken from the interface. 595753642Sguido */ 595853642Sguido SPL_NET(s); 5959255332Scy WRITE_ENTER(&softc->ipf_nat); 5960145522Sdarrenr 5961255332Scy if (softc->ipf_running <= 0) { 5962255332Scy RWLOCK_EXIT(&softc->ipf_nat); 5963145522Sdarrenr return; 5964145522Sdarrenr } 5965145522Sdarrenr 5966255332Scy for (nat = softn->ipf_nat_instances; nat; nat = nat->nat_next) { 5967145522Sdarrenr if ((nat->nat_flags & IPN_TCP) != 0) 5968145522Sdarrenr continue; 5969255332Scy 5970145522Sdarrenr n = nat->nat_ptr; 5971255332Scy if (n != NULL) { 5972255332Scy if (n->in_v[1] == 4) { 5973255332Scy if (n->in_redir & NAT_MAP) { 5974255332Scy if ((n->in_nsrcaddr != 0) || 5975255332Scy (n->in_nsrcmsk != 0xffffffff)) 5976255332Scy continue; 5977255332Scy } else if (n->in_redir & NAT_REDIRECT) { 5978255332Scy if ((n->in_ndstaddr != 0) || 5979255332Scy (n->in_ndstmsk != 0xffffffff)) 5980255332Scy continue; 5981255332Scy } 5982255332Scy } 5983255332Scy#ifdef USE_INET6 5984255332Scy if (n->in_v[1] == 4) { 5985255332Scy if (n->in_redir & NAT_MAP) { 5986255332Scy if (!IP6_ISZERO(&n->in_nsrcaddr) || 5987255332Scy !IP6_ISONES(&n->in_nsrcmsk)) 5988255332Scy continue; 5989255332Scy } else if (n->in_redir & NAT_REDIRECT) { 5990255332Scy if (!IP6_ISZERO(&n->in_ndstaddr) || 5991255332Scy !IP6_ISONES(&n->in_ndstmsk)) 5992255332Scy continue; 5993255332Scy } 5994255332Scy } 5995255332Scy#endif 5996255332Scy } 5997255332Scy 5998145522Sdarrenr if (((ifp == NULL) || (ifp == nat->nat_ifps[0]) || 5999145522Sdarrenr (ifp == nat->nat_ifps[1]))) { 6000255332Scy nat->nat_ifps[0] = GETIFP(nat->nat_ifnames[0], 6001255332Scy nat->nat_v[0]); 6002255332Scy if ((nat->nat_ifps[0] != NULL) && 6003255332Scy (nat->nat_ifps[0] != (void *)-1)) { 6004255332Scy nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); 6005255332Scy } 6006145522Sdarrenr if (nat->nat_ifnames[1][0] != '\0') { 6007145522Sdarrenr nat->nat_ifps[1] = GETIFP(nat->nat_ifnames[1], 6008255332Scy nat->nat_v[1]); 6009255332Scy } else { 6010145522Sdarrenr nat->nat_ifps[1] = nat->nat_ifps[0]; 6011255332Scy } 6012255332Scy if ((nat->nat_ifps[1] != NULL) && 6013255332Scy (nat->nat_ifps[1] != (void *)-1)) { 6014255332Scy nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); 6015255332Scy } 6016145522Sdarrenr ifp2 = nat->nat_ifps[0]; 6017145522Sdarrenr if (ifp2 == NULL) 6018145522Sdarrenr continue; 6019145522Sdarrenr 602053642Sguido /* 602153642Sguido * Change the map-to address to be the same as the 602253642Sguido * new one. 602353642Sguido */ 6024255332Scy sum1 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6); 6025255332Scy if (ipf_ifpaddr(softc, nat->nat_v[0], FRI_NORMAL, ifp2, 6026255332Scy &in, NULL) != -1) { 6027255332Scy if (nat->nat_v[0] == 4) 6028255332Scy nat->nat_nsrcip = in.in4; 6029255332Scy } 6030255332Scy sum2 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6); 603153642Sguido 603253642Sguido if (sum1 == sum2) 603353642Sguido continue; 603453642Sguido /* 603553642Sguido * Readjust the checksum adjustment to take into 603653642Sguido * account the new IP#. 603753642Sguido */ 603853642Sguido CALC_SUMD(sum1, sum2, sumd); 603955929Sguido /* XXX - dont change for TCP when solaris does 604055929Sguido * hardware checksumming. 604155929Sguido */ 604255929Sguido sumd += nat->nat_sumd[0]; 604355929Sguido nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 604455929Sguido nat->nat_sumd[1] = nat->nat_sumd[0]; 604553642Sguido } 6046145522Sdarrenr } 604753642Sguido 6048255332Scy for (n = softn->ipf_nat_list; (n != NULL); n = n->in_next) { 6049255332Scy char *base = n->in_names; 6050255332Scy 6051145522Sdarrenr if ((ifp == NULL) || (n->in_ifps[0] == ifp)) 6052255332Scy n->in_ifps[0] = ipf_resolvenic(softc, 6053255332Scy base + n->in_ifnames[0], 6054255332Scy n->in_v[0]); 6055145522Sdarrenr if ((ifp == NULL) || (n->in_ifps[1] == ifp)) 6056255332Scy n->in_ifps[1] = ipf_resolvenic(softc, 6057255332Scy base + n->in_ifnames[1], 6058255332Scy n->in_v[1]); 6059255332Scy 6060255332Scy if (n->in_redir & NAT_REDIRECT) 6061255332Scy idx = 1; 6062255332Scy else 6063255332Scy idx = 0; 6064255332Scy 6065255332Scy if (((ifp == NULL) || (n->in_ifps[idx] == ifp)) && 6066255332Scy (n->in_ifps[idx] != NULL && 6067255332Scy n->in_ifps[idx] != (void *)-1)) { 6068255332Scy 6069255332Scy ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, 6070255332Scy 0, n->in_ifps[idx]); 6071255332Scy ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, 6072255332Scy 0, n->in_ifps[idx]); 6073255332Scy ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, 6074255332Scy 0, n->in_ifps[idx]); 6075255332Scy ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, 6076255332Scy 0, n->in_ifps[idx]); 6077255332Scy } 6078145522Sdarrenr } 6079255332Scy RWLOCK_EXIT(&softc->ipf_nat); 608053642Sguido SPL_X(s); 608153642Sguido} 608253642Sguido 608353642Sguido 6084145522Sdarrenr/* ------------------------------------------------------------------------ */ 6085255332Scy/* Function: ipf_nat_icmpquerytype */ 6086145522Sdarrenr/* Returns: int - 1 == success, 0 == failure */ 6087145522Sdarrenr/* Parameters: icmptype(I) - ICMP type number */ 6088145522Sdarrenr/* */ 6089145522Sdarrenr/* Tests to see if the ICMP type number passed is a query/response type or */ 6090145522Sdarrenr/* not. */ 6091145522Sdarrenr/* ------------------------------------------------------------------------ */ 6092255332Scystatic int 6093255332Scyipf_nat_icmpquerytype(icmptype) 6094255332Scy int icmptype; 6095145522Sdarrenr{ 6096145522Sdarrenr 6097145522Sdarrenr /* 6098145522Sdarrenr * For the ICMP query NAT code, it is essential that both the query 6099145522Sdarrenr * and the reply match on the NAT rule. Because the NAT structure 6100145522Sdarrenr * does not keep track of the icmptype, and a single NAT structure 6101145522Sdarrenr * is used for all icmp types with the same src, dest and id, we 6102145522Sdarrenr * simply define the replies as queries as well. The funny thing is, 6103145522Sdarrenr * altough it seems silly to call a reply a query, this is exactly 6104145522Sdarrenr * as it is defined in the IPv4 specification 6105145522Sdarrenr */ 6106145522Sdarrenr switch (icmptype) 6107145522Sdarrenr { 6108145522Sdarrenr case ICMP_ECHOREPLY: 6109145522Sdarrenr case ICMP_ECHO: 6110145522Sdarrenr /* route aedvertisement/solliciation is currently unsupported: */ 6111145522Sdarrenr /* it would require rewriting the ICMP data section */ 6112145522Sdarrenr case ICMP_TSTAMP: 6113145522Sdarrenr case ICMP_TSTAMPREPLY: 6114145522Sdarrenr case ICMP_IREQ: 6115145522Sdarrenr case ICMP_IREQREPLY: 6116145522Sdarrenr case ICMP_MASKREQ: 6117145522Sdarrenr case ICMP_MASKREPLY: 6118145522Sdarrenr return 1; 6119145522Sdarrenr default: 6120145522Sdarrenr return 0; 6121145522Sdarrenr } 6122145522Sdarrenr} 6123145522Sdarrenr 6124145522Sdarrenr 6125145522Sdarrenr/* ------------------------------------------------------------------------ */ 6126145522Sdarrenr/* Function: nat_log */ 6127145522Sdarrenr/* Returns: Nil */ 6128255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6129255332Scy/* softn(I) - pointer to NAT context structure */ 6130255332Scy/* nat(I) - pointer to NAT structure */ 6131255332Scy/* action(I) - action related to NAT structure being performed */ 6132145522Sdarrenr/* */ 6133145522Sdarrenr/* Creates a NAT log entry. */ 6134145522Sdarrenr/* ------------------------------------------------------------------------ */ 6135255332Scyvoid 6136255332Scyipf_nat_log(softc, softn, nat, action) 6137255332Scy ipf_main_softc_t *softc; 6138255332Scy ipf_nat_softc_t *softn; 6139255332Scy struct nat *nat; 6140255332Scy u_int action; 614153642Sguido{ 6142145522Sdarrenr#ifdef IPFILTER_LOG 6143139005Smlaier# ifndef LARGE_NAT 614453642Sguido struct ipnat *np; 6145138979Sdarrenr int rulen; 6146138979Sdarrenr# endif 614753642Sguido struct natlog natl; 614853642Sguido void *items[1]; 614953642Sguido size_t sizes[1]; 6150138979Sdarrenr int types[1]; 615153642Sguido 6152255332Scy bcopy((char *)&nat->nat_osrc6, (char *)&natl.nl_osrcip, 6153255332Scy sizeof(natl.nl_osrcip)); 6154255332Scy bcopy((char *)&nat->nat_nsrc6, (char *)&natl.nl_nsrcip, 6155255332Scy sizeof(natl.nl_nsrcip)); 6156255332Scy bcopy((char *)&nat->nat_odst6, (char *)&natl.nl_odstip, 6157255332Scy sizeof(natl.nl_odstip)); 6158255332Scy bcopy((char *)&nat->nat_ndst6, (char *)&natl.nl_ndstip, 6159255332Scy sizeof(natl.nl_ndstip)); 6160255332Scy 6161145522Sdarrenr natl.nl_bytes[0] = nat->nat_bytes[0]; 6162145522Sdarrenr natl.nl_bytes[1] = nat->nat_bytes[1]; 6163145522Sdarrenr natl.nl_pkts[0] = nat->nat_pkts[0]; 6164145522Sdarrenr natl.nl_pkts[1] = nat->nat_pkts[1]; 6165255332Scy natl.nl_odstport = nat->nat_odport; 6166255332Scy natl.nl_osrcport = nat->nat_osport; 6167255332Scy natl.nl_nsrcport = nat->nat_nsport; 6168255332Scy natl.nl_ndstport = nat->nat_ndport; 6169255332Scy natl.nl_p[0] = nat->nat_pr[0]; 6170255332Scy natl.nl_p[1] = nat->nat_pr[1]; 6171255332Scy natl.nl_v[0] = nat->nat_v[0]; 6172255332Scy natl.nl_v[1] = nat->nat_v[1]; 6173255332Scy natl.nl_type = nat->nat_redir; 6174255332Scy natl.nl_action = action; 617553642Sguido natl.nl_rule = -1; 6176255332Scy 6177255332Scy bcopy(nat->nat_ifnames[0], natl.nl_ifnames[0], 6178255332Scy sizeof(nat->nat_ifnames[0])); 6179255332Scy bcopy(nat->nat_ifnames[1], natl.nl_ifnames[1], 6180255332Scy sizeof(nat->nat_ifnames[1])); 6181255332Scy 6182145522Sdarrenr# ifndef LARGE_NAT 618353642Sguido if (nat->nat_ptr != NULL) { 6184255332Scy for (rulen = 0, np = softn->ipf_nat_list; np != NULL; 6185255332Scy np = np->in_next, rulen++) 618653642Sguido if (np == nat->nat_ptr) { 618753642Sguido natl.nl_rule = rulen; 618853642Sguido break; 618953642Sguido } 619053642Sguido } 6191145522Sdarrenr# endif 619253642Sguido items[0] = &natl; 619353642Sguido sizes[0] = sizeof(natl); 619453642Sguido types[0] = 0; 619553642Sguido 6196255332Scy (void) ipf_log_items(softc, IPL_LOGNAT, NULL, items, sizes, types, 1); 6197145522Sdarrenr#endif 619853642Sguido} 619992685Sdarrenr 620092685Sdarrenr 620192685Sdarrenr#if defined(__OpenBSD__) 6202145522Sdarrenr/* ------------------------------------------------------------------------ */ 6203255332Scy/* Function: ipf_nat_ifdetach */ 6204145522Sdarrenr/* Returns: Nil */ 6205145522Sdarrenr/* Parameters: ifp(I) - pointer to network interface */ 6206145522Sdarrenr/* */ 6207145522Sdarrenr/* Compatibility interface for OpenBSD to trigger the correct updating of */ 6208145522Sdarrenr/* interface references within IPFilter. */ 6209145522Sdarrenr/* ------------------------------------------------------------------------ */ 6210255332Scyvoid 6211255332Scyipf_nat_ifdetach(ifp) 6212255332Scy void *ifp; 621392685Sdarrenr{ 6214255332Scy ipf_main_softc_t *softc; 6215255332Scy 6216255332Scy softc = ipf_get_softc(0); 6217255332Scy 6218255332Scy ipf_sync(ifp); 621992685Sdarrenr return; 622092685Sdarrenr} 622192685Sdarrenr#endif 6222110916Sdarrenr 6223110916Sdarrenr 6224145522Sdarrenr/* ------------------------------------------------------------------------ */ 6225255332Scy/* Function: ipf_nat_rule_deref */ 6226170268Sdarrenr/* Returns: Nil */ 6227255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6228255332Scy/* inp(I) - pointer to pointer to NAT rule */ 6229170268Sdarrenr/* Write Locks: ipf_nat */ 6230170268Sdarrenr/* */ 6231255332Scy/* Dropping the refernce count for a rule means that whatever held the */ 6232255332Scy/* pointer to this rule (*inp) is no longer interested in it and when the */ 6233255332Scy/* reference count drops to zero, any resources allocated for the rule can */ 6234255332Scy/* be released and the rule itself free'd. */ 6235170268Sdarrenr/* ------------------------------------------------------------------------ */ 6236255332Scyvoid 6237255332Scyipf_nat_rule_deref(softc, inp) 6238255332Scy ipf_main_softc_t *softc; 6239255332Scy ipnat_t **inp; 6240170268Sdarrenr{ 6241255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 6242255332Scy ipnat_t *n; 6243170268Sdarrenr 6244255332Scy n = *inp; 6245170268Sdarrenr *inp = NULL; 6246255332Scy n->in_use--; 6247255332Scy if (n->in_use > 0) 6248255332Scy return; 6249255332Scy 6250255332Scy if (n->in_apr != NULL) 6251255332Scy ipf_proxy_deref(n->in_apr); 6252255332Scy 6253255332Scy ipf_nat_rule_fini(softc, n); 6254255332Scy 6255255332Scy if (n->in_redir & NAT_REDIRECT) { 6256255332Scy if ((n->in_flags & IPN_PROXYRULE) == 0) { 6257255332Scy ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_rdr); 6258255332Scy } 6259255332Scy } 6260255332Scy if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { 6261255332Scy if ((n->in_flags & IPN_PROXYRULE) == 0) { 6262255332Scy ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_map); 6263255332Scy } 6264255332Scy } 6265255332Scy 6266255332Scy if (n->in_tqehead[0] != NULL) { 6267255332Scy if (ipf_deletetimeoutqueue(n->in_tqehead[0]) == 0) { 6268255332Scy ipf_freetimeoutqueue(softc, n->in_tqehead[1]); 6269255332Scy } 6270255332Scy } 6271255332Scy 6272255332Scy if (n->in_tqehead[1] != NULL) { 6273255332Scy if (ipf_deletetimeoutqueue(n->in_tqehead[1]) == 0) { 6274255332Scy ipf_freetimeoutqueue(softc, n->in_tqehead[1]); 6275255332Scy } 6276255332Scy } 6277255332Scy 6278255332Scy if ((n->in_flags & IPN_PROXYRULE) == 0) { 6279255332Scy ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules); 6280255332Scy } 6281255332Scy 6282255332Scy MUTEX_DESTROY(&n->in_lock); 6283255332Scy 6284255332Scy KFREES(n, n->in_size); 6285255332Scy 6286255332Scy#if SOLARIS && !defined(INSTANCES) 6287255332Scy if (softn->ipf_nat_stats.ns_rules == 0) 6288255332Scy pfil_delayed_copy = 1; 6289170268Sdarrenr#endif 6290170268Sdarrenr} 6291170268Sdarrenr 6292170268Sdarrenr 6293170268Sdarrenr/* ------------------------------------------------------------------------ */ 6294255332Scy/* Function: ipf_nat_deref */ 6295145522Sdarrenr/* Returns: Nil */ 6296255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6297255332Scy/* natp(I) - pointer to pointer to NAT table entry */ 6298145522Sdarrenr/* */ 6299145522Sdarrenr/* Decrement the reference counter for this NAT table entry and free it if */ 6300145522Sdarrenr/* there are no more things using it. */ 6301172776Sdarrenr/* */ 6302172776Sdarrenr/* IF nat_ref == 1 when this function is called, then we have an orphan nat */ 6303172776Sdarrenr/* structure *because* it only gets called on paths _after_ nat_ref has been*/ 6304172776Sdarrenr/* incremented. If nat_ref == 1 then we shouldn't decrement it here */ 6305172776Sdarrenr/* because nat_delete() will do that and send nat_ref to -1. */ 6306172776Sdarrenr/* */ 6307172776Sdarrenr/* Holding the lock on nat_lock is required to serialise nat_delete() being */ 6308172776Sdarrenr/* called from a NAT flush ioctl with a deref happening because of a packet.*/ 6309145522Sdarrenr/* ------------------------------------------------------------------------ */ 6310255332Scyvoid 6311255332Scyipf_nat_deref(softc, natp) 6312255332Scy ipf_main_softc_t *softc; 6313255332Scy nat_t **natp; 6314145522Sdarrenr{ 6315145522Sdarrenr nat_t *nat; 6316145522Sdarrenr 6317145522Sdarrenr nat = *natp; 6318145522Sdarrenr *natp = NULL; 6319172776Sdarrenr 6320172776Sdarrenr MUTEX_ENTER(&nat->nat_lock); 6321172776Sdarrenr if (nat->nat_ref > 1) { 6322172776Sdarrenr nat->nat_ref--; 6323255332Scy ASSERT(nat->nat_ref >= 0); 6324172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 6325172776Sdarrenr return; 6326172776Sdarrenr } 6327172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 6328172776Sdarrenr 6329255332Scy WRITE_ENTER(&softc->ipf_nat); 6330255332Scy ipf_nat_delete(softc, nat, NL_EXPIRE); 6331255332Scy RWLOCK_EXIT(&softc->ipf_nat); 6332145522Sdarrenr} 6333145522Sdarrenr 6334145522Sdarrenr 6335145522Sdarrenr/* ------------------------------------------------------------------------ */ 6336255332Scy/* Function: ipf_nat_clone */ 6337145522Sdarrenr/* Returns: ipstate_t* - NULL == cloning failed, */ 6338145522Sdarrenr/* else pointer to new state structure */ 6339145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 6340145522Sdarrenr/* is(I) - pointer to master state structure */ 6341145522Sdarrenr/* Write Lock: ipf_nat */ 6342145522Sdarrenr/* */ 6343145522Sdarrenr/* Create a "duplcate" state table entry from the master. */ 6344145522Sdarrenr/* ------------------------------------------------------------------------ */ 6345255332Scynat_t * 6346255332Scyipf_nat_clone(fin, nat) 6347255332Scy fr_info_t *fin; 6348255332Scy nat_t *nat; 6349145522Sdarrenr{ 6350255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 6351255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 6352145522Sdarrenr frentry_t *fr; 6353145522Sdarrenr nat_t *clone; 6354145522Sdarrenr ipnat_t *np; 6355145522Sdarrenr 6356145522Sdarrenr KMALLOC(clone, nat_t *); 6357255332Scy if (clone == NULL) { 6358255332Scy NBUMPSIDED(fin->fin_out, ns_clone_nomem); 6359145522Sdarrenr return NULL; 6360255332Scy } 6361145522Sdarrenr bcopy((char *)nat, (char *)clone, sizeof(*clone)); 6362145522Sdarrenr 6363145522Sdarrenr MUTEX_NUKE(&clone->nat_lock); 6364145522Sdarrenr 6365255332Scy clone->nat_rev = fin->fin_rev; 6366153876Sguido clone->nat_aps = NULL; 6367153876Sguido /* 6368255332Scy * Initialize all these so that ipf_nat_delete() doesn't cause a crash. 6369153876Sguido */ 6370153876Sguido clone->nat_tqe.tqe_pnext = NULL; 6371153876Sguido clone->nat_tqe.tqe_next = NULL; 6372153876Sguido clone->nat_tqe.tqe_ifq = NULL; 6373153876Sguido clone->nat_tqe.tqe_parent = clone; 6374153876Sguido 6375145522Sdarrenr clone->nat_flags &= ~SI_CLONE; 6376145522Sdarrenr clone->nat_flags |= SI_CLONED; 6377145522Sdarrenr 6378153876Sguido if (clone->nat_hm) 6379153876Sguido clone->nat_hm->hm_ref++; 6380145522Sdarrenr 6381255332Scy if (ipf_nat_insert(softc, softn, clone) == -1) { 6382145522Sdarrenr KFREE(clone); 6383255332Scy NBUMPSIDED(fin->fin_out, ns_insert_fail); 6384145522Sdarrenr return NULL; 6385145522Sdarrenr } 6386255332Scy 6387145522Sdarrenr np = clone->nat_ptr; 6388145522Sdarrenr if (np != NULL) { 6389255332Scy if (softn->ipf_nat_logging) 6390255332Scy ipf_nat_log(softc, softn, clone, NL_CLONE); 6391145522Sdarrenr np->in_use++; 6392145522Sdarrenr } 6393145522Sdarrenr fr = clone->nat_fr; 6394145522Sdarrenr if (fr != NULL) { 6395145522Sdarrenr MUTEX_ENTER(&fr->fr_lock); 6396145522Sdarrenr fr->fr_ref++; 6397145522Sdarrenr MUTEX_EXIT(&fr->fr_lock); 6398145522Sdarrenr } 6399145522Sdarrenr 6400255332Scy 6401145522Sdarrenr /* 6402145522Sdarrenr * Because the clone is created outside the normal loop of things and 6403145522Sdarrenr * TCP has special needs in terms of state, initialise the timeout 6404145522Sdarrenr * state of the new NAT from here. 6405145522Sdarrenr */ 6406255332Scy if (clone->nat_pr[0] == IPPROTO_TCP) { 6407255332Scy (void) ipf_tcp_age(&clone->nat_tqe, fin, softn->ipf_nat_tcptq, 6408255332Scy clone->nat_flags, 2); 6409145522Sdarrenr } 6410255332Scy clone->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, clone); 6411255332Scy if (softn->ipf_nat_logging) 6412255332Scy ipf_nat_log(softc, softn, clone, NL_CLONE); 6413145522Sdarrenr return clone; 6414145522Sdarrenr} 6415145522Sdarrenr 6416145522Sdarrenr 6417145522Sdarrenr/* ------------------------------------------------------------------------ */ 6418255332Scy/* Function: ipf_nat_wildok */ 6419145522Sdarrenr/* Returns: int - 1 == packet's ports match wildcards */ 6420145522Sdarrenr/* 0 == packet's ports don't match wildcards */ 6421145522Sdarrenr/* Parameters: nat(I) - NAT entry */ 6422145522Sdarrenr/* sport(I) - source port */ 6423145522Sdarrenr/* dport(I) - destination port */ 6424145522Sdarrenr/* flags(I) - wildcard flags */ 6425145522Sdarrenr/* dir(I) - packet direction */ 6426145522Sdarrenr/* */ 6427145522Sdarrenr/* Use NAT entry and packet direction to determine which combination of */ 6428145522Sdarrenr/* wildcard flags should be used. */ 6429145522Sdarrenr/* ------------------------------------------------------------------------ */ 6430255332Scyint 6431255332Scyipf_nat_wildok(nat, sport, dport, flags, dir) 6432255332Scy nat_t *nat; 6433255332Scy int sport, dport, flags, dir; 6434145522Sdarrenr{ 6435145522Sdarrenr /* 6436145522Sdarrenr * When called by dir is set to 6437145522Sdarrenr * nat_inlookup NAT_INBOUND (0) 6438145522Sdarrenr * nat_outlookup NAT_OUTBOUND (1) 6439145522Sdarrenr * 6440145522Sdarrenr * We simply combine the packet's direction in dir with the original 6441145522Sdarrenr * "intended" direction of that NAT entry in nat->nat_dir to decide 6442145522Sdarrenr * which combination of wildcard flags to allow. 6443145522Sdarrenr */ 6444255332Scy switch ((dir << 1) | (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND))) 6445145522Sdarrenr { 6446145522Sdarrenr case 3: /* outbound packet / outbound entry */ 6447255332Scy if (((nat->nat_osport == sport) || 6448145522Sdarrenr (flags & SI_W_SPORT)) && 6449255332Scy ((nat->nat_odport == dport) || 6450145522Sdarrenr (flags & SI_W_DPORT))) 6451145522Sdarrenr return 1; 6452145522Sdarrenr break; 6453145522Sdarrenr case 2: /* outbound packet / inbound entry */ 6454255332Scy if (((nat->nat_osport == dport) || 6455255332Scy (flags & SI_W_SPORT)) && 6456255332Scy ((nat->nat_odport == sport) || 6457255332Scy (flags & SI_W_DPORT))) 6458145522Sdarrenr return 1; 6459145522Sdarrenr break; 6460145522Sdarrenr case 1: /* inbound packet / outbound entry */ 6461255332Scy if (((nat->nat_osport == dport) || 6462255332Scy (flags & SI_W_SPORT)) && 6463255332Scy ((nat->nat_odport == sport) || 6464255332Scy (flags & SI_W_DPORT))) 6465145522Sdarrenr return 1; 6466145522Sdarrenr break; 6467145522Sdarrenr case 0: /* inbound packet / inbound entry */ 6468255332Scy if (((nat->nat_osport == sport) || 6469145522Sdarrenr (flags & SI_W_SPORT)) && 6470255332Scy ((nat->nat_odport == dport) || 6471145522Sdarrenr (flags & SI_W_DPORT))) 6472145522Sdarrenr return 1; 6473145522Sdarrenr break; 6474145522Sdarrenr default: 6475145522Sdarrenr break; 6476145522Sdarrenr } 6477145522Sdarrenr 6478145522Sdarrenr return(0); 6479145522Sdarrenr} 6480145522Sdarrenr 6481145522Sdarrenr 6482145522Sdarrenr/* ------------------------------------------------------------------------ */ 6483145522Sdarrenr/* Function: nat_mssclamp */ 6484145522Sdarrenr/* Returns: Nil */ 6485145522Sdarrenr/* Parameters: tcp(I) - pointer to TCP header */ 6486145522Sdarrenr/* maxmss(I) - value to clamp the TCP MSS to */ 6487145522Sdarrenr/* fin(I) - pointer to packet information */ 6488145522Sdarrenr/* csump(I) - pointer to TCP checksum */ 6489145522Sdarrenr/* */ 6490145522Sdarrenr/* Check for MSS option and clamp it if necessary. If found and changed, */ 6491145522Sdarrenr/* then the TCP header checksum will be updated to reflect the change in */ 6492145522Sdarrenr/* the MSS. */ 6493145522Sdarrenr/* ------------------------------------------------------------------------ */ 6494255332Scystatic void 6495255332Scyipf_nat_mssclamp(tcp, maxmss, fin, csump) 6496255332Scy tcphdr_t *tcp; 6497255332Scy u_32_t maxmss; 6498255332Scy fr_info_t *fin; 6499255332Scy u_short *csump; 6500110916Sdarrenr{ 6501110916Sdarrenr u_char *cp, *ep, opt; 6502110916Sdarrenr int hlen, advance; 6503110916Sdarrenr u_32_t mss, sumd; 6504110916Sdarrenr 6505145522Sdarrenr hlen = TCP_OFF(tcp) << 2; 6506110916Sdarrenr if (hlen > sizeof(*tcp)) { 6507110916Sdarrenr cp = (u_char *)tcp + sizeof(*tcp); 6508110916Sdarrenr ep = (u_char *)tcp + hlen; 6509110916Sdarrenr 6510110916Sdarrenr while (cp < ep) { 6511110916Sdarrenr opt = cp[0]; 6512110916Sdarrenr if (opt == TCPOPT_EOL) 6513110916Sdarrenr break; 6514110916Sdarrenr else if (opt == TCPOPT_NOP) { 6515110916Sdarrenr cp++; 6516110916Sdarrenr continue; 6517110916Sdarrenr } 6518145522Sdarrenr 6519145522Sdarrenr if (cp + 1 >= ep) 6520110916Sdarrenr break; 6521110916Sdarrenr advance = cp[1]; 6522145522Sdarrenr if ((cp + advance > ep) || (advance <= 0)) 6523110916Sdarrenr break; 6524145522Sdarrenr switch (opt) 6525145522Sdarrenr { 6526110916Sdarrenr case TCPOPT_MAXSEG: 6527110916Sdarrenr if (advance != 4) 6528110916Sdarrenr break; 6529145522Sdarrenr mss = cp[2] * 256 + cp[3]; 6530110916Sdarrenr if (mss > maxmss) { 6531145522Sdarrenr cp[2] = maxmss / 256; 6532145522Sdarrenr cp[3] = maxmss & 0xff; 6533110916Sdarrenr CALC_SUMD(mss, maxmss, sumd); 6534255332Scy ipf_fix_outcksum(0, csump, sumd, 0); 6535110916Sdarrenr } 6536110916Sdarrenr break; 6537110916Sdarrenr default: 6538110916Sdarrenr /* ignore unknown options */ 6539110916Sdarrenr break; 6540110916Sdarrenr } 6541145522Sdarrenr 6542145522Sdarrenr cp += advance; 6543145522Sdarrenr } 6544145522Sdarrenr } 6545145522Sdarrenr} 6546145522Sdarrenr 6547145522Sdarrenr 6548145522Sdarrenr/* ------------------------------------------------------------------------ */ 6549255332Scy/* Function: ipf_nat_setqueue */ 6550145522Sdarrenr/* Returns: Nil */ 6551255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6552255332Scy/* softn(I) - pointer to NAT context structure */ 6553255332Scy/* nat(I)- pointer to NAT structure */ 6554145522Sdarrenr/* Locks: ipf_nat (read or write) */ 6555145522Sdarrenr/* */ 6556145522Sdarrenr/* Put the NAT entry on its default queue entry, using rev as a helped in */ 6557145522Sdarrenr/* determining which queue it should be placed on. */ 6558145522Sdarrenr/* ------------------------------------------------------------------------ */ 6559255332Scyvoid 6560255332Scyipf_nat_setqueue(softc, softn, nat) 6561255332Scy ipf_main_softc_t *softc; 6562255332Scy ipf_nat_softc_t *softn; 6563255332Scy nat_t *nat; 6564145522Sdarrenr{ 6565145522Sdarrenr ipftq_t *oifq, *nifq; 6566255332Scy int rev = nat->nat_rev; 6567145522Sdarrenr 6568145522Sdarrenr if (nat->nat_ptr != NULL) 6569145522Sdarrenr nifq = nat->nat_ptr->in_tqehead[rev]; 6570145522Sdarrenr else 6571145522Sdarrenr nifq = NULL; 6572145522Sdarrenr 6573145522Sdarrenr if (nifq == NULL) { 6574255332Scy switch (nat->nat_pr[0]) 6575145522Sdarrenr { 6576145522Sdarrenr case IPPROTO_UDP : 6577255332Scy nifq = &softn->ipf_nat_udptq; 6578145522Sdarrenr break; 6579145522Sdarrenr case IPPROTO_ICMP : 6580255332Scy nifq = &softn->ipf_nat_icmptq; 6581145522Sdarrenr break; 6582145522Sdarrenr case IPPROTO_TCP : 6583255332Scy nifq = softn->ipf_nat_tcptq + 6584255332Scy nat->nat_tqe.tqe_state[rev]; 6585145522Sdarrenr break; 6586145522Sdarrenr default : 6587255332Scy nifq = &softn->ipf_nat_iptq; 6588145522Sdarrenr break; 6589145522Sdarrenr } 6590145522Sdarrenr } 6591145522Sdarrenr 6592145522Sdarrenr oifq = nat->nat_tqe.tqe_ifq; 6593145522Sdarrenr /* 6594145522Sdarrenr * If it's currently on a timeout queue, move it from one queue to 6595145522Sdarrenr * another, else put it on the end of the newly determined queue. 6596145522Sdarrenr */ 6597145522Sdarrenr if (oifq != NULL) 6598255332Scy ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, nifq); 6599145522Sdarrenr else 6600255332Scy ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, nifq, nat); 6601145522Sdarrenr return; 6602145522Sdarrenr} 6603170268Sdarrenr 6604170268Sdarrenr 6605170268Sdarrenr/* ------------------------------------------------------------------------ */ 6606170268Sdarrenr/* Function: nat_getnext */ 6607170268Sdarrenr/* Returns: int - 0 == ok, else error */ 6608255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6609255332Scy/* t(I) - pointer to ipftoken structure */ 6610170268Sdarrenr/* itp(I) - pointer to ipfgeniter_t structure */ 6611170268Sdarrenr/* */ 6612170268Sdarrenr/* Fetch the next nat/ipnat structure pointer from the linked list and */ 6613170268Sdarrenr/* copy it out to the storage space pointed to by itp_data. The next item */ 6614170268Sdarrenr/* in the list to look at is put back in the ipftoken struture. */ 6615170268Sdarrenr/* ------------------------------------------------------------------------ */ 6616255332Scystatic int 6617255332Scyipf_nat_getnext(softc, t, itp, objp) 6618255332Scy ipf_main_softc_t *softc; 6619255332Scy ipftoken_t *t; 6620255332Scy ipfgeniter_t *itp; 6621255332Scy ipfobj_t *objp; 6622170268Sdarrenr{ 6623255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 6624170268Sdarrenr hostmap_t *hm, *nexthm = NULL, zerohm; 6625170268Sdarrenr ipnat_t *ipn, *nextipnat = NULL, zeroipn; 6626170268Sdarrenr nat_t *nat, *nextnat = NULL, zeronat; 6627255332Scy int error = 0; 6628255332Scy void *nnext; 6629170268Sdarrenr 6630255332Scy if (itp->igi_nitems != 1) { 6631255332Scy IPFERROR(60075); 6632172776Sdarrenr return ENOSPC; 6633255332Scy } 6634170268Sdarrenr 6635255332Scy READ_ENTER(&softc->ipf_nat); 6636170268Sdarrenr 6637170268Sdarrenr switch (itp->igi_type) 6638170268Sdarrenr { 6639170268Sdarrenr case IPFGENITER_HOSTMAP : 6640170268Sdarrenr hm = t->ipt_data; 6641170268Sdarrenr if (hm == NULL) { 6642255332Scy nexthm = softn->ipf_hm_maplist; 6643170268Sdarrenr } else { 6644170268Sdarrenr nexthm = hm->hm_next; 6645170268Sdarrenr } 6646255332Scy if (nexthm != NULL) { 6647255332Scy ATOMIC_INC32(nexthm->hm_ref); 6648255332Scy t->ipt_data = nexthm; 6649255332Scy } else { 6650255332Scy bzero(&zerohm, sizeof(zerohm)); 6651255332Scy nexthm = &zerohm; 6652255332Scy t->ipt_data = NULL; 6653255332Scy } 6654255332Scy nnext = nexthm->hm_next; 6655170268Sdarrenr break; 6656170268Sdarrenr 6657170268Sdarrenr case IPFGENITER_IPNAT : 6658170268Sdarrenr ipn = t->ipt_data; 6659170268Sdarrenr if (ipn == NULL) { 6660255332Scy nextipnat = softn->ipf_nat_list; 6661170268Sdarrenr } else { 6662170268Sdarrenr nextipnat = ipn->in_next; 6663170268Sdarrenr } 6664255332Scy if (nextipnat != NULL) { 6665255332Scy ATOMIC_INC32(nextipnat->in_use); 6666255332Scy t->ipt_data = nextipnat; 6667255332Scy } else { 6668255332Scy bzero(&zeroipn, sizeof(zeroipn)); 6669255332Scy nextipnat = &zeroipn; 6670255332Scy t->ipt_data = NULL; 6671255332Scy } 6672255332Scy nnext = nextipnat->in_next; 6673170268Sdarrenr break; 6674170268Sdarrenr 6675170268Sdarrenr case IPFGENITER_NAT : 6676170268Sdarrenr nat = t->ipt_data; 6677170268Sdarrenr if (nat == NULL) { 6678255332Scy nextnat = softn->ipf_nat_instances; 6679170268Sdarrenr } else { 6680170268Sdarrenr nextnat = nat->nat_next; 6681170268Sdarrenr } 6682255332Scy if (nextnat != NULL) { 6683255332Scy MUTEX_ENTER(&nextnat->nat_lock); 6684255332Scy nextnat->nat_ref++; 6685255332Scy MUTEX_EXIT(&nextnat->nat_lock); 6686255332Scy t->ipt_data = nextnat; 6687255332Scy } else { 6688255332Scy bzero(&zeronat, sizeof(zeronat)); 6689255332Scy nextnat = &zeronat; 6690255332Scy t->ipt_data = NULL; 6691255332Scy } 6692255332Scy nnext = nextnat->nat_next; 6693170268Sdarrenr break; 6694255332Scy 6695170268Sdarrenr default : 6696255332Scy RWLOCK_EXIT(&softc->ipf_nat); 6697255332Scy IPFERROR(60055); 6698170268Sdarrenr return EINVAL; 6699170268Sdarrenr } 6700170268Sdarrenr 6701255332Scy RWLOCK_EXIT(&softc->ipf_nat); 6702170268Sdarrenr 6703255332Scy objp->ipfo_ptr = itp->igi_data; 6704170268Sdarrenr 6705172776Sdarrenr switch (itp->igi_type) 6706172776Sdarrenr { 6707172776Sdarrenr case IPFGENITER_HOSTMAP : 6708255332Scy error = COPYOUT(nexthm, objp->ipfo_ptr, sizeof(*nexthm)); 6709255332Scy if (error != 0) { 6710255332Scy IPFERROR(60049); 6711255332Scy error = EFAULT; 6712255332Scy } 6713172776Sdarrenr if (hm != NULL) { 6714255332Scy WRITE_ENTER(&softc->ipf_nat); 6715255332Scy ipf_nat_hostmapdel(softc, &hm); 6716255332Scy RWLOCK_EXIT(&softc->ipf_nat); 6717172776Sdarrenr } 6718172776Sdarrenr break; 6719255332Scy 6720172776Sdarrenr case IPFGENITER_IPNAT : 6721255332Scy objp->ipfo_size = nextipnat->in_size; 6722255332Scy objp->ipfo_type = IPFOBJ_IPNAT; 6723255332Scy error = ipf_outobjk(softc, objp, nextipnat); 6724172776Sdarrenr if (ipn != NULL) { 6725255332Scy WRITE_ENTER(&softc->ipf_nat); 6726255332Scy ipf_nat_rule_deref(softc, &ipn); 6727255332Scy RWLOCK_EXIT(&softc->ipf_nat); 6728172776Sdarrenr } 6729172776Sdarrenr break; 6730172776Sdarrenr 6731170268Sdarrenr case IPFGENITER_NAT : 6732255332Scy objp->ipfo_size = sizeof(nat_t); 6733255332Scy objp->ipfo_type = IPFOBJ_NAT; 6734255332Scy error = ipf_outobjk(softc, objp, nextnat); 6735255332Scy if (nat != NULL) 6736255332Scy ipf_nat_deref(softc, &nat); 6737170268Sdarrenr 6738170268Sdarrenr break; 6739170268Sdarrenr } 6740170268Sdarrenr 6741255332Scy if (nnext == NULL) 6742255332Scy ipf_token_mark_complete(t); 6743255332Scy 6744170268Sdarrenr return error; 6745170268Sdarrenr} 6746170268Sdarrenr 6747170268Sdarrenr 6748170268Sdarrenr/* ------------------------------------------------------------------------ */ 6749170268Sdarrenr/* Function: nat_extraflush */ 6750170268Sdarrenr/* Returns: int - 0 == success, -1 == failure */ 6751255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6752255332Scy/* softn(I) - pointer to NAT context structure */ 6753255332Scy/* which(I) - how to flush the active NAT table */ 6754170268Sdarrenr/* Write Locks: ipf_nat */ 6755170268Sdarrenr/* */ 6756170268Sdarrenr/* Flush nat tables. Three actions currently defined: */ 6757170268Sdarrenr/* which == 0 : flush all nat table entries */ 6758170268Sdarrenr/* which == 1 : flush TCP connections which have started to close but are */ 6759170268Sdarrenr/* stuck for some reason. */ 6760170268Sdarrenr/* which == 2 : flush TCP connections which have been idle for a long time, */ 6761170268Sdarrenr/* starting at > 4 days idle and working back in successive half-*/ 6762170268Sdarrenr/* days to at most 12 hours old. If this fails to free enough */ 6763170268Sdarrenr/* slots then work backwards in half hour slots to 30 minutes. */ 6764170268Sdarrenr/* If that too fails, then work backwards in 30 second intervals */ 6765170268Sdarrenr/* for the last 30 minutes to at worst 30 seconds idle. */ 6766170268Sdarrenr/* ------------------------------------------------------------------------ */ 6767255332Scystatic int 6768255332Scyipf_nat_extraflush(softc, softn, which) 6769255332Scy ipf_main_softc_t *softc; 6770255332Scy ipf_nat_softc_t *softn; 6771255332Scy int which; 6772170268Sdarrenr{ 6773170268Sdarrenr nat_t *nat, **natp; 6774170268Sdarrenr ipftqent_t *tqn; 6775255332Scy ipftq_t *ifq; 6776170268Sdarrenr int removed; 6777170268Sdarrenr SPL_INT(s); 6778170268Sdarrenr 6779170268Sdarrenr removed = 0; 6780170268Sdarrenr 6781170268Sdarrenr SPL_NET(s); 6782170268Sdarrenr switch (which) 6783170268Sdarrenr { 6784170268Sdarrenr case 0 : 6785255332Scy softn->ipf_nat_stats.ns_flush_all++; 6786170268Sdarrenr /* 6787170268Sdarrenr * Style 0 flush removes everything... 6788170268Sdarrenr */ 6789255332Scy for (natp = &softn->ipf_nat_instances; 6790255332Scy ((nat = *natp) != NULL); ) { 6791255332Scy ipf_nat_delete(softc, nat, NL_FLUSH); 6792170268Sdarrenr removed++; 6793170268Sdarrenr } 6794170268Sdarrenr break; 6795170268Sdarrenr 6796170268Sdarrenr case 1 : 6797255332Scy softn->ipf_nat_stats.ns_flush_closing++; 6798170268Sdarrenr /* 6799170268Sdarrenr * Since we're only interested in things that are closing, 6800170268Sdarrenr * we can start with the appropriate timeout queue. 6801170268Sdarrenr */ 6802255332Scy for (ifq = softn->ipf_nat_tcptq + IPF_TCPS_CLOSE_WAIT; 6803255332Scy ifq != NULL; ifq = ifq->ifq_next) { 6804170268Sdarrenr 6805170268Sdarrenr for (tqn = ifq->ifq_head; tqn != NULL; ) { 6806170268Sdarrenr nat = tqn->tqe_parent; 6807170268Sdarrenr tqn = tqn->tqe_next; 6808255332Scy if (nat->nat_pr[0] != IPPROTO_TCP || 6809255332Scy nat->nat_pr[1] != IPPROTO_TCP) 6810170268Sdarrenr break; 6811255332Scy ipf_nat_delete(softc, nat, NL_EXPIRE); 6812170268Sdarrenr removed++; 6813170268Sdarrenr } 6814170268Sdarrenr } 6815170268Sdarrenr 6816170268Sdarrenr /* 6817170268Sdarrenr * Also need to look through the user defined queues. 6818170268Sdarrenr */ 6819255332Scy for (ifq = softn->ipf_nat_utqe; ifq != NULL; 6820255332Scy ifq = ifq->ifq_next) { 6821170268Sdarrenr for (tqn = ifq->ifq_head; tqn != NULL; ) { 6822170268Sdarrenr nat = tqn->tqe_parent; 6823170268Sdarrenr tqn = tqn->tqe_next; 6824255332Scy if (nat->nat_pr[0] != IPPROTO_TCP || 6825255332Scy nat->nat_pr[1] != IPPROTO_TCP) 6826170268Sdarrenr continue; 6827170268Sdarrenr 6828170268Sdarrenr if ((nat->nat_tcpstate[0] > 6829170268Sdarrenr IPF_TCPS_ESTABLISHED) && 6830170268Sdarrenr (nat->nat_tcpstate[1] > 6831170268Sdarrenr IPF_TCPS_ESTABLISHED)) { 6832255332Scy ipf_nat_delete(softc, nat, NL_EXPIRE); 6833170268Sdarrenr removed++; 6834170268Sdarrenr } 6835170268Sdarrenr } 6836170268Sdarrenr } 6837170268Sdarrenr break; 6838170268Sdarrenr 6839170268Sdarrenr /* 6840170268Sdarrenr * Args 5-11 correspond to flushing those particular states 6841170268Sdarrenr * for TCP connections. 6842170268Sdarrenr */ 6843170268Sdarrenr case IPF_TCPS_CLOSE_WAIT : 6844170268Sdarrenr case IPF_TCPS_FIN_WAIT_1 : 6845170268Sdarrenr case IPF_TCPS_CLOSING : 6846170268Sdarrenr case IPF_TCPS_LAST_ACK : 6847170268Sdarrenr case IPF_TCPS_FIN_WAIT_2 : 6848170268Sdarrenr case IPF_TCPS_TIME_WAIT : 6849170268Sdarrenr case IPF_TCPS_CLOSED : 6850255332Scy softn->ipf_nat_stats.ns_flush_state++; 6851255332Scy tqn = softn->ipf_nat_tcptq[which].ifq_head; 6852170268Sdarrenr while (tqn != NULL) { 6853170268Sdarrenr nat = tqn->tqe_parent; 6854170268Sdarrenr tqn = tqn->tqe_next; 6855255332Scy ipf_nat_delete(softc, nat, NL_FLUSH); 6856170268Sdarrenr removed++; 6857170268Sdarrenr } 6858170268Sdarrenr break; 6859255332Scy 6860170268Sdarrenr default : 6861170268Sdarrenr if (which < 30) 6862170268Sdarrenr break; 6863255332Scy 6864255332Scy softn->ipf_nat_stats.ns_flush_timeout++; 6865170268Sdarrenr /* 6866170268Sdarrenr * Take a large arbitrary number to mean the number of seconds 6867170268Sdarrenr * for which which consider to be the maximum value we'll allow 6868170268Sdarrenr * the expiration to be. 6869170268Sdarrenr */ 6870170268Sdarrenr which = IPF_TTLVAL(which); 6871255332Scy for (natp = &softn->ipf_nat_instances; 6872255332Scy ((nat = *natp) != NULL); ) { 6873255332Scy if (softc->ipf_ticks - nat->nat_touched > which) { 6874255332Scy ipf_nat_delete(softc, nat, NL_FLUSH); 6875170268Sdarrenr removed++; 6876170268Sdarrenr } else 6877170268Sdarrenr natp = &nat->nat_next; 6878170268Sdarrenr } 6879170268Sdarrenr break; 6880170268Sdarrenr } 6881170268Sdarrenr 6882170268Sdarrenr if (which != 2) { 6883170268Sdarrenr SPL_X(s); 6884170268Sdarrenr return removed; 6885170268Sdarrenr } 6886170268Sdarrenr 6887255332Scy softn->ipf_nat_stats.ns_flush_queue++; 6888255332Scy 6889170268Sdarrenr /* 6890255332Scy * Asked to remove inactive entries because the table is full, try 6891255332Scy * again, 3 times, if first attempt failed with a different criteria 6892255332Scy * each time. The order tried in must be in decreasing age. 6893255332Scy * Another alternative is to implement random drop and drop N entries 6894255332Scy * at random until N have been freed up. 6895170268Sdarrenr */ 6896255332Scy if (softc->ipf_ticks - softn->ipf_nat_last_force_flush > 6897255332Scy IPF_TTLVAL(5)) { 6898255332Scy softn->ipf_nat_last_force_flush = softc->ipf_ticks; 6899255332Scy 6900255332Scy removed = ipf_queueflush(softc, ipf_nat_flush_entry, 6901255332Scy softn->ipf_nat_tcptq, 6902255332Scy softn->ipf_nat_utqe, 6903255332Scy &softn->ipf_nat_stats.ns_active, 6904255332Scy softn->ipf_nat_table_sz, 6905255332Scy softn->ipf_nat_table_wm_low); 6906170268Sdarrenr } 6907170268Sdarrenr 6908170268Sdarrenr SPL_X(s); 6909170268Sdarrenr return removed; 6910170268Sdarrenr} 6911170268Sdarrenr 6912170268Sdarrenr 6913170268Sdarrenr/* ------------------------------------------------------------------------ */ 6914255332Scy/* Function: ipf_nat_flush_entry */ 6915170268Sdarrenr/* Returns: 0 - always succeeds */ 6916255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6917255332Scy/* entry(I) - pointer to NAT entry */ 6918170268Sdarrenr/* Write Locks: ipf_nat */ 6919170268Sdarrenr/* */ 6920170268Sdarrenr/* This function is a stepping stone between ipf_queueflush() and */ 6921170268Sdarrenr/* nat_dlete(). It is used so we can provide a uniform interface via the */ 6922170268Sdarrenr/* ipf_queueflush() function. Since the nat_delete() function returns void */ 6923170268Sdarrenr/* we translate that to mean it always succeeds in deleting something. */ 6924170268Sdarrenr/* ------------------------------------------------------------------------ */ 6925255332Scystatic int 6926255332Scyipf_nat_flush_entry(softc, entry) 6927255332Scy ipf_main_softc_t *softc; 6928255332Scy void *entry; 6929170268Sdarrenr{ 6930255332Scy ipf_nat_delete(softc, entry, NL_FLUSH); 6931170268Sdarrenr return 0; 6932170268Sdarrenr} 6933172776Sdarrenr 6934172776Sdarrenr 6935172776Sdarrenr/* ------------------------------------------------------------------------ */ 6936255332Scy/* Function: ipf_nat_iterator */ 6937255332Scy/* Returns: int - 0 == ok, else error */ 6938255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6939255332Scy/* token(I) - pointer to ipftoken structure */ 6940255332Scy/* itp(I) - pointer to ipfgeniter_t structure */ 6941255332Scy/* obj(I) - pointer to data description structure */ 6942255332Scy/* */ 6943255332Scy/* This function acts as a handler for the SIOCGENITER ioctls that use a */ 6944255332Scy/* generic structure to iterate through a list. There are three different */ 6945255332Scy/* linked lists of NAT related information to go through: NAT rules, active */ 6946255332Scy/* NAT mappings and the NAT fragment cache. */ 6947255332Scy/* ------------------------------------------------------------------------ */ 6948255332Scystatic int 6949255332Scyipf_nat_iterator(softc, token, itp, obj) 6950255332Scy ipf_main_softc_t *softc; 6951255332Scy ipftoken_t *token; 6952255332Scy ipfgeniter_t *itp; 6953255332Scy ipfobj_t *obj; 6954255332Scy{ 6955255332Scy int error; 6956255332Scy 6957255332Scy if (itp->igi_data == NULL) { 6958255332Scy IPFERROR(60052); 6959255332Scy return EFAULT; 6960255332Scy } 6961255332Scy 6962255332Scy switch (itp->igi_type) 6963255332Scy { 6964255332Scy case IPFGENITER_HOSTMAP : 6965255332Scy case IPFGENITER_IPNAT : 6966255332Scy case IPFGENITER_NAT : 6967255332Scy error = ipf_nat_getnext(softc, token, itp, obj); 6968255332Scy break; 6969255332Scy 6970255332Scy case IPFGENITER_NATFRAG : 6971255332Scy error = ipf_frag_nat_next(softc, token, itp); 6972255332Scy break; 6973255332Scy default : 6974255332Scy IPFERROR(60053); 6975255332Scy error = EINVAL; 6976255332Scy break; 6977255332Scy } 6978255332Scy 6979255332Scy return error; 6980255332Scy} 6981255332Scy 6982255332Scy 6983255332Scy/* ------------------------------------------------------------------------ */ 6984255332Scy/* Function: ipf_nat_setpending */ 6985255332Scy/* Returns: Nil */ 6986255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6987255332Scy/* nat(I) - pointer to NAT structure */ 6988255332Scy/* Locks: ipf_nat (read or write) */ 6989255332Scy/* */ 6990255332Scy/* Put the NAT entry on to the pending queue - this queue has a very short */ 6991255332Scy/* lifetime where items are put that can't be deleted straight away because */ 6992255332Scy/* of locking issues but we want to delete them ASAP, anyway. In calling */ 6993255332Scy/* this function, it is assumed that the owner (if there is one, as shown */ 6994255332Scy/* by nat_me) is no longer interested in it. */ 6995255332Scy/* ------------------------------------------------------------------------ */ 6996255332Scyvoid 6997255332Scyipf_nat_setpending(softc, nat) 6998255332Scy ipf_main_softc_t *softc; 6999255332Scy nat_t *nat; 7000255332Scy{ 7001255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 7002255332Scy ipftq_t *oifq; 7003255332Scy 7004255332Scy oifq = nat->nat_tqe.tqe_ifq; 7005255332Scy if (oifq != NULL) 7006255332Scy ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, 7007255332Scy &softn->ipf_nat_pending); 7008255332Scy else 7009255332Scy ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, 7010255332Scy &softn->ipf_nat_pending, nat); 7011255332Scy 7012255332Scy if (nat->nat_me != NULL) { 7013255332Scy *nat->nat_me = NULL; 7014255332Scy nat->nat_me = NULL; 7015255332Scy nat->nat_ref--; 7016255332Scy ASSERT(nat->nat_ref >= 0); 7017255332Scy } 7018255332Scy} 7019255332Scy 7020255332Scy 7021255332Scy/* ------------------------------------------------------------------------ */ 7022255332Scy/* Function: nat_newrewrite */ 7023255332Scy/* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ 7024255332Scy/* allow rule to be moved if IPN_ROUNDR is set. */ 7025255332Scy/* Parameters: fin(I) - pointer to packet information */ 7026255332Scy/* nat(I) - pointer to NAT entry */ 7027255332Scy/* ni(I) - pointer to structure with misc. information needed */ 7028255332Scy/* to create new NAT entry. */ 7029255332Scy/* Write Lock: ipf_nat */ 7030255332Scy/* */ 7031255332Scy/* This function is responsible for setting up an active NAT session where */ 7032255332Scy/* we are changing both the source and destination parameters at the same */ 7033255332Scy/* time. The loop in here works differently to elsewhere - each iteration */ 7034255332Scy/* is responsible for changing a single parameter that can be incremented. */ 7035255332Scy/* So one pass may increase the source IP#, next source port, next dest. IP#*/ 7036255332Scy/* and the last destination port for a total of 4 iterations to try each. */ 7037255332Scy/* This is done to try and exhaustively use the translation space available.*/ 7038255332Scy/* ------------------------------------------------------------------------ */ 7039255332Scystatic int 7040255332Scyipf_nat_newrewrite(fin, nat, nai) 7041255332Scy fr_info_t *fin; 7042255332Scy nat_t *nat; 7043255332Scy natinfo_t *nai; 7044255332Scy{ 7045255332Scy int src_search = 1; 7046255332Scy int dst_search = 1; 7047255332Scy fr_info_t frnat; 7048255332Scy u_32_t flags; 7049255332Scy u_short swap; 7050255332Scy ipnat_t *np; 7051255332Scy nat_t *natl; 7052255332Scy int l = 0; 7053255332Scy int changed; 7054255332Scy 7055255332Scy natl = NULL; 7056255332Scy changed = -1; 7057255332Scy np = nai->nai_np; 7058255332Scy flags = nat->nat_flags; 7059255332Scy bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); 7060255332Scy 7061255332Scy nat->nat_hm = NULL; 7062255332Scy 7063255332Scy do { 7064255332Scy changed = -1; 7065255332Scy /* TRACE (l, src_search, dst_search, np) */ 7066255332Scy 7067255332Scy if ((src_search == 0) && (np->in_spnext == 0) && 7068255332Scy (dst_search == 0) && (np->in_dpnext == 0)) { 7069255332Scy if (l > 0) 7070255332Scy return -1; 7071255332Scy } 7072255332Scy 7073255332Scy /* 7074255332Scy * Find a new source address 7075255332Scy */ 7076255332Scy if (ipf_nat_nextaddr(fin, &np->in_nsrc, &frnat.fin_saddr, 7077255332Scy &frnat.fin_saddr) == -1) { 7078255332Scy return -1; 7079255332Scy } 7080255332Scy 7081255332Scy if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0xffffffff)) { 7082255332Scy src_search = 0; 7083255332Scy if (np->in_stepnext == 0) 7084255332Scy np->in_stepnext = 1; 7085255332Scy 7086255332Scy } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) { 7087255332Scy src_search = 0; 7088255332Scy if (np->in_stepnext == 0) 7089255332Scy np->in_stepnext = 1; 7090255332Scy 7091255332Scy } else if (np->in_nsrcmsk == 0xffffffff) { 7092255332Scy src_search = 0; 7093255332Scy if (np->in_stepnext == 0) 7094255332Scy np->in_stepnext = 1; 7095255332Scy 7096255332Scy } else if (np->in_nsrcmsk != 0xffffffff) { 7097255332Scy if (np->in_stepnext == 0 && changed == -1) { 7098255332Scy np->in_snip++; 7099255332Scy np->in_stepnext++; 7100255332Scy changed = 0; 7101255332Scy } 7102255332Scy } 7103255332Scy 7104255332Scy if ((flags & IPN_TCPUDPICMP) != 0) { 7105255332Scy if (np->in_spnext != 0) 7106255332Scy frnat.fin_data[0] = np->in_spnext; 7107255332Scy 7108255332Scy /* 7109255332Scy * Standard port translation. Select next port. 7110255332Scy */ 7111255332Scy if ((flags & IPN_FIXEDSPORT) != 0) { 7112255332Scy np->in_stepnext = 2; 7113255332Scy } else if ((np->in_stepnext == 1) && 7114255332Scy (changed == -1) && (natl != NULL)) { 7115255332Scy np->in_spnext++; 7116255332Scy np->in_stepnext++; 7117255332Scy changed = 1; 7118255332Scy if (np->in_spnext > np->in_spmax) 7119255332Scy np->in_spnext = np->in_spmin; 7120255332Scy } 7121255332Scy } else { 7122255332Scy np->in_stepnext = 2; 7123255332Scy } 7124255332Scy np->in_stepnext &= 0x3; 7125255332Scy 7126255332Scy /* 7127255332Scy * Find a new destination address 7128255332Scy */ 7129255332Scy /* TRACE (fin, np, l, frnat) */ 7130255332Scy 7131255332Scy if (ipf_nat_nextaddr(fin, &np->in_ndst, &frnat.fin_daddr, 7132255332Scy &frnat.fin_daddr) == -1) 7133255332Scy return -1; 7134255332Scy if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) { 7135255332Scy dst_search = 0; 7136255332Scy if (np->in_stepnext == 2) 7137255332Scy np->in_stepnext = 3; 7138255332Scy 7139255332Scy } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0)) { 7140255332Scy dst_search = 0; 7141255332Scy if (np->in_stepnext == 2) 7142255332Scy np->in_stepnext = 3; 7143255332Scy 7144255332Scy } else if (np->in_ndstmsk == 0xffffffff) { 7145255332Scy dst_search = 0; 7146255332Scy if (np->in_stepnext == 2) 7147255332Scy np->in_stepnext = 3; 7148255332Scy 7149255332Scy } else if (np->in_ndstmsk != 0xffffffff) { 7150255332Scy if ((np->in_stepnext == 2) && (changed == -1) && 7151255332Scy (natl != NULL)) { 7152255332Scy changed = 2; 7153255332Scy np->in_stepnext++; 7154255332Scy np->in_dnip++; 7155255332Scy } 7156255332Scy } 7157255332Scy 7158255332Scy if ((flags & IPN_TCPUDPICMP) != 0) { 7159255332Scy if (np->in_dpnext != 0) 7160255332Scy frnat.fin_data[1] = np->in_dpnext; 7161255332Scy 7162255332Scy /* 7163255332Scy * Standard port translation. Select next port. 7164255332Scy */ 7165255332Scy if ((flags & IPN_FIXEDDPORT) != 0) { 7166255332Scy np->in_stepnext = 0; 7167255332Scy } else if (np->in_stepnext == 3 && changed == -1) { 7168255332Scy np->in_dpnext++; 7169255332Scy np->in_stepnext++; 7170255332Scy changed = 3; 7171255332Scy if (np->in_dpnext > np->in_dpmax) 7172255332Scy np->in_dpnext = np->in_dpmin; 7173255332Scy } 7174255332Scy } else { 7175255332Scy if (np->in_stepnext == 3) 7176255332Scy np->in_stepnext = 0; 7177255332Scy } 7178255332Scy 7179255332Scy /* TRACE (frnat) */ 7180255332Scy 7181255332Scy /* 7182255332Scy * Here we do a lookup of the connection as seen from 7183255332Scy * the outside. If an IP# pair already exists, try 7184255332Scy * again. So if you have A->B becomes C->B, you can 7185255332Scy * also have D->E become C->E but not D->B causing 7186255332Scy * another C->B. Also take protocol and ports into 7187255332Scy * account when determining whether a pre-existing 7188255332Scy * NAT setup will cause an external conflict where 7189255332Scy * this is appropriate. 7190255332Scy * 7191255332Scy * fin_data[] is swapped around because we are doing a 7192255332Scy * lookup of the packet is if it were moving in the opposite 7193255332Scy * direction of the one we are working with now. 7194255332Scy */ 7195255332Scy if (flags & IPN_TCPUDP) { 7196255332Scy swap = frnat.fin_data[0]; 7197255332Scy frnat.fin_data[0] = frnat.fin_data[1]; 7198255332Scy frnat.fin_data[1] = swap; 7199255332Scy } 7200255332Scy if (fin->fin_out == 1) { 7201255332Scy natl = ipf_nat_inlookup(&frnat, 7202255332Scy flags & ~(SI_WILDP|NAT_SEARCH), 7203255332Scy (u_int)frnat.fin_p, 7204255332Scy frnat.fin_dst, frnat.fin_src); 7205255332Scy 7206255332Scy } else { 7207255332Scy natl = ipf_nat_outlookup(&frnat, 7208255332Scy flags & ~(SI_WILDP|NAT_SEARCH), 7209255332Scy (u_int)frnat.fin_p, 7210255332Scy frnat.fin_dst, frnat.fin_src); 7211255332Scy } 7212255332Scy if (flags & IPN_TCPUDP) { 7213255332Scy swap = frnat.fin_data[0]; 7214255332Scy frnat.fin_data[0] = frnat.fin_data[1]; 7215255332Scy frnat.fin_data[1] = swap; 7216255332Scy } 7217255332Scy 7218255332Scy /* TRACE natl, in_stepnext, l */ 7219255332Scy 7220255332Scy if ((natl != NULL) && (l > 8)) /* XXX 8 is arbitrary */ 7221255332Scy return -1; 7222255332Scy 7223255332Scy np->in_stepnext &= 0x3; 7224255332Scy 7225255332Scy l++; 7226255332Scy changed = -1; 7227255332Scy } while (natl != NULL); 7228255332Scy 7229255332Scy nat->nat_osrcip = fin->fin_src; 7230255332Scy nat->nat_odstip = fin->fin_dst; 7231255332Scy nat->nat_nsrcip = frnat.fin_src; 7232255332Scy nat->nat_ndstip = frnat.fin_dst; 7233255332Scy 7234255332Scy if ((flags & IPN_TCPUDP) != 0) { 7235255332Scy nat->nat_osport = htons(fin->fin_data[0]); 7236255332Scy nat->nat_odport = htons(fin->fin_data[1]); 7237255332Scy nat->nat_nsport = htons(frnat.fin_data[0]); 7238255332Scy nat->nat_ndport = htons(frnat.fin_data[1]); 7239255332Scy } else if ((flags & IPN_ICMPQUERY) != 0) { 7240255332Scy nat->nat_oicmpid = fin->fin_data[1]; 7241255332Scy nat->nat_nicmpid = frnat.fin_data[1]; 7242255332Scy } 7243255332Scy 7244255332Scy return 0; 7245255332Scy} 7246255332Scy 7247255332Scy 7248255332Scy/* ------------------------------------------------------------------------ */ 7249255332Scy/* Function: nat_newdivert */ 7250255332Scy/* Returns: int - -1 == error, 0 == success */ 7251255332Scy/* Parameters: fin(I) - pointer to packet information */ 7252255332Scy/* nat(I) - pointer to NAT entry */ 7253255332Scy/* ni(I) - pointer to structure with misc. information needed */ 7254255332Scy/* to create new NAT entry. */ 7255255332Scy/* Write Lock: ipf_nat */ 7256255332Scy/* */ 7257255332Scy/* Create a new NAT divert session as defined by the NAT rule. This is */ 7258255332Scy/* somewhat different to other NAT session creation routines because we */ 7259255332Scy/* do not iterate through either port numbers or IP addresses, searching */ 7260255332Scy/* for a unique mapping, however, a complimentary duplicate check is made. */ 7261255332Scy/* ------------------------------------------------------------------------ */ 7262255332Scystatic int 7263255332Scyipf_nat_newdivert(fin, nat, nai) 7264255332Scy fr_info_t *fin; 7265255332Scy nat_t *nat; 7266255332Scy natinfo_t *nai; 7267255332Scy{ 7268255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 7269255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 7270255332Scy fr_info_t frnat; 7271255332Scy ipnat_t *np; 7272255332Scy nat_t *natl; 7273255332Scy int p; 7274255332Scy 7275255332Scy np = nai->nai_np; 7276255332Scy bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); 7277255332Scy 7278255332Scy nat->nat_pr[0] = 0; 7279255332Scy nat->nat_osrcaddr = fin->fin_saddr; 7280255332Scy nat->nat_odstaddr = fin->fin_daddr; 7281255332Scy frnat.fin_saddr = htonl(np->in_snip); 7282255332Scy frnat.fin_daddr = htonl(np->in_dnip); 7283255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 7284255332Scy nat->nat_osport = htons(fin->fin_data[0]); 7285255332Scy nat->nat_odport = htons(fin->fin_data[1]); 7286255332Scy } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { 7287255332Scy nat->nat_oicmpid = fin->fin_data[1]; 7288255332Scy } 7289255332Scy 7290255332Scy if (np->in_redir & NAT_DIVERTUDP) { 7291255332Scy frnat.fin_data[0] = np->in_spnext; 7292255332Scy frnat.fin_data[1] = np->in_dpnext; 7293255332Scy frnat.fin_flx |= FI_TCPUDP; 7294255332Scy p = IPPROTO_UDP; 7295255332Scy } else { 7296255332Scy frnat.fin_flx &= ~FI_TCPUDP; 7297255332Scy p = IPPROTO_IPIP; 7298255332Scy } 7299255332Scy 7300255332Scy if (fin->fin_out == 1) { 7301255332Scy natl = ipf_nat_inlookup(&frnat, 0, p, 7302255332Scy frnat.fin_dst, frnat.fin_src); 7303255332Scy 7304255332Scy } else { 7305255332Scy natl = ipf_nat_outlookup(&frnat, 0, p, 7306255332Scy frnat.fin_dst, frnat.fin_src); 7307255332Scy } 7308255332Scy 7309255332Scy if (natl != NULL) { 7310255332Scy NBUMPSIDED(fin->fin_out, ns_divert_exist); 7311255332Scy return -1; 7312255332Scy } 7313255332Scy 7314255332Scy nat->nat_nsrcaddr = frnat.fin_saddr; 7315255332Scy nat->nat_ndstaddr = frnat.fin_daddr; 7316255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 7317255332Scy nat->nat_nsport = htons(frnat.fin_data[0]); 7318255332Scy nat->nat_ndport = htons(frnat.fin_data[1]); 7319255332Scy } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { 7320255332Scy nat->nat_nicmpid = frnat.fin_data[1]; 7321255332Scy } 7322255332Scy 7323255332Scy nat->nat_pr[fin->fin_out] = fin->fin_p; 7324255332Scy nat->nat_pr[1 - fin->fin_out] = p; 7325255332Scy 7326255332Scy if (np->in_redir & NAT_REDIRECT) 7327255332Scy nat->nat_dir = NAT_DIVERTIN; 7328255332Scy else 7329255332Scy nat->nat_dir = NAT_DIVERTOUT; 7330255332Scy 7331255332Scy return 0; 7332255332Scy} 7333255332Scy 7334255332Scy 7335255332Scy/* ------------------------------------------------------------------------ */ 7336255332Scy/* Function: nat_builddivertmp */ 7337255332Scy/* Returns: int - -1 == error, 0 == success */ 7338255332Scy/* Parameters: softn(I) - pointer to NAT context structure */ 7339255332Scy/* np(I) - pointer to a NAT rule */ 7340255332Scy/* */ 7341255332Scy/* For divert rules, a skeleton packet representing what will be prepended */ 7342255332Scy/* to the real packet is created. Even though we don't have the full */ 7343255332Scy/* packet here, a checksum is calculated that we update later when we */ 7344255332Scy/* fill in the final details. At present a 0 checksum for UDP is being set */ 7345255332Scy/* here because it is expected that divert will be used for localhost. */ 7346255332Scy/* ------------------------------------------------------------------------ */ 7347255332Scystatic int 7348255332Scyipf_nat_builddivertmp(softn, np) 7349255332Scy ipf_nat_softc_t *softn; 7350255332Scy ipnat_t *np; 7351255332Scy{ 7352255332Scy udphdr_t *uh; 7353255332Scy size_t len; 7354255332Scy ip_t *ip; 7355255332Scy 7356255332Scy if ((np->in_redir & NAT_DIVERTUDP) != 0) 7357255332Scy len = sizeof(ip_t) + sizeof(udphdr_t); 7358255332Scy else 7359255332Scy len = sizeof(ip_t); 7360255332Scy 7361255332Scy ALLOC_MB_T(np->in_divmp, len); 7362255332Scy if (np->in_divmp == NULL) { 7363255332Scy NBUMPD(ipf_nat_stats, ns_divert_build); 7364255332Scy return -1; 7365255332Scy } 7366255332Scy 7367255332Scy /* 7368255332Scy * First, the header to get the packet diverted to the new destination 7369255332Scy */ 7370255332Scy ip = MTOD(np->in_divmp, ip_t *); 7371255332Scy IP_V_A(ip, 4); 7372255332Scy IP_HL_A(ip, 5); 7373255332Scy ip->ip_tos = 0; 7374255332Scy if ((np->in_redir & NAT_DIVERTUDP) != 0) 7375255332Scy ip->ip_p = IPPROTO_UDP; 7376255332Scy else 7377255332Scy ip->ip_p = IPPROTO_IPIP; 7378255332Scy ip->ip_ttl = 255; 7379255332Scy ip->ip_off = 0; 7380255332Scy ip->ip_sum = 0; 7381255332Scy ip->ip_len = htons(len); 7382255332Scy ip->ip_id = 0; 7383255332Scy ip->ip_src.s_addr = htonl(np->in_snip); 7384255332Scy ip->ip_dst.s_addr = htonl(np->in_dnip); 7385255332Scy ip->ip_sum = ipf_cksum((u_short *)ip, sizeof(*ip)); 7386255332Scy 7387255332Scy if (np->in_redir & NAT_DIVERTUDP) { 7388255332Scy uh = (udphdr_t *)(ip + 1); 7389255332Scy uh->uh_sum = 0; 7390255332Scy uh->uh_ulen = 8; 7391255332Scy uh->uh_sport = htons(np->in_spnext); 7392255332Scy uh->uh_dport = htons(np->in_dpnext); 7393255332Scy } 7394255332Scy 7395255332Scy return 0; 7396255332Scy} 7397255332Scy 7398255332Scy 7399255332Scy#define MINDECAP (sizeof(ip_t) + sizeof(udphdr_t) + sizeof(ip_t)) 7400255332Scy 7401255332Scy/* ------------------------------------------------------------------------ */ 7402255332Scy/* Function: nat_decap */ 7403255332Scy/* Returns: int - -1 == error, 0 == success */ 7404255332Scy/* Parameters: fin(I) - pointer to packet information */ 7405255332Scy/* nat(I) - pointer to current NAT session */ 7406255332Scy/* */ 7407255332Scy/* This function is responsible for undoing a packet's encapsulation in the */ 7408255332Scy/* reverse of an encap/divert rule. After removing the outer encapsulation */ 7409255332Scy/* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/ 7410255332Scy/* match the "new" packet as it may still be used by IPFilter elsewhere. */ 7411255332Scy/* We use "dir" here as the basis for some of the expectations about the */ 7412255332Scy/* outer header. If we return an error, the goal is to leave the original */ 7413255332Scy/* packet information undisturbed - this falls short at the end where we'd */ 7414255332Scy/* need to back a backup copy of "fin" - expensive. */ 7415255332Scy/* ------------------------------------------------------------------------ */ 7416255332Scystatic int 7417255332Scyipf_nat_decap(fin, nat) 7418255332Scy fr_info_t *fin; 7419255332Scy nat_t *nat; 7420255332Scy{ 7421255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 7422255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 7423255332Scy char *hdr; 7424255332Scy int hlen; 7425255332Scy int skip; 7426255332Scy mb_t *m; 7427255332Scy 7428255332Scy if ((fin->fin_flx & FI_ICMPERR) != 0) { 7429255332Scy /* 7430255332Scy * ICMP packets don't get decapsulated, instead what we need 7431255332Scy * to do is change the ICMP reply from including (in the data 7432255332Scy * portion for errors) the encapsulated packet that we sent 7433255332Scy * out to something that resembles the original packet prior 7434255332Scy * to encapsulation. This isn't done here - all we're doing 7435255332Scy * here is changing the outer address to ensure that it gets 7436255332Scy * targetted back to the correct system. 7437255332Scy */ 7438255332Scy 7439255332Scy if (nat->nat_dir & NAT_OUTBOUND) { 7440255332Scy u_32_t sum1, sum2, sumd; 7441255332Scy 7442255332Scy sum1 = ntohl(fin->fin_daddr); 7443255332Scy sum2 = ntohl(nat->nat_osrcaddr); 7444255332Scy CALC_SUMD(sum1, sum2, sumd); 7445255332Scy fin->fin_ip->ip_dst = nat->nat_osrcip; 7446255332Scy fin->fin_daddr = nat->nat_osrcaddr; 7447255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ 7448255332Scy defined(__osf__) || defined(linux) 7449255332Scy ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, sumd, 0); 7450255332Scy#endif 7451255332Scy } 7452255332Scy return 0; 7453255332Scy } 7454255332Scy 7455255332Scy m = fin->fin_m; 7456255332Scy skip = fin->fin_hlen; 7457255332Scy 7458255332Scy switch (nat->nat_dir) 7459255332Scy { 7460255332Scy case NAT_DIVERTIN : 7461255332Scy case NAT_DIVERTOUT : 7462255332Scy if (fin->fin_plen < MINDECAP) 7463255332Scy return -1; 7464255332Scy skip += sizeof(udphdr_t); 7465255332Scy break; 7466255332Scy 7467255332Scy case NAT_ENCAPIN : 7468255332Scy case NAT_ENCAPOUT : 7469255332Scy if (fin->fin_plen < (skip + sizeof(ip_t))) 7470255332Scy return -1; 7471255332Scy break; 7472255332Scy default : 7473255332Scy return -1; 7474255332Scy /* NOTREACHED */ 7475255332Scy } 7476255332Scy 7477255332Scy /* 7478255332Scy * The aim here is to keep the original packet details in "fin" for 7479255332Scy * as long as possible so that returning with an error is for the 7480255332Scy * original packet and there is little undoing work to do. 7481255332Scy */ 7482255332Scy if (M_LEN(m) < skip + sizeof(ip_t)) { 7483255332Scy if (ipf_pr_pullup(fin, skip + sizeof(ip_t)) == -1) 7484255332Scy return -1; 7485255332Scy } 7486255332Scy 7487255332Scy hdr = MTOD(fin->fin_m, char *); 7488255332Scy fin->fin_ip = (ip_t *)(hdr + skip); 7489255332Scy hlen = IP_HL(fin->fin_ip) << 2; 7490255332Scy 7491255332Scy if (ipf_pr_pullup(fin, skip + hlen) == -1) { 7492255332Scy NBUMPSIDED(fin->fin_out, ns_decap_pullup); 7493255332Scy return -1; 7494255332Scy } 7495255332Scy 7496255332Scy fin->fin_hlen = hlen; 7497255332Scy fin->fin_dlen -= skip; 7498255332Scy fin->fin_plen -= skip; 7499255332Scy fin->fin_ipoff += skip; 7500255332Scy 7501255332Scy if (ipf_makefrip(hlen, (ip_t *)hdr, fin) == -1) { 7502255332Scy NBUMPSIDED(fin->fin_out, ns_decap_bad); 7503255332Scy return -1; 7504255332Scy } 7505255332Scy 7506255332Scy return skip; 7507255332Scy} 7508255332Scy 7509255332Scy 7510255332Scy/* ------------------------------------------------------------------------ */ 7511255332Scy/* Function: nat_nextaddr */ 7512255332Scy/* Returns: int - -1 == bad input (no new address), */ 7513255332Scy/* 0 == success and dst has new address */ 7514255332Scy/* Parameters: fin(I) - pointer to packet information */ 7515255332Scy/* na(I) - how to generate new address */ 7516255332Scy/* old(I) - original address being replaced */ 7517255332Scy/* dst(O) - where to put the new address */ 7518255332Scy/* Write Lock: ipf_nat */ 7519255332Scy/* */ 7520255332Scy/* This function uses the contents of the "na" structure, in combination */ 7521255332Scy/* with "old" to produce a new address to store in "dst". Not all of the */ 7522255332Scy/* possible uses of "na" will result in a new address. */ 7523255332Scy/* ------------------------------------------------------------------------ */ 7524255332Scystatic int 7525255332Scyipf_nat_nextaddr(fin, na, old, dst) 7526255332Scy fr_info_t *fin; 7527255332Scy nat_addr_t *na; 7528255332Scy u_32_t *old, *dst; 7529255332Scy{ 7530255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 7531255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 7532255332Scy u_32_t amin, amax, new; 7533255332Scy i6addr_t newip; 7534255332Scy int error; 7535255332Scy 7536255332Scy new = 0; 7537255332Scy amin = na->na_addr[0].in4.s_addr; 7538255332Scy 7539255332Scy switch (na->na_atype) 7540255332Scy { 7541255332Scy case FRI_RANGE : 7542255332Scy amax = na->na_addr[1].in4.s_addr; 7543255332Scy break; 7544255332Scy 7545255332Scy case FRI_NETMASKED : 7546255332Scy case FRI_DYNAMIC : 7547255332Scy case FRI_NORMAL : 7548255332Scy /* 7549255332Scy * Compute the maximum address by adding the inverse of the 7550255332Scy * netmask to the minimum address. 7551255332Scy */ 7552255332Scy amax = ~na->na_addr[1].in4.s_addr; 7553255332Scy amax |= amin; 7554255332Scy break; 7555255332Scy 7556255332Scy case FRI_LOOKUP : 7557255332Scy break; 7558255332Scy 7559255332Scy case FRI_BROADCAST : 7560255332Scy case FRI_PEERADDR : 7561255332Scy case FRI_NETWORK : 7562255332Scy default : 7563255332Scy return -1; 7564255332Scy } 7565255332Scy 7566255332Scy error = -1; 7567255332Scy 7568255332Scy if (na->na_atype == FRI_LOOKUP) { 7569255332Scy if (na->na_type == IPLT_DSTLIST) { 7570255332Scy error = ipf_dstlist_select_node(fin, na->na_ptr, dst, 7571255332Scy NULL); 7572255332Scy } else { 7573255332Scy NBUMPSIDE(fin->fin_out, ns_badnextaddr); 7574255332Scy } 7575255332Scy 7576255332Scy } else if (na->na_atype == IPLT_NONE) { 7577255332Scy /* 7578255332Scy * 0/0 as the new address means leave it alone. 7579255332Scy */ 7580255332Scy if (na->na_addr[0].in4.s_addr == 0 && 7581255332Scy na->na_addr[1].in4.s_addr == 0) { 7582255332Scy new = *old; 7583255332Scy 7584255332Scy /* 7585255332Scy * 0/32 means get the interface's address 7586255332Scy */ 7587255332Scy } else if (na->na_addr[0].in4.s_addr == 0 && 7588255332Scy na->na_addr[1].in4.s_addr == 0xffffffff) { 7589255332Scy if (ipf_ifpaddr(softc, 4, na->na_atype, 7590255332Scy fin->fin_ifp, &newip, NULL) == -1) { 7591255332Scy NBUMPSIDED(fin->fin_out, ns_ifpaddrfail); 7592255332Scy return -1; 7593255332Scy } 7594255332Scy new = newip.in4.s_addr; 7595255332Scy } else { 7596255332Scy new = htonl(na->na_nextip); 7597255332Scy } 7598255332Scy *dst = new; 7599255332Scy error = 0; 7600255332Scy 7601255332Scy } else { 7602255332Scy NBUMPSIDE(fin->fin_out, ns_badnextaddr); 7603255332Scy } 7604255332Scy 7605255332Scy return error; 7606255332Scy} 7607255332Scy 7608255332Scy 7609255332Scy/* ------------------------------------------------------------------------ */ 7610255332Scy/* Function: nat_nextaddrinit */ 7611255332Scy/* Returns: int - 0 == success, else error number */ 7612255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7613255332Scy/* na(I) - NAT address information for generating new addr*/ 7614255332Scy/* initial(I) - flag indicating if it is the first call for */ 7615255332Scy/* this "na" structure. */ 7616255332Scy/* ifp(I) - network interface to derive address */ 7617255332Scy/* information from. */ 7618255332Scy/* */ 7619255332Scy/* This function is expected to be called in two scenarious: when a new NAT */ 7620255332Scy/* rule is loaded into the kernel and when the list of NAT rules is sync'd */ 7621255332Scy/* up with the valid network interfaces (possibly due to them changing.) */ 7622255332Scy/* To distinguish between these, the "initial" parameter is used. If it is */ 7623255332Scy/* 1 then this indicates the rule has just been reloaded and 0 for when we */ 7624255332Scy/* are updating information. This difference is important because in */ 7625255332Scy/* instances where we are not updating address information associated with */ 7626255332Scy/* a network interface, we don't want to disturb what the "next" address to */ 7627255332Scy/* come out of ipf_nat_nextaddr() will be. */ 7628255332Scy/* ------------------------------------------------------------------------ */ 7629255332Scystatic int 7630255332Scyipf_nat_nextaddrinit(softc, base, na, initial, ifp) 7631255332Scy ipf_main_softc_t *softc; 7632255332Scy char *base; 7633255332Scy nat_addr_t *na; 7634255332Scy int initial; 7635255332Scy void *ifp; 7636255332Scy{ 7637255332Scy 7638255332Scy switch (na->na_atype) 7639255332Scy { 7640255332Scy case FRI_LOOKUP : 7641255332Scy if (na->na_subtype == 0) { 7642255332Scy na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT, 7643255332Scy na->na_type, 7644255332Scy na->na_num, 7645255332Scy &na->na_func); 7646255332Scy } else if (na->na_subtype == 1) { 7647255332Scy na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT, 7648255332Scy na->na_type, 7649255332Scy base + na->na_num, 7650255332Scy &na->na_func); 7651255332Scy } 7652255332Scy if (na->na_func == NULL) { 7653255332Scy IPFERROR(60060); 7654255332Scy return ESRCH; 7655255332Scy } 7656255332Scy if (na->na_ptr == NULL) { 7657255332Scy IPFERROR(60056); 7658255332Scy return ESRCH; 7659255332Scy } 7660255332Scy break; 7661255332Scy 7662255332Scy case FRI_DYNAMIC : 7663255332Scy case FRI_BROADCAST : 7664255332Scy case FRI_NETWORK : 7665255332Scy case FRI_NETMASKED : 7666255332Scy case FRI_PEERADDR : 7667255332Scy if (ifp != NULL) 7668255332Scy (void )ipf_ifpaddr(softc, 4, na->na_atype, ifp, 7669255332Scy &na->na_addr[0], &na->na_addr[1]); 7670255332Scy break; 7671255332Scy 7672255332Scy case FRI_SPLIT : 7673255332Scy case FRI_RANGE : 7674255332Scy if (initial) 7675255332Scy na->na_nextip = ntohl(na->na_addr[0].in4.s_addr); 7676255332Scy break; 7677255332Scy 7678255332Scy case FRI_NONE : 7679255332Scy na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr; 7680255332Scy return 0; 7681255332Scy 7682255332Scy case FRI_NORMAL : 7683255332Scy na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr; 7684255332Scy break; 7685255332Scy 7686255332Scy default : 7687255332Scy IPFERROR(60054); 7688255332Scy return EINVAL; 7689255332Scy } 7690255332Scy 7691255332Scy if (initial && (na->na_atype == FRI_NORMAL)) { 7692255332Scy if (na->na_addr[0].in4.s_addr == 0) { 7693255332Scy if ((na->na_addr[1].in4.s_addr == 0xffffffff) || 7694255332Scy (na->na_addr[1].in4.s_addr == 0)) { 7695255332Scy return 0; 7696255332Scy } 7697255332Scy } 7698255332Scy 7699255332Scy if (na->na_addr[1].in4.s_addr == 0xffffffff) { 7700255332Scy na->na_nextip = ntohl(na->na_addr[0].in4.s_addr); 7701255332Scy } else { 7702255332Scy na->na_nextip = ntohl(na->na_addr[0].in4.s_addr) + 1; 7703255332Scy } 7704255332Scy } 7705255332Scy 7706255332Scy return 0; 7707255332Scy} 7708255332Scy 7709255332Scy 7710255332Scy/* ------------------------------------------------------------------------ */ 7711255332Scy/* Function: ipf_nat_matchflush */ 7712255332Scy/* Returns: int - -1 == error, 0 == success */ 7713255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7714255332Scy/* softn(I) - pointer to NAT context structure */ 7715255332Scy/* nat(I) - pointer to current NAT session */ 7716255332Scy/* */ 7717255332Scy/* ------------------------------------------------------------------------ */ 7718255332Scystatic int 7719255332Scyipf_nat_matchflush(softc, softn, data) 7720255332Scy ipf_main_softc_t *softc; 7721255332Scy ipf_nat_softc_t *softn; 7722255332Scy caddr_t data; 7723255332Scy{ 7724255332Scy int *array, flushed, error; 7725255332Scy nat_t *nat, *natnext; 7726255332Scy ipfobj_t obj; 7727255332Scy 7728255332Scy error = ipf_matcharray_load(softc, data, &obj, &array); 7729255332Scy if (error != 0) 7730255332Scy return error; 7731255332Scy 7732255332Scy flushed = 0; 7733255332Scy 7734255332Scy for (nat = softn->ipf_nat_instances; nat != NULL; nat = natnext) { 7735255332Scy natnext = nat->nat_next; 7736255332Scy if (ipf_nat_matcharray(nat, array, softc->ipf_ticks) == 0) { 7737255332Scy ipf_nat_delete(softc, nat, NL_FLUSH); 7738255332Scy flushed++; 7739255332Scy } 7740255332Scy } 7741255332Scy 7742255332Scy obj.ipfo_retval = flushed; 7743255332Scy error = BCOPYOUT(&obj, data, sizeof(obj)); 7744255332Scy 7745255332Scy KFREES(array, array[0] * sizeof(*array)); 7746255332Scy 7747255332Scy return error; 7748255332Scy} 7749255332Scy 7750255332Scy 7751255332Scy/* ------------------------------------------------------------------------ */ 7752255332Scy/* Function: ipf_nat_matcharray */ 7753255332Scy/* Returns: int - -1 == error, 0 == success */ 7754255332Scy/* Parameters: fin(I) - pointer to packet information */ 7755255332Scy/* nat(I) - pointer to current NAT session */ 7756255332Scy/* */ 7757255332Scy/* ------------------------------------------------------------------------ */ 7758255332Scystatic int 7759255332Scyipf_nat_matcharray(nat, array, ticks) 7760255332Scy nat_t *nat; 7761255332Scy int *array; 7762255332Scy u_long ticks; 7763255332Scy{ 7764255332Scy int i, n, *x, e, p; 7765255332Scy 7766255332Scy e = 0; 7767255332Scy n = array[0]; 7768255332Scy x = array + 1; 7769255332Scy 7770255332Scy for (; n > 0; x += 3 + x[2]) { 7771255332Scy if (x[0] == IPF_EXP_END) 7772255332Scy break; 7773255332Scy e = 0; 7774255332Scy 7775255332Scy n -= x[2] + 3; 7776255332Scy if (n < 0) 7777255332Scy break; 7778255332Scy 7779255332Scy p = x[0] >> 16; 7780255332Scy if (p != 0 && p != nat->nat_pr[1]) 7781255332Scy break; 7782255332Scy 7783255332Scy switch (x[0]) 7784255332Scy { 7785255332Scy case IPF_EXP_IP_PR : 7786255332Scy for (i = 0; !e && i < x[2]; i++) { 7787255332Scy e |= (nat->nat_pr[1] == x[i + 3]); 7788255332Scy } 7789255332Scy break; 7790255332Scy 7791255332Scy case IPF_EXP_IP_SRCADDR : 7792255332Scy if (nat->nat_v[0] == 4) { 7793255332Scy for (i = 0; !e && i < x[2]; i++) { 7794255332Scy e |= ((nat->nat_osrcaddr & x[i + 4]) == 7795255332Scy x[i + 3]); 7796255332Scy } 7797255332Scy } 7798255332Scy if (nat->nat_v[1] == 4) { 7799255332Scy for (i = 0; !e && i < x[2]; i++) { 7800255332Scy e |= ((nat->nat_nsrcaddr & x[i + 4]) == 7801255332Scy x[i + 3]); 7802255332Scy } 7803255332Scy } 7804255332Scy break; 7805255332Scy 7806255332Scy case IPF_EXP_IP_DSTADDR : 7807255332Scy if (nat->nat_v[0] == 4) { 7808255332Scy for (i = 0; !e && i < x[2]; i++) { 7809255332Scy e |= ((nat->nat_odstaddr & x[i + 4]) == 7810255332Scy x[i + 3]); 7811255332Scy } 7812255332Scy } 7813255332Scy if (nat->nat_v[1] == 4) { 7814255332Scy for (i = 0; !e && i < x[2]; i++) { 7815255332Scy e |= ((nat->nat_ndstaddr & x[i + 4]) == 7816255332Scy x[i + 3]); 7817255332Scy } 7818255332Scy } 7819255332Scy break; 7820255332Scy 7821255332Scy case IPF_EXP_IP_ADDR : 7822255332Scy for (i = 0; !e && i < x[2]; i++) { 7823255332Scy if (nat->nat_v[0] == 4) { 7824255332Scy e |= ((nat->nat_osrcaddr & x[i + 4]) == 7825255332Scy x[i + 3]); 7826255332Scy } 7827255332Scy if (nat->nat_v[1] == 4) { 7828255332Scy e |= ((nat->nat_nsrcaddr & x[i + 4]) == 7829255332Scy x[i + 3]); 7830255332Scy } 7831255332Scy if (nat->nat_v[0] == 4) { 7832255332Scy e |= ((nat->nat_odstaddr & x[i + 4]) == 7833255332Scy x[i + 3]); 7834255332Scy } 7835255332Scy if (nat->nat_v[1] == 4) { 7836255332Scy e |= ((nat->nat_ndstaddr & x[i + 4]) == 7837255332Scy x[i + 3]); 7838255332Scy } 7839255332Scy } 7840255332Scy break; 7841255332Scy 7842255332Scy#ifdef USE_INET6 7843255332Scy case IPF_EXP_IP6_SRCADDR : 7844255332Scy if (nat->nat_v[0] == 6) { 7845255332Scy for (i = 0; !e && i < x[3]; i++) { 7846255332Scy e |= IP6_MASKEQ(&nat->nat_osrc6, 7847255332Scy x + i + 7, x + i + 3); 7848255332Scy } 7849255332Scy } 7850255332Scy if (nat->nat_v[1] == 6) { 7851255332Scy for (i = 0; !e && i < x[3]; i++) { 7852255332Scy e |= IP6_MASKEQ(&nat->nat_nsrc6, 7853255332Scy x + i + 7, x + i + 3); 7854255332Scy } 7855255332Scy } 7856255332Scy break; 7857255332Scy 7858255332Scy case IPF_EXP_IP6_DSTADDR : 7859255332Scy if (nat->nat_v[0] == 6) { 7860255332Scy for (i = 0; !e && i < x[3]; i++) { 7861255332Scy e |= IP6_MASKEQ(&nat->nat_odst6, 7862255332Scy x + i + 7, 7863255332Scy x + i + 3); 7864255332Scy } 7865255332Scy } 7866255332Scy if (nat->nat_v[1] == 6) { 7867255332Scy for (i = 0; !e && i < x[3]; i++) { 7868255332Scy e |= IP6_MASKEQ(&nat->nat_ndst6, 7869255332Scy x + i + 7, 7870255332Scy x + i + 3); 7871255332Scy } 7872255332Scy } 7873255332Scy break; 7874255332Scy 7875255332Scy case IPF_EXP_IP6_ADDR : 7876255332Scy for (i = 0; !e && i < x[3]; i++) { 7877255332Scy if (nat->nat_v[0] == 6) { 7878255332Scy e |= IP6_MASKEQ(&nat->nat_osrc6, 7879255332Scy x + i + 7, 7880255332Scy x + i + 3); 7881255332Scy } 7882255332Scy if (nat->nat_v[0] == 6) { 7883255332Scy e |= IP6_MASKEQ(&nat->nat_odst6, 7884255332Scy x + i + 7, 7885255332Scy x + i + 3); 7886255332Scy } 7887255332Scy if (nat->nat_v[1] == 6) { 7888255332Scy e |= IP6_MASKEQ(&nat->nat_nsrc6, 7889255332Scy x + i + 7, 7890255332Scy x + i + 3); 7891255332Scy } 7892255332Scy if (nat->nat_v[1] == 6) { 7893255332Scy e |= IP6_MASKEQ(&nat->nat_ndst6, 7894255332Scy x + i + 7, 7895255332Scy x + i + 3); 7896255332Scy } 7897255332Scy } 7898255332Scy break; 7899255332Scy#endif 7900255332Scy 7901255332Scy case IPF_EXP_UDP_PORT : 7902255332Scy case IPF_EXP_TCP_PORT : 7903255332Scy for (i = 0; !e && i < x[2]; i++) { 7904255332Scy e |= (nat->nat_nsport == x[i + 3]) || 7905255332Scy (nat->nat_ndport == x[i + 3]); 7906255332Scy } 7907255332Scy break; 7908255332Scy 7909255332Scy case IPF_EXP_UDP_SPORT : 7910255332Scy case IPF_EXP_TCP_SPORT : 7911255332Scy for (i = 0; !e && i < x[2]; i++) { 7912255332Scy e |= (nat->nat_nsport == x[i + 3]); 7913255332Scy } 7914255332Scy break; 7915255332Scy 7916255332Scy case IPF_EXP_UDP_DPORT : 7917255332Scy case IPF_EXP_TCP_DPORT : 7918255332Scy for (i = 0; !e && i < x[2]; i++) { 7919255332Scy e |= (nat->nat_ndport == x[i + 3]); 7920255332Scy } 7921255332Scy break; 7922255332Scy 7923255332Scy case IPF_EXP_TCP_STATE : 7924255332Scy for (i = 0; !e && i < x[2]; i++) { 7925255332Scy e |= (nat->nat_tcpstate[0] == x[i + 3]) || 7926255332Scy (nat->nat_tcpstate[1] == x[i + 3]); 7927255332Scy } 7928255332Scy break; 7929255332Scy 7930255332Scy case IPF_EXP_IDLE_GT : 7931255332Scy e |= (ticks - nat->nat_touched > x[3]); 7932255332Scy break; 7933255332Scy } 7934255332Scy e ^= x[1]; 7935255332Scy 7936255332Scy if (!e) 7937255332Scy break; 7938255332Scy } 7939255332Scy 7940255332Scy return e; 7941255332Scy} 7942255332Scy 7943255332Scy 7944255332Scy/* ------------------------------------------------------------------------ */ 7945255332Scy/* Function: ipf_nat_gettable */ 7946172776Sdarrenr/* Returns: int - 0 = success, else error */ 7947255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7948255332Scy/* softn(I) - pointer to NAT context structure */ 7949255332Scy/* data(I) - pointer to ioctl data */ 7950172776Sdarrenr/* */ 7951172776Sdarrenr/* This function handles ioctl requests for tables of nat information. */ 7952172776Sdarrenr/* At present the only table it deals with is the hash bucket statistics. */ 7953172776Sdarrenr/* ------------------------------------------------------------------------ */ 7954255332Scystatic int 7955255332Scyipf_nat_gettable(softc, softn, data) 7956255332Scy ipf_main_softc_t *softc; 7957255332Scy ipf_nat_softc_t *softn; 7958255332Scy char *data; 7959172776Sdarrenr{ 7960172776Sdarrenr ipftable_t table; 7961172776Sdarrenr int error; 7962172776Sdarrenr 7963255332Scy error = ipf_inobj(softc, data, NULL, &table, IPFOBJ_GTABLE); 7964172776Sdarrenr if (error != 0) 7965172776Sdarrenr return error; 7966172776Sdarrenr 7967172776Sdarrenr switch (table.ita_type) 7968172776Sdarrenr { 7969172776Sdarrenr case IPFTABLE_BUCKETS_NATIN : 7970255332Scy error = COPYOUT(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, 7971255332Scy table.ita_table, 7972255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 7973172776Sdarrenr break; 7974172776Sdarrenr 7975172776Sdarrenr case IPFTABLE_BUCKETS_NATOUT : 7976255332Scy error = COPYOUT(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, 7977255332Scy table.ita_table, 7978255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 7979172776Sdarrenr break; 7980172776Sdarrenr 7981172776Sdarrenr default : 7982255332Scy IPFERROR(60058); 7983172776Sdarrenr return EINVAL; 7984172776Sdarrenr } 7985172776Sdarrenr 7986172776Sdarrenr if (error != 0) { 7987255332Scy IPFERROR(60059); 7988172776Sdarrenr error = EFAULT; 7989172776Sdarrenr } 7990172776Sdarrenr return error; 7991172776Sdarrenr} 7992255332Scy 7993255332Scy 7994255332Scy/* ------------------------------------------------------------------------ */ 7995255332Scy/* Function: ipf_nat_settimeout */ 7996255332Scy/* Returns: int - 0 = success, else failure */ 7997255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7998255332Scy/* t(I) - pointer to tunable */ 7999255332Scy/* p(I) - pointer to new tuning data */ 8000255332Scy/* */ 8001255332Scy/* Apply the timeout change to the NAT timeout queues. */ 8002255332Scy/* ------------------------------------------------------------------------ */ 8003255332Scyint 8004255332Scyipf_nat_settimeout(softc, t, p) 8005255332Scy struct ipf_main_softc_s *softc; 8006255332Scy ipftuneable_t *t; 8007255332Scy ipftuneval_t *p; 8008255332Scy{ 8009255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 8010255332Scy 8011255332Scy if (!strncmp(t->ipft_name, "tcp_", 4)) 8012255332Scy return ipf_settimeout_tcp(t, p, softn->ipf_nat_tcptq); 8013255332Scy 8014255332Scy if (!strcmp(t->ipft_name, "udp_timeout")) { 8015255332Scy ipf_apply_timeout(&softn->ipf_nat_udptq, p->ipftu_int); 8016255332Scy } else if (!strcmp(t->ipft_name, "udp_ack_timeout")) { 8017255332Scy ipf_apply_timeout(&softn->ipf_nat_udpacktq, p->ipftu_int); 8018255332Scy } else if (!strcmp(t->ipft_name, "icmp_timeout")) { 8019255332Scy ipf_apply_timeout(&softn->ipf_nat_icmptq, p->ipftu_int); 8020255332Scy } else if (!strcmp(t->ipft_name, "icmp_ack_timeout")) { 8021255332Scy ipf_apply_timeout(&softn->ipf_nat_icmpacktq, p->ipftu_int); 8022255332Scy } else if (!strcmp(t->ipft_name, "ip_timeout")) { 8023255332Scy ipf_apply_timeout(&softn->ipf_nat_iptq, p->ipftu_int); 8024255332Scy } else { 8025255332Scy IPFERROR(60062); 8026255332Scy return ESRCH; 8027255332Scy } 8028255332Scy return 0; 8029255332Scy} 8030255332Scy 8031255332Scy 8032255332Scy/* ------------------------------------------------------------------------ */ 8033255332Scy/* Function: ipf_nat_rehash */ 8034255332Scy/* Returns: int - 0 = success, else failure */ 8035255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8036255332Scy/* t(I) - pointer to tunable */ 8037255332Scy/* p(I) - pointer to new tuning data */ 8038255332Scy/* */ 8039255332Scy/* To change the size of the basic NAT table, we need to first allocate the */ 8040255332Scy/* new tables (lest it fails and we've got nowhere to store all of the NAT */ 8041255332Scy/* sessions currently active) and then walk through the entire list and */ 8042255332Scy/* insert them into the table. There are two tables here: an inbound one */ 8043255332Scy/* and an outbound one. Each NAT entry goes into each table once. */ 8044255332Scy/* ------------------------------------------------------------------------ */ 8045255332Scyint 8046255332Scyipf_nat_rehash(softc, t, p) 8047255332Scy ipf_main_softc_t *softc; 8048255332Scy ipftuneable_t *t; 8049255332Scy ipftuneval_t *p; 8050255332Scy{ 8051255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 8052255332Scy nat_t **newtab[2], *nat, **natp; 8053255332Scy u_int *bucketlens[2]; 8054255332Scy u_int maxbucket; 8055255332Scy u_int newsize; 8056255332Scy int error; 8057255332Scy u_int hv; 8058255332Scy int i; 8059255332Scy 8060255332Scy newsize = p->ipftu_int; 8061255332Scy /* 8062255332Scy * In case there is nothing to do... 8063255332Scy */ 8064255332Scy if (newsize == softn->ipf_nat_table_sz) 8065255332Scy return 0; 8066255332Scy 8067255332Scy newtab[0] = NULL; 8068255332Scy newtab[1] = NULL; 8069255332Scy bucketlens[0] = NULL; 8070255332Scy bucketlens[1] = NULL; 8071255332Scy /* 8072255332Scy * 4 tables depend on the NAT table size: the inbound looking table, 8073255332Scy * the outbound lookup table and the hash chain length for each. 8074255332Scy */ 8075255332Scy KMALLOCS(newtab[0], nat_t **, newsize * sizeof(nat_t *)); 8076255332Scy if (newtab == NULL) { 8077255332Scy error = 60063; 8078255332Scy goto badrehash; 8079255332Scy } 8080255332Scy 8081255332Scy KMALLOCS(newtab[1], nat_t **, newsize * sizeof(nat_t *)); 8082255332Scy if (newtab == NULL) { 8083255332Scy error = 60064; 8084255332Scy goto badrehash; 8085255332Scy } 8086255332Scy 8087255332Scy KMALLOCS(bucketlens[0], u_int *, newsize * sizeof(u_int)); 8088255332Scy if (bucketlens[0] == NULL) { 8089255332Scy error = 60065; 8090255332Scy goto badrehash; 8091255332Scy } 8092255332Scy 8093255332Scy KMALLOCS(bucketlens[1], u_int *, newsize * sizeof(u_int)); 8094255332Scy if (bucketlens[1] == NULL) { 8095255332Scy error = 60066; 8096255332Scy goto badrehash; 8097255332Scy } 8098255332Scy 8099255332Scy /* 8100255332Scy * Recalculate the maximum length based on the new size. 8101255332Scy */ 8102255332Scy for (maxbucket = 0, i = newsize; i > 0; i >>= 1) 8103255332Scy maxbucket++; 8104255332Scy maxbucket *= 2; 8105255332Scy 8106255332Scy bzero((char *)newtab[0], newsize * sizeof(nat_t *)); 8107255332Scy bzero((char *)newtab[1], newsize * sizeof(nat_t *)); 8108255332Scy bzero((char *)bucketlens[0], newsize * sizeof(u_int)); 8109255332Scy bzero((char *)bucketlens[1], newsize * sizeof(u_int)); 8110255332Scy 8111255332Scy WRITE_ENTER(&softc->ipf_nat); 8112255332Scy 8113255332Scy if (softn->ipf_nat_table[0] != NULL) { 8114255332Scy KFREES(softn->ipf_nat_table[0], 8115255332Scy softn->ipf_nat_table_sz * 8116255332Scy sizeof(*softn->ipf_nat_table[0])); 8117255332Scy } 8118255332Scy softn->ipf_nat_table[0] = newtab[0]; 8119255332Scy 8120255332Scy if (softn->ipf_nat_table[1] != NULL) { 8121255332Scy KFREES(softn->ipf_nat_table[1], 8122255332Scy softn->ipf_nat_table_sz * 8123255332Scy sizeof(*softn->ipf_nat_table[1])); 8124255332Scy } 8125255332Scy softn->ipf_nat_table[1] = newtab[1]; 8126255332Scy 8127255332Scy if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) { 8128255332Scy KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, 8129255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 8130255332Scy } 8131255332Scy softn->ipf_nat_stats.ns_side[0].ns_bucketlen = bucketlens[0]; 8132255332Scy 8133255332Scy if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) { 8134255332Scy KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, 8135255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 8136255332Scy } 8137255332Scy softn->ipf_nat_stats.ns_side[1].ns_bucketlen = bucketlens[1]; 8138255332Scy 8139255332Scy#ifdef USE_INET6 8140255332Scy if (softn->ipf_nat_stats.ns_side6[0].ns_bucketlen != NULL) { 8141255332Scy KFREES(softn->ipf_nat_stats.ns_side6[0].ns_bucketlen, 8142255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 8143255332Scy } 8144255332Scy softn->ipf_nat_stats.ns_side6[0].ns_bucketlen = bucketlens[0]; 8145255332Scy 8146255332Scy if (softn->ipf_nat_stats.ns_side6[1].ns_bucketlen != NULL) { 8147255332Scy KFREES(softn->ipf_nat_stats.ns_side6[1].ns_bucketlen, 8148255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 8149255332Scy } 8150255332Scy softn->ipf_nat_stats.ns_side6[1].ns_bucketlen = bucketlens[1]; 8151255332Scy#endif 8152255332Scy 8153255332Scy softn->ipf_nat_maxbucket = maxbucket; 8154255332Scy softn->ipf_nat_table_sz = newsize; 8155255332Scy /* 8156255332Scy * Walk through the entire list of NAT table entries and put them 8157255332Scy * in the new NAT table, somewhere. Because we have a new table, 8158255332Scy * we need to restart the counter of how many chains are in use. 8159255332Scy */ 8160255332Scy softn->ipf_nat_stats.ns_side[0].ns_inuse = 0; 8161255332Scy softn->ipf_nat_stats.ns_side[1].ns_inuse = 0; 8162255332Scy#ifdef USE_INET6 8163255332Scy softn->ipf_nat_stats.ns_side6[0].ns_inuse = 0; 8164255332Scy softn->ipf_nat_stats.ns_side6[1].ns_inuse = 0; 8165255332Scy#endif 8166255332Scy 8167255332Scy for (nat = softn->ipf_nat_instances; nat != NULL; nat = nat->nat_next) { 8168255332Scy nat->nat_hnext[0] = NULL; 8169255332Scy nat->nat_phnext[0] = NULL; 8170255332Scy hv = nat->nat_hv[0] % softn->ipf_nat_table_sz; 8171255332Scy 8172255332Scy natp = &softn->ipf_nat_table[0][hv]; 8173255332Scy if (*natp) { 8174255332Scy (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 8175255332Scy } else { 8176255332Scy NBUMPSIDE(0, ns_inuse); 8177255332Scy } 8178255332Scy nat->nat_phnext[0] = natp; 8179255332Scy nat->nat_hnext[0] = *natp; 8180255332Scy *natp = nat; 8181255332Scy NBUMPSIDE(0, ns_bucketlen[hv]); 8182255332Scy 8183255332Scy nat->nat_hnext[1] = NULL; 8184255332Scy nat->nat_phnext[1] = NULL; 8185255332Scy hv = nat->nat_hv[1] % softn->ipf_nat_table_sz; 8186255332Scy 8187255332Scy natp = &softn->ipf_nat_table[1][hv]; 8188255332Scy if (*natp) { 8189255332Scy (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 8190255332Scy } else { 8191255332Scy NBUMPSIDE(1, ns_inuse); 8192255332Scy } 8193255332Scy nat->nat_phnext[1] = natp; 8194255332Scy nat->nat_hnext[1] = *natp; 8195255332Scy *natp = nat; 8196255332Scy NBUMPSIDE(1, ns_bucketlen[hv]); 8197255332Scy } 8198255332Scy RWLOCK_EXIT(&softc->ipf_nat); 8199255332Scy 8200255332Scy return 0; 8201255332Scy 8202255332Scybadrehash: 8203255332Scy if (bucketlens[1] != NULL) { 8204255332Scy KFREES(bucketlens[0], newsize * sizeof(u_int)); 8205255332Scy } 8206255332Scy if (bucketlens[0] != NULL) { 8207255332Scy KFREES(bucketlens[0], newsize * sizeof(u_int)); 8208255332Scy } 8209255332Scy if (newtab[0] != NULL) { 8210255332Scy KFREES(newtab[0], newsize * sizeof(nat_t *)); 8211255332Scy } 8212255332Scy if (newtab[1] != NULL) { 8213255332Scy KFREES(newtab[1], newsize * sizeof(nat_t *)); 8214255332Scy } 8215255332Scy IPFERROR(error); 8216255332Scy return ENOMEM; 8217255332Scy} 8218255332Scy 8219255332Scy 8220255332Scy/* ------------------------------------------------------------------------ */ 8221255332Scy/* Function: ipf_nat_rehash_rules */ 8222255332Scy/* Returns: int - 0 = success, else failure */ 8223255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8224255332Scy/* t(I) - pointer to tunable */ 8225255332Scy/* p(I) - pointer to new tuning data */ 8226255332Scy/* */ 8227255332Scy/* All of the NAT rules hang off of a hash table that is searched with a */ 8228255332Scy/* hash on address after the netmask is applied. There is a different table*/ 8229255332Scy/* for both inbound rules (rdr) and outbound (map.) The resizing will only */ 8230255332Scy/* affect one of these two tables. */ 8231255332Scy/* ------------------------------------------------------------------------ */ 8232255332Scyint 8233255332Scyipf_nat_rehash_rules(softc, t, p) 8234255332Scy ipf_main_softc_t *softc; 8235255332Scy ipftuneable_t *t; 8236255332Scy ipftuneval_t *p; 8237255332Scy{ 8238255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 8239255332Scy ipnat_t **newtab, *np, ***old, **npp; 8240255332Scy u_int newsize; 8241255332Scy u_int mask; 8242255332Scy u_int hv; 8243255332Scy 8244255332Scy newsize = p->ipftu_int; 8245255332Scy /* 8246255332Scy * In case there is nothing to do... 8247255332Scy */ 8248255332Scy if (newsize == *t->ipft_pint) 8249255332Scy return 0; 8250255332Scy 8251255332Scy /* 8252255332Scy * All inbound rules have the NAT_REDIRECT bit set in in_redir and 8253255332Scy * all outbound rules have either NAT_MAP or MAT_MAPBLK set. 8254255332Scy * This if statement allows for some more generic code to be below, 8255255332Scy * rather than two huge gobs of code that almost do the same thing. 8256255332Scy */ 8257255332Scy if (t->ipft_pint == &softn->ipf_nat_rdrrules_sz) { 8258255332Scy old = &softn->ipf_nat_rdr_rules; 8259255332Scy mask = NAT_REDIRECT; 8260255332Scy } else { 8261255332Scy old = &softn->ipf_nat_map_rules; 8262255332Scy mask = NAT_MAP|NAT_MAPBLK; 8263255332Scy } 8264255332Scy 8265255332Scy KMALLOCS(newtab, ipnat_t **, newsize * sizeof(ipnat_t *)); 8266255332Scy if (newtab == NULL) { 8267255332Scy IPFERROR(60067); 8268255332Scy return ENOMEM; 8269255332Scy } 8270255332Scy 8271255332Scy bzero((char *)newtab, newsize * sizeof(ipnat_t *)); 8272255332Scy 8273255332Scy WRITE_ENTER(&softc->ipf_nat); 8274255332Scy 8275255332Scy if (*old != NULL) { 8276255332Scy KFREES(*old, *t->ipft_pint * sizeof(ipnat_t **)); 8277255332Scy } 8278255332Scy *old = newtab; 8279255332Scy *t->ipft_pint = newsize; 8280255332Scy 8281255332Scy for (np = softn->ipf_nat_list; np != NULL; np = np->in_next) { 8282255332Scy if ((np->in_redir & mask) == 0) 8283255332Scy continue; 8284255332Scy 8285255332Scy if (np->in_redir & NAT_REDIRECT) { 8286255332Scy np->in_rnext = NULL; 8287255332Scy hv = np->in_hv[0] % newsize; 8288255332Scy for (npp = newtab + hv; *npp != NULL; ) 8289255332Scy npp = &(*npp)->in_rnext; 8290255332Scy np->in_prnext = npp; 8291255332Scy *npp = np; 8292255332Scy } 8293255332Scy if (np->in_redir & NAT_MAP) { 8294255332Scy np->in_mnext = NULL; 8295255332Scy hv = np->in_hv[1] % newsize; 8296255332Scy for (npp = newtab + hv; *npp != NULL; ) 8297255332Scy npp = &(*npp)->in_mnext; 8298255332Scy np->in_pmnext = npp; 8299255332Scy *npp = np; 8300255332Scy } 8301255332Scy 8302255332Scy } 8303255332Scy RWLOCK_EXIT(&softc->ipf_nat); 8304255332Scy 8305255332Scy return 0; 8306255332Scy} 8307255332Scy 8308255332Scy 8309255332Scy/* ------------------------------------------------------------------------ */ 8310255332Scy/* Function: ipf_nat_hostmap_rehash */ 8311255332Scy/* Returns: int - 0 = success, else failure */ 8312255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8313255332Scy/* t(I) - pointer to tunable */ 8314255332Scy/* p(I) - pointer to new tuning data */ 8315255332Scy/* */ 8316255332Scy/* Allocate and populate a new hash table that will contain a reference to */ 8317255332Scy/* all of the active IP# translations currently in place. */ 8318255332Scy/* ------------------------------------------------------------------------ */ 8319255332Scyint 8320255332Scyipf_nat_hostmap_rehash(softc, t, p) 8321255332Scy ipf_main_softc_t *softc; 8322255332Scy ipftuneable_t *t; 8323255332Scy ipftuneval_t *p; 8324255332Scy{ 8325255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 8326255332Scy hostmap_t *hm, **newtab; 8327255332Scy u_int newsize; 8328255332Scy u_int hv; 8329255332Scy 8330255332Scy newsize = p->ipftu_int; 8331255332Scy /* 8332255332Scy * In case there is nothing to do... 8333255332Scy */ 8334255332Scy if (newsize == *t->ipft_pint) 8335255332Scy return 0; 8336255332Scy 8337255332Scy KMALLOCS(newtab, hostmap_t **, newsize * sizeof(hostmap_t *)); 8338255332Scy if (newtab == NULL) { 8339255332Scy IPFERROR(60068); 8340255332Scy return ENOMEM; 8341255332Scy } 8342255332Scy 8343255332Scy bzero((char *)newtab, newsize * sizeof(hostmap_t *)); 8344255332Scy 8345255332Scy WRITE_ENTER(&softc->ipf_nat); 8346255332Scy if (softn->ipf_hm_maptable != NULL) { 8347255332Scy KFREES(softn->ipf_hm_maptable, 8348255332Scy softn->ipf_nat_hostmap_sz * sizeof(hostmap_t *)); 8349255332Scy } 8350255332Scy softn->ipf_hm_maptable = newtab; 8351255332Scy softn->ipf_nat_hostmap_sz = newsize; 8352255332Scy 8353255332Scy for (hm = softn->ipf_hm_maplist; hm != NULL; hm = hm->hm_next) { 8354255332Scy hv = hm->hm_hv % softn->ipf_nat_hostmap_sz; 8355255332Scy hm->hm_hnext = softn->ipf_hm_maptable[hv]; 8356255332Scy hm->hm_phnext = softn->ipf_hm_maptable + hv; 8357255332Scy if (softn->ipf_hm_maptable[hv] != NULL) 8358255332Scy softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; 8359255332Scy softn->ipf_hm_maptable[hv] = hm; 8360255332Scy } 8361255332Scy RWLOCK_EXIT(&softc->ipf_nat); 8362255332Scy 8363255332Scy return 0; 8364255332Scy} 8365255332Scy 8366255332Scy 8367255332Scy/* ------------------------------------------------------------------------ */ 8368255332Scy/* Function: ipf_nat_add_tq */ 8369255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8370255332Scy/* */ 8371255332Scy/* ------------------------------------------------------------------------ */ 8372255332Scyipftq_t * 8373255332Scyipf_nat_add_tq(softc, ttl) 8374255332Scy ipf_main_softc_t *softc; 8375255332Scy int ttl; 8376255332Scy{ 8377255332Scy ipf_nat_softc_t *softs = softc->ipf_nat_soft; 8378255332Scy 8379255332Scy return ipf_addtimeoutqueue(softc, &softs->ipf_nat_utqe, ttl); 8380255332Scy} 8381255332Scy 8382255332Scy/* ------------------------------------------------------------------------ */ 8383255332Scy/* Function: ipf_nat_uncreate */ 8384255332Scy/* Returns: Nil */ 8385255332Scy/* Parameters: fin(I) - pointer to packet information */ 8386255332Scy/* */ 8387255332Scy/* This function is used to remove a NAT entry from the NAT table when we */ 8388255332Scy/* decide that the create was actually in error. It is thus assumed that */ 8389255332Scy/* fin_flx will have both FI_NATED and FI_NATNEW set. Because we're dealing */ 8390255332Scy/* with the translated packet (not the original), we have to reverse the */ 8391255332Scy/* lookup. Although doing the lookup is expensive (relatively speaking), it */ 8392255332Scy/* is not anticipated that this will be a frequent occurance for normal */ 8393255332Scy/* traffic patterns. */ 8394255332Scy/* ------------------------------------------------------------------------ */ 8395255332Scyvoid 8396255332Scyipf_nat_uncreate(fin) 8397255332Scy fr_info_t *fin; 8398255332Scy{ 8399255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 8400255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 8401255332Scy int nflags; 8402255332Scy nat_t *nat; 8403255332Scy 8404255332Scy switch (fin->fin_p) 8405255332Scy { 8406255332Scy case IPPROTO_TCP : 8407255332Scy nflags = IPN_TCP; 8408255332Scy break; 8409255332Scy case IPPROTO_UDP : 8410255332Scy nflags = IPN_UDP; 8411255332Scy break; 8412255332Scy default : 8413255332Scy nflags = 0; 8414255332Scy break; 8415255332Scy } 8416255332Scy 8417255332Scy WRITE_ENTER(&softc->ipf_nat); 8418255332Scy 8419255332Scy if (fin->fin_out == 0) { 8420255332Scy nat = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p, 8421255332Scy fin->fin_dst, fin->fin_src); 8422255332Scy } else { 8423255332Scy nat = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p, 8424255332Scy fin->fin_src, fin->fin_dst); 8425255332Scy } 8426255332Scy 8427255332Scy if (nat != NULL) { 8428255332Scy NBUMPSIDE(fin->fin_out, ns_uncreate[0]); 8429255332Scy ipf_nat_delete(softc, nat, NL_DESTROY); 8430255332Scy } else { 8431255332Scy NBUMPSIDE(fin->fin_out, ns_uncreate[1]); 8432255332Scy } 8433255332Scy 8434255332Scy RWLOCK_EXIT(&softc->ipf_nat); 8435255332Scy} 8436255332Scy 8437255332Scy 8438255332Scy/* ------------------------------------------------------------------------ */ 8439255332Scy/* Function: ipf_nat_cmp_rules */ 8440255332Scy/* Returns: int - 0 == success, else rules do not match. */ 8441255332Scy/* Parameters: n1(I) - first rule to compare */ 8442255332Scy/* n2(I) - first rule to compare */ 8443255332Scy/* */ 8444255332Scy/* Compare two rules using pointers to each rule. A straight bcmp will not */ 8445255332Scy/* work as some fields (such as in_dst, in_pkts) actually do change once */ 8446255332Scy/* the rule has been loaded into the kernel. Whilst this function returns */ 8447255332Scy/* various non-zero returns, they're strictly to aid in debugging. Use of */ 8448255332Scy/* this function should simply care if the result is zero or not. */ 8449255332Scy/* ------------------------------------------------------------------------ */ 8450255332Scystatic int 8451255332Scyipf_nat_cmp_rules(n1, n2) 8452255332Scy ipnat_t *n1, *n2; 8453255332Scy{ 8454255332Scy if (n1->in_size != n2->in_size) 8455255332Scy return 1; 8456255332Scy 8457255332Scy if (bcmp((char *)&n1->in_v, (char *)&n2->in_v, 8458255332Scy offsetof(ipnat_t, in_ndst) - offsetof(ipnat_t, in_v)) != 0) 8459255332Scy return 2; 8460255332Scy 8461255332Scy if (bcmp((char *)&n1->in_tuc, (char *)&n2->in_tuc, 8462255332Scy n1->in_size - offsetof(ipnat_t, in_tuc)) != 0) 8463255332Scy return 3; 8464255332Scy if (n1->in_ndst.na_atype != n2->in_ndst.na_atype) 8465255332Scy return 5; 8466255332Scy if (n1->in_ndst.na_function != n2->in_ndst.na_function) 8467255332Scy return 6; 8468255332Scy if (bcmp((char *)&n1->in_ndst.na_addr, (char *)&n2->in_ndst.na_addr, 8469255332Scy sizeof(n1->in_ndst.na_addr))) 8470255332Scy return 7; 8471255332Scy if (n1->in_nsrc.na_atype != n2->in_nsrc.na_atype) 8472255332Scy return 8; 8473255332Scy if (n1->in_nsrc.na_function != n2->in_nsrc.na_function) 8474255332Scy return 9; 8475255332Scy if (bcmp((char *)&n1->in_nsrc.na_addr, (char *)&n2->in_nsrc.na_addr, 8476255332Scy sizeof(n1->in_nsrc.na_addr))) 8477255332Scy return 10; 8478255332Scy if (n1->in_odst.na_atype != n2->in_odst.na_atype) 8479255332Scy return 11; 8480255332Scy if (n1->in_odst.na_function != n2->in_odst.na_function) 8481255332Scy return 12; 8482255332Scy if (bcmp((char *)&n1->in_odst.na_addr, (char *)&n2->in_odst.na_addr, 8483255332Scy sizeof(n1->in_odst.na_addr))) 8484255332Scy return 13; 8485255332Scy if (n1->in_osrc.na_atype != n2->in_osrc.na_atype) 8486255332Scy return 14; 8487255332Scy if (n1->in_osrc.na_function != n2->in_osrc.na_function) 8488255332Scy return 15; 8489255332Scy if (bcmp((char *)&n1->in_osrc.na_addr, (char *)&n2->in_osrc.na_addr, 8490255332Scy sizeof(n1->in_osrc.na_addr))) 8491255332Scy return 16; 8492255332Scy return 0; 8493255332Scy} 8494255332Scy 8495255332Scy 8496255332Scy/* ------------------------------------------------------------------------ */ 8497255332Scy/* Function: ipf_nat_rule_init */ 8498255332Scy/* Returns: int - 0 == success, else rules do not match. */ 8499255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8500255332Scy/* softn(I) - pointer to NAT context structure */ 8501255332Scy/* n(I) - first rule to compare */ 8502255332Scy/* */ 8503255332Scy/* ------------------------------------------------------------------------ */ 8504255332Scystatic int 8505255332Scyipf_nat_rule_init(softc, softn, n) 8506255332Scy ipf_main_softc_t *softc; 8507255332Scy ipf_nat_softc_t *softn; 8508255332Scy ipnat_t *n; 8509255332Scy{ 8510255332Scy int error = 0; 8511255332Scy 8512255332Scy if ((n->in_flags & IPN_SIPRANGE) != 0) 8513255332Scy n->in_nsrcatype = FRI_RANGE; 8514255332Scy 8515255332Scy if ((n->in_flags & IPN_DIPRANGE) != 0) 8516255332Scy n->in_ndstatype = FRI_RANGE; 8517255332Scy 8518255332Scy if ((n->in_flags & IPN_SPLIT) != 0) 8519255332Scy n->in_ndstatype = FRI_SPLIT; 8520255332Scy 8521255332Scy if ((n->in_redir & (NAT_MAP|NAT_REWRITE|NAT_DIVERTUDP)) != 0) 8522255332Scy n->in_spnext = n->in_spmin; 8523255332Scy 8524255332Scy if ((n->in_redir & (NAT_REWRITE|NAT_DIVERTUDP)) != 0) { 8525255332Scy n->in_dpnext = n->in_dpmin; 8526255332Scy } else if (n->in_redir == NAT_REDIRECT) { 8527255332Scy n->in_dpnext = n->in_dpmin; 8528255332Scy } 8529255332Scy 8530255332Scy n->in_stepnext = 0; 8531255332Scy 8532255332Scy switch (n->in_v[0]) 8533255332Scy { 8534255332Scy case 4 : 8535255332Scy error = ipf_nat_ruleaddrinit(softc, softn, n); 8536255332Scy if (error != 0) 8537255332Scy return error; 8538255332Scy break; 8539255332Scy#ifdef USE_INET6 8540255332Scy case 6 : 8541255332Scy error = ipf_nat6_ruleaddrinit(softc, softn, n); 8542255332Scy if (error != 0) 8543255332Scy return error; 8544255332Scy break; 8545255332Scy#endif 8546255332Scy default : 8547255332Scy break; 8548255332Scy } 8549255332Scy 8550255332Scy if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) { 8551255332Scy /* 8552255332Scy * Prerecord whether or not the destination of the divert 8553255332Scy * is local or not to the interface the packet is going 8554255332Scy * to be sent out. 8555255332Scy */ 8556255332Scy n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1], 8557255332Scy n->in_ifps[1], &n->in_ndstip6); 8558255332Scy } 8559255332Scy 8560255332Scy return error; 8561255332Scy} 8562255332Scy 8563255332Scy 8564255332Scy/* ------------------------------------------------------------------------ */ 8565255332Scy/* Function: ipf_nat_rule_fini */ 8566255332Scy/* Returns: int - 0 == success, else rules do not match. */ 8567255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8568255332Scy/* n(I) - rule to work on */ 8569255332Scy/* */ 8570255332Scy/* This function is used to release any objects that were referenced during */ 8571255332Scy/* the rule initialisation. This is useful both when free'ing the rule and */ 8572255332Scy/* when handling ioctls that need to initialise these fields but not */ 8573255332Scy/* actually use them after the ioctl processing has finished. */ 8574255332Scy/* ------------------------------------------------------------------------ */ 8575255332Scystatic void 8576255332Scyipf_nat_rule_fini(softc, n) 8577255332Scy ipf_main_softc_t *softc; 8578255332Scy ipnat_t *n; 8579255332Scy{ 8580255332Scy if (n->in_odst.na_atype == FRI_LOOKUP && n->in_odst.na_ptr != NULL) 8581255332Scy ipf_lookup_deref(softc, n->in_odst.na_type, n->in_odst.na_ptr); 8582255332Scy 8583255332Scy if (n->in_osrc.na_atype == FRI_LOOKUP && n->in_osrc.na_ptr != NULL) 8584255332Scy ipf_lookup_deref(softc, n->in_osrc.na_type, n->in_osrc.na_ptr); 8585255332Scy 8586255332Scy if (n->in_ndst.na_atype == FRI_LOOKUP && n->in_ndst.na_ptr != NULL) 8587255332Scy ipf_lookup_deref(softc, n->in_ndst.na_type, n->in_ndst.na_ptr); 8588255332Scy 8589255332Scy if (n->in_nsrc.na_atype == FRI_LOOKUP && n->in_nsrc.na_ptr != NULL) 8590255332Scy ipf_lookup_deref(softc, n->in_nsrc.na_type, n->in_nsrc.na_ptr); 8591255332Scy 8592255332Scy if (n->in_divmp != NULL) 8593255332Scy FREE_MB_T(n->in_divmp); 8594255332Scy} 8595