1145522Sdarrenr/* $FreeBSD: stable/11/sys/contrib/ipfilter/netinet/ip_nat.c 369541 2021-04-01 13:29:16Z git2svn $ */ 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 34369277Scy#if defined(_KERNEL) && defined(__FreeBSD__) 3553642Sguido# include <sys/filio.h> 3653642Sguido# include <sys/fcntl.h> 3753642Sguido#else 3853642Sguido# include <sys/ioctl.h> 3953642Sguido#endif 40153876Sguido# include <sys/fcntl.h> 4153642Sguido# include <sys/protosw.h> 4253642Sguido#include <sys/socket.h> 43145522Sdarrenr#if defined(_KERNEL) 4453642Sguido# include <sys/systm.h> 45344833Scy# if !defined(__SVR4) 4653642Sguido# include <sys/mbuf.h> 4753642Sguido# endif 48145522Sdarrenr#endif 49344833Scy#if defined(__SVR4) 5053642Sguido# include <sys/filio.h> 5153642Sguido# include <sys/byteorder.h> 52255332Scy# ifdef KERNEL 5353642Sguido# include <sys/dditypes.h> 5453642Sguido# endif 5553642Sguido# include <sys/stream.h> 5653642Sguido# include <sys/kmem.h> 5753642Sguido#endif 58369277Scy#if defined(__FreeBSD__) 5953642Sguido# include <sys/queue.h> 6053642Sguido#endif 6153642Sguido#include <net/if.h> 62369277Scy#if defined(__FreeBSD__) 6353642Sguido# include <net/if_var.h> 6453642Sguido#endif 6553642Sguido#ifdef sun 6653642Sguido# include <net/af.h> 6753642Sguido#endif 6853642Sguido#include <netinet/in.h> 6953642Sguido#include <netinet/in_systm.h> 7053642Sguido#include <netinet/ip.h> 7153642Sguido 7253642Sguido#ifdef RFC1825 7353642Sguido# include <vpn/md5.h> 7453642Sguido# include <vpn/ipsec.h> 7553642Sguidoextern struct ifnet vpnif; 7653642Sguido#endif 7753642Sguido 7853642Sguido# include <netinet/ip_var.h> 7953642Sguido#include <netinet/tcp.h> 8053642Sguido#include <netinet/udp.h> 8153642Sguido#include <netinet/ip_icmp.h> 8253642Sguido#include "netinet/ip_compat.h" 8353642Sguido#include <netinet/tcpip.h> 84255332Scy#include "netinet/ipl.h" 8553642Sguido#include "netinet/ip_fil.h" 8653642Sguido#include "netinet/ip_nat.h" 8753642Sguido#include "netinet/ip_frag.h" 8853642Sguido#include "netinet/ip_state.h" 8992685Sdarrenr#include "netinet/ip_proxy.h" 90255332Scy#include "netinet/ip_lookup.h" 91255332Scy#include "netinet/ip_dstlist.h" 92145522Sdarrenr#include "netinet/ip_sync.h" 93369277Scy#if defined(__FreeBSD__) 9453642Sguido# include <sys/malloc.h> 9553642Sguido#endif 96255332Scy#ifdef HAS_SYS_MD5_H 97255332Scy# include <sys/md5.h> 98255332Scy#else 99255332Scy# include "md5.h" 100255332Scy#endif 101145522Sdarrenr/* END OF INCLUDES */ 102145522Sdarrenr 10353642Sguido#undef SOCKADDR_IN 10453642Sguido#define SOCKADDR_IN struct sockaddr_in 10553642Sguido 10680482Sdarrenr#if !defined(lint) 10780482Sdarrenrstatic const char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed"; 10880482Sdarrenrstatic const char rcsid[] = "@(#)$FreeBSD: stable/11/sys/contrib/ipfilter/netinet/ip_nat.c 369541 2021-04-01 13:29:16Z git2svn $"; 109172776Sdarrenr/* static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.195.2.102 2007/10/16 10:08:10 darrenr Exp $"; */ 11080482Sdarrenr#endif 11180482Sdarrenr 112145522Sdarrenr 113255332Scy#define NATFSUM(n,v,f) ((v) == 4 ? (n)->f.in4.s_addr : (n)->f.i6[0] + \ 114255332Scy (n)->f.i6[1] + (n)->f.i6[2] + (n)->f.i6[3]) 115255332Scy#define NBUMP(x) softn->(x)++ 116255332Scy#define NBUMPD(x, y) do { \ 117255332Scy softn->x.y++; \ 118255332Scy DT(y); \ 119255332Scy } while (0) 120255332Scy#define NBUMPSIDE(y,x) softn->ipf_nat_stats.ns_side[y].x++ 121255332Scy#define NBUMPSIDED(y,x) do { softn->ipf_nat_stats.ns_side[y].x++; \ 122255332Scy DT(x); } while (0) 123255332Scy#define NBUMPSIDEX(y,x,z) \ 124255332Scy do { softn->ipf_nat_stats.ns_side[y].x++; \ 125255332Scy DT(z); } while (0) 126255332Scy#define NBUMPSIDEDF(y,x)do { softn->ipf_nat_stats.ns_side[y].x++; \ 127255332Scy DT1(x, fr_info_t *, fin); } while (0) 128255332Scy 129255332Scystatic ipftuneable_t ipf_nat_tuneables[] = { 130255332Scy /* nat */ 131255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_lock) }, 132255332Scy "nat_lock", 0, 1, 133255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_lock), 134255332Scy IPFT_RDONLY, NULL, NULL }, 135255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_sz) }, 136255332Scy "nat_table_size", 1, 0x7fffffff, 137255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_table_sz), 138255332Scy 0, NULL, ipf_nat_rehash }, 139255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_max) }, 140255332Scy "nat_table_max", 1, 0x7fffffff, 141255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_table_max), 142255332Scy 0, NULL, NULL }, 143255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maprules_sz) }, 144255332Scy "nat_rules_size", 1, 0x7fffffff, 145255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_maprules_sz), 146255332Scy 0, NULL, ipf_nat_rehash_rules }, 147255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_rdrrules_sz) }, 148255332Scy "rdr_rules_size", 1, 0x7fffffff, 149255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_rdrrules_sz), 150255332Scy 0, NULL, ipf_nat_rehash_rules }, 151255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_hostmap_sz) }, 152255332Scy "hostmap_size", 1, 0x7fffffff, 153255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_hostmap_sz), 154255332Scy 0, NULL, ipf_nat_hostmap_rehash }, 155255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maxbucket) }, 156255332Scy "nat_maxbucket",1, 0x7fffffff, 157255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_maxbucket), 158255332Scy 0, NULL, NULL }, 159255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_logging) }, 160255332Scy "nat_logging", 0, 1, 161255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_logging), 162255332Scy 0, NULL, NULL }, 163255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_doflush) }, 164255332Scy "nat_doflush", 0, 1, 165255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_doflush), 166255332Scy 0, NULL, NULL }, 167255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_low) }, 168255332Scy "nat_table_wm_low", 1, 99, 169255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_low), 170255332Scy 0, NULL, NULL }, 171255332Scy { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_high) }, 172255332Scy "nat_table_wm_high", 2, 100, 173255332Scy stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_high), 174255332Scy 0, NULL, NULL }, 175255332Scy { { 0 }, 176255332Scy NULL, 0, 0, 177255332Scy 0, 178255332Scy 0, NULL, NULL } 179255332Scy}; 180255332Scy 181145522Sdarrenr/* ======================================================================== */ 182145522Sdarrenr/* How the NAT is organised and works. */ 183145522Sdarrenr/* */ 184145522Sdarrenr/* Inside (interface y) NAT Outside (interface x) */ 185145522Sdarrenr/* -------------------- -+- ------------------------------------- */ 186255332Scy/* Packet going | out, processsed by ipf_nat_checkout() for x */ 187145522Sdarrenr/* ------------> | ------------> */ 188145522Sdarrenr/* src=10.1.1.1 | src=192.1.1.1 */ 189145522Sdarrenr/* | */ 190255332Scy/* | in, processed by ipf_nat_checkin() for x */ 191145522Sdarrenr/* <------------ | <------------ */ 192145522Sdarrenr/* dst=10.1.1.1 | dst=192.1.1.1 */ 193145522Sdarrenr/* -------------------- -+- ------------------------------------- */ 194255332Scy/* ipf_nat_checkout() - changes ip_src and if required, sport */ 195145522Sdarrenr/* - creates a new mapping, if required. */ 196255332Scy/* ipf_nat_checkin() - changes ip_dst and if required, dport */ 197145522Sdarrenr/* */ 198145522Sdarrenr/* In the NAT table, internal source is recorded as "in" and externally */ 199145522Sdarrenr/* seen as "out". */ 200145522Sdarrenr/* ======================================================================== */ 201145522Sdarrenr 202145522Sdarrenr 203255332Scy#if SOLARIS && !defined(INSTANCES) 204255332Scyextern int pfil_delayed_copy; 205255332Scy#endif 206255332Scy 207369245Sgit2svnstatic int ipf_nat_flush_entry(ipf_main_softc_t *, void *); 208369245Sgit2svnstatic int ipf_nat_getent(ipf_main_softc_t *, caddr_t, int); 209369245Sgit2svnstatic int ipf_nat_getsz(ipf_main_softc_t *, caddr_t, int); 210369245Sgit2svnstatic int ipf_nat_putent(ipf_main_softc_t *, caddr_t, int); 211369245Sgit2svnstatic void ipf_nat_addmap(ipf_nat_softc_t *, ipnat_t *); 212369245Sgit2svnstatic void ipf_nat_addrdr(ipf_nat_softc_t *, ipnat_t *); 213369245Sgit2svnstatic int ipf_nat_builddivertmp(ipf_nat_softc_t *, ipnat_t *); 214369245Sgit2svnstatic int ipf_nat_clearlist(ipf_main_softc_t *, ipf_nat_softc_t *); 215369245Sgit2svnstatic int ipf_nat_cmp_rules(ipnat_t *, ipnat_t *); 216369245Sgit2svnstatic int ipf_nat_decap(fr_info_t *, nat_t *); 217369245Sgit2svnstatic void ipf_nat_delrule(ipf_main_softc_t *, ipf_nat_softc_t *, 218369245Sgit2svn ipnat_t *, int); 219369245Sgit2svnstatic int ipf_nat_extraflush(ipf_main_softc_t *, ipf_nat_softc_t *, int); 220369245Sgit2svnstatic int ipf_nat_finalise(fr_info_t *, nat_t *); 221369245Sgit2svnstatic int ipf_nat_flushtable(ipf_main_softc_t *, ipf_nat_softc_t *); 222369245Sgit2svnstatic int ipf_nat_getnext(ipf_main_softc_t *, ipftoken_t *, 223369245Sgit2svn ipfgeniter_t *, ipfobj_t *); 224369245Sgit2svnstatic int ipf_nat_gettable(ipf_main_softc_t *, ipf_nat_softc_t *, 225369245Sgit2svn char *); 226369245Sgit2svnstatic hostmap_t *ipf_nat_hostmap(ipf_nat_softc_t *, ipnat_t *, 227255332Scy struct in_addr, struct in_addr, 228369245Sgit2svn struct in_addr, u_32_t); 229369245Sgit2svnstatic int ipf_nat_icmpquerytype(int); 230369245Sgit2svnstatic int ipf_nat_iterator(ipf_main_softc_t *, ipftoken_t *, 231369245Sgit2svn ipfgeniter_t *, ipfobj_t *); 232369245Sgit2svnstatic int ipf_nat_match(fr_info_t *, ipnat_t *); 233369245Sgit2svnstatic int ipf_nat_matcharray(nat_t *, int *, u_long); 234369245Sgit2svnstatic int ipf_nat_matchflush(ipf_main_softc_t *, ipf_nat_softc_t *, 235369245Sgit2svn caddr_t); 236369245Sgit2svnstatic void ipf_nat_mssclamp(tcphdr_t *, u_32_t, fr_info_t *, 237369245Sgit2svn u_short *); 238369245Sgit2svnstatic int ipf_nat_newmap(fr_info_t *, nat_t *, natinfo_t *); 239369245Sgit2svnstatic int ipf_nat_newdivert(fr_info_t *, nat_t *, natinfo_t *); 240369245Sgit2svnstatic int ipf_nat_newrdr(fr_info_t *, nat_t *, natinfo_t *); 241369245Sgit2svnstatic int ipf_nat_newrewrite(fr_info_t *, nat_t *, natinfo_t *); 242369245Sgit2svnstatic int ipf_nat_nextaddr(fr_info_t *, nat_addr_t *, u_32_t *, 243369245Sgit2svn u_32_t *); 244369245Sgit2svnstatic int ipf_nat_nextaddrinit(ipf_main_softc_t *, char *, 245369245Sgit2svn nat_addr_t *, int, void *); 246369245Sgit2svnstatic int ipf_nat_resolverule(ipf_main_softc_t *, ipnat_t *); 247369245Sgit2svnstatic int ipf_nat_ruleaddrinit(ipf_main_softc_t *, 248369245Sgit2svn ipf_nat_softc_t *, ipnat_t *); 249369245Sgit2svnstatic void ipf_nat_rule_fini(ipf_main_softc_t *, ipnat_t *); 250369245Sgit2svnstatic int ipf_nat_rule_init(ipf_main_softc_t *, ipf_nat_softc_t *, 251369245Sgit2svn ipnat_t *); 252369245Sgit2svnstatic int ipf_nat_siocaddnat(ipf_main_softc_t *, ipf_nat_softc_t *, 253369245Sgit2svn ipnat_t *, int); 254369245Sgit2svnstatic void ipf_nat_siocdelnat(ipf_main_softc_t *, ipf_nat_softc_t *, 255369245Sgit2svn ipnat_t *, int); 256369245Sgit2svnstatic void ipf_nat_tabmove(ipf_nat_softc_t *, nat_t *); 257255332Scy 258255332Scy/* ------------------------------------------------------------------------ */ 259255332Scy/* Function: ipf_nat_main_load */ 260255332Scy/* Returns: int - 0 == success, -1 == failure */ 261255332Scy/* Parameters: Nil */ 262255332Scy/* */ 263255332Scy/* The only global NAT structure that needs to be initialised is the filter */ 264255332Scy/* rule that is used with blocking packets. */ 265255332Scy/* ------------------------------------------------------------------------ */ 266255332Scyint 267255332Scyipf_nat_main_load() 268255332Scy{ 269255332Scy 270255332Scy return 0; 271255332Scy} 272255332Scy 273255332Scy 274255332Scy/* ------------------------------------------------------------------------ */ 275255332Scy/* Function: ipf_nat_main_unload */ 276255332Scy/* Returns: int - 0 == success, -1 == failure */ 277255332Scy/* Parameters: Nil */ 278255332Scy/* */ 279255332Scy/* A null-op function that exists as a placeholder so that the flow in */ 280255332Scy/* other functions is obvious. */ 281255332Scy/* ------------------------------------------------------------------------ */ 282255332Scyint 283255332Scyipf_nat_main_unload() 284255332Scy{ 285255332Scy return 0; 286255332Scy} 287255332Scy 288255332Scy 289255332Scy/* ------------------------------------------------------------------------ */ 290255332Scy/* Function: ipf_nat_soft_create */ 291255332Scy/* Returns: void * - NULL = failure, else pointer to NAT context */ 292255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 293255332Scy/* */ 294255332Scy/* Allocate the initial soft context structure for NAT and populate it with */ 295255332Scy/* some default values. Creating the tables is left until we call _init so */ 296255332Scy/* that sizes can be changed before we get under way. */ 297255332Scy/* ------------------------------------------------------------------------ */ 298255332Scyvoid * 299255332Scyipf_nat_soft_create(softc) 300255332Scy ipf_main_softc_t *softc; 301255332Scy{ 302255332Scy ipf_nat_softc_t *softn; 303255332Scy 304255332Scy KMALLOC(softn, ipf_nat_softc_t *); 305255332Scy if (softn == NULL) 306255332Scy return NULL; 307255332Scy 308255332Scy bzero((char *)softn, sizeof(*softn)); 309255332Scy 310255332Scy softn->ipf_nat_tune = ipf_tune_array_copy(softn, 311255332Scy sizeof(ipf_nat_tuneables), 312255332Scy ipf_nat_tuneables); 313255332Scy if (softn->ipf_nat_tune == NULL) { 314255332Scy ipf_nat_soft_destroy(softc, softn); 315255332Scy return NULL; 316255332Scy } 317255332Scy if (ipf_tune_array_link(softc, softn->ipf_nat_tune) == -1) { 318255332Scy ipf_nat_soft_destroy(softc, softn); 319255332Scy return NULL; 320255332Scy } 321255332Scy 322255332Scy softn->ipf_nat_list_tail = &softn->ipf_nat_list; 323255332Scy 324255332Scy softn->ipf_nat_table_max = NAT_TABLE_MAX; 325255332Scy softn->ipf_nat_table_sz = NAT_TABLE_SZ; 326255332Scy softn->ipf_nat_maprules_sz = NAT_SIZE; 327255332Scy softn->ipf_nat_rdrrules_sz = RDR_SIZE; 328255332Scy softn->ipf_nat_hostmap_sz = HOSTMAP_SIZE; 329255332Scy softn->ipf_nat_doflush = 0; 330145522Sdarrenr#ifdef IPFILTER_LOG 331255332Scy softn->ipf_nat_logging = 1; 332145522Sdarrenr#else 333255332Scy softn->ipf_nat_logging = 0; 334145522Sdarrenr#endif 33553642Sguido 336255332Scy softn->ipf_nat_defage = DEF_NAT_AGE; 337255332Scy softn->ipf_nat_defipage = IPF_TTLVAL(60); 338255332Scy softn->ipf_nat_deficmpage = IPF_TTLVAL(3); 339255332Scy softn->ipf_nat_table_wm_high = 99; 340255332Scy softn->ipf_nat_table_wm_low = 90; 34153642Sguido 342255332Scy return softn; 343255332Scy} 34453642Sguido 345255332Scy/* ------------------------------------------------------------------------ */ 346255332Scy/* Function: ipf_nat_soft_destroy */ 347255332Scy/* Returns: Nil */ 348255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 349255332Scy/* */ 350255332Scy/* ------------------------------------------------------------------------ */ 351255332Scyvoid 352255332Scyipf_nat_soft_destroy(softc, arg) 353255332Scy ipf_main_softc_t *softc; 354255332Scy void *arg; 355255332Scy{ 356255332Scy ipf_nat_softc_t *softn = arg; 35753642Sguido 358255332Scy if (softn->ipf_nat_tune != NULL) { 359255332Scy ipf_tune_array_unlink(softc, softn->ipf_nat_tune); 360255332Scy KFREES(softn->ipf_nat_tune, sizeof(ipf_nat_tuneables)); 361255332Scy softn->ipf_nat_tune = NULL; 362255332Scy } 363255332Scy 364255332Scy KFREE(softn); 365255332Scy} 366255332Scy 367255332Scy 368145522Sdarrenr/* ------------------------------------------------------------------------ */ 369255332Scy/* Function: ipf_nat_init */ 370145522Sdarrenr/* Returns: int - 0 == success, -1 == failure */ 371255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 372145522Sdarrenr/* */ 373145522Sdarrenr/* Initialise all of the NAT locks, tables and other structures. */ 374145522Sdarrenr/* ------------------------------------------------------------------------ */ 375255332Scyint 376255332Scyipf_nat_soft_init(softc, arg) 377255332Scy ipf_main_softc_t *softc; 378255332Scy void *arg; 37953642Sguido{ 380255332Scy ipf_nat_softc_t *softn = arg; 381255332Scy ipftq_t *tq; 382145522Sdarrenr int i; 383145522Sdarrenr 384255332Scy KMALLOCS(softn->ipf_nat_table[0], nat_t **, \ 385255332Scy sizeof(nat_t *) * softn->ipf_nat_table_sz); 386255332Scy 387255332Scy if (softn->ipf_nat_table[0] != NULL) { 388255332Scy bzero((char *)softn->ipf_nat_table[0], 389255332Scy softn->ipf_nat_table_sz * sizeof(nat_t *)); 390255332Scy } else { 39153642Sguido return -1; 392255332Scy } 39353642Sguido 394255332Scy KMALLOCS(softn->ipf_nat_table[1], nat_t **, \ 395255332Scy sizeof(nat_t *) * softn->ipf_nat_table_sz); 396255332Scy 397255332Scy if (softn->ipf_nat_table[1] != NULL) { 398255332Scy bzero((char *)softn->ipf_nat_table[1], 399255332Scy softn->ipf_nat_table_sz * sizeof(nat_t *)); 400255332Scy } else { 401145522Sdarrenr return -2; 402255332Scy } 40353642Sguido 404255332Scy KMALLOCS(softn->ipf_nat_map_rules, ipnat_t **, \ 405255332Scy sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz); 406255332Scy 407255332Scy if (softn->ipf_nat_map_rules != NULL) { 408255332Scy bzero((char *)softn->ipf_nat_map_rules, 409255332Scy softn->ipf_nat_maprules_sz * sizeof(ipnat_t *)); 410255332Scy } else { 411145522Sdarrenr return -3; 412255332Scy } 41353642Sguido 414255332Scy KMALLOCS(softn->ipf_nat_rdr_rules, ipnat_t **, \ 415255332Scy sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz); 416255332Scy 417255332Scy if (softn->ipf_nat_rdr_rules != NULL) { 418255332Scy bzero((char *)softn->ipf_nat_rdr_rules, 419255332Scy softn->ipf_nat_rdrrules_sz * sizeof(ipnat_t *)); 420255332Scy } else { 421145522Sdarrenr return -4; 422255332Scy } 42360852Sdarrenr 424255332Scy KMALLOCS(softn->ipf_hm_maptable, hostmap_t **, \ 425255332Scy sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); 426255332Scy 427255332Scy if (softn->ipf_hm_maptable != NULL) { 428255332Scy bzero((char *)softn->ipf_hm_maptable, 429255332Scy sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); 430255332Scy } else { 431145522Sdarrenr return -5; 432255332Scy } 433255332Scy softn->ipf_hm_maplist = NULL; 434145522Sdarrenr 435255332Scy KMALLOCS(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, u_int *, 436255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 437255332Scy 438255332Scy if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen == NULL) { 439145522Sdarrenr return -6; 440255332Scy } 441255332Scy bzero((char *)softn->ipf_nat_stats.ns_side[0].ns_bucketlen, 442255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 443145522Sdarrenr 444255332Scy KMALLOCS(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, u_int *, 445255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 446255332Scy 447255332Scy if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen == NULL) { 448145522Sdarrenr return -7; 449255332Scy } 450145522Sdarrenr 451255332Scy bzero((char *)softn->ipf_nat_stats.ns_side[1].ns_bucketlen, 452255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 453145522Sdarrenr 454255332Scy if (softn->ipf_nat_maxbucket == 0) { 455255332Scy for (i = softn->ipf_nat_table_sz; i > 0; i >>= 1) 456255332Scy softn->ipf_nat_maxbucket++; 457255332Scy softn->ipf_nat_maxbucket *= 2; 458145522Sdarrenr } 459145522Sdarrenr 460255332Scy ipf_sttab_init(softc, softn->ipf_nat_tcptq); 461145522Sdarrenr /* 462145522Sdarrenr * Increase this because we may have "keep state" following this too 463145522Sdarrenr * and packet storms can occur if this is removed too quickly. 464145522Sdarrenr */ 465255332Scy softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack; 466255332Scy softn->ipf_nat_tcptq[IPF_TCP_NSTATES - 1].ifq_next = 467255332Scy &softn->ipf_nat_udptq; 468145522Sdarrenr 469255332Scy IPFTQ_INIT(&softn->ipf_nat_udptq, softn->ipf_nat_defage, 470255332Scy "nat ipftq udp tab"); 471255332Scy softn->ipf_nat_udptq.ifq_next = &softn->ipf_nat_udpacktq; 472255332Scy 473255332Scy IPFTQ_INIT(&softn->ipf_nat_udpacktq, softn->ipf_nat_defage, 474255332Scy "nat ipftq udpack tab"); 475255332Scy softn->ipf_nat_udpacktq.ifq_next = &softn->ipf_nat_icmptq; 476255332Scy 477255332Scy IPFTQ_INIT(&softn->ipf_nat_icmptq, softn->ipf_nat_deficmpage, 478255332Scy "nat icmp ipftq tab"); 479255332Scy softn->ipf_nat_icmptq.ifq_next = &softn->ipf_nat_icmpacktq; 480255332Scy 481255332Scy IPFTQ_INIT(&softn->ipf_nat_icmpacktq, softn->ipf_nat_defage, 482255332Scy "nat icmpack ipftq tab"); 483255332Scy softn->ipf_nat_icmpacktq.ifq_next = &softn->ipf_nat_iptq; 484255332Scy 485255332Scy IPFTQ_INIT(&softn->ipf_nat_iptq, softn->ipf_nat_defipage, 486255332Scy "nat ip ipftq tab"); 487255332Scy softn->ipf_nat_iptq.ifq_next = &softn->ipf_nat_pending; 488255332Scy 489255332Scy IPFTQ_INIT(&softn->ipf_nat_pending, 1, "nat pending ipftq tab"); 490255332Scy softn->ipf_nat_pending.ifq_next = NULL; 491255332Scy 492255332Scy for (i = 0, tq = softn->ipf_nat_tcptq; i < IPF_TCP_NSTATES; i++, tq++) { 493255332Scy if (tq->ifq_ttl < softn->ipf_nat_deficmpage) 494255332Scy tq->ifq_ttl = softn->ipf_nat_deficmpage; 495145522Sdarrenr#ifdef LARGE_NAT 496255332Scy else if (tq->ifq_ttl > softn->ipf_nat_defage) 497255332Scy tq->ifq_ttl = softn->ipf_nat_defage; 498145522Sdarrenr#endif 499145522Sdarrenr } 500145522Sdarrenr 501145522Sdarrenr /* 502145522Sdarrenr * Increase this because we may have "keep state" following 503145522Sdarrenr * this too and packet storms can occur if this is removed 504145522Sdarrenr * too quickly. 505145522Sdarrenr */ 506255332Scy softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack; 507145522Sdarrenr 508255332Scy MUTEX_INIT(&softn->ipf_nat_new, "ipf nat new mutex"); 509255332Scy MUTEX_INIT(&softn->ipf_nat_io, "ipf nat io mutex"); 510145522Sdarrenr 511255332Scy softn->ipf_nat_inited = 1; 512145522Sdarrenr 51353642Sguido return 0; 51453642Sguido} 51553642Sguido 51653642Sguido 517145522Sdarrenr/* ------------------------------------------------------------------------ */ 518255332Scy/* Function: ipf_nat_soft_fini */ 519145522Sdarrenr/* Returns: Nil */ 520255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 521255332Scy/* */ 522255332Scy/* Free all memory used by NAT structures allocated at runtime. */ 523255332Scy/* ------------------------------------------------------------------------ */ 524255332Scyint 525255332Scyipf_nat_soft_fini(softc, arg) 526255332Scy ipf_main_softc_t *softc; 527255332Scy void *arg; 528255332Scy{ 529255332Scy ipf_nat_softc_t *softn = arg; 530255332Scy ipftq_t *ifq, *ifqnext; 531255332Scy 532255332Scy (void) ipf_nat_clearlist(softc, softn); 533255332Scy (void) ipf_nat_flushtable(softc, softn); 534255332Scy 535255332Scy /* 536255332Scy * Proxy timeout queues are not cleaned here because although they 537255332Scy * exist on the NAT list, ipf_proxy_unload is called after unload 538255332Scy * and the proxies actually are responsible for them being created. 539255332Scy * Should the proxy timeouts have their own list? There's no real 540255332Scy * justification as this is the only complication. 541255332Scy */ 542255332Scy for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) { 543255332Scy ifqnext = ifq->ifq_next; 544255332Scy if (ipf_deletetimeoutqueue(ifq) == 0) 545255332Scy ipf_freetimeoutqueue(softc, ifq); 546255332Scy } 547255332Scy 548255332Scy if (softn->ipf_nat_table[0] != NULL) { 549255332Scy KFREES(softn->ipf_nat_table[0], 550255332Scy sizeof(nat_t *) * softn->ipf_nat_table_sz); 551255332Scy softn->ipf_nat_table[0] = NULL; 552255332Scy } 553255332Scy if (softn->ipf_nat_table[1] != NULL) { 554255332Scy KFREES(softn->ipf_nat_table[1], 555255332Scy sizeof(nat_t *) * softn->ipf_nat_table_sz); 556255332Scy softn->ipf_nat_table[1] = NULL; 557255332Scy } 558255332Scy if (softn->ipf_nat_map_rules != NULL) { 559255332Scy KFREES(softn->ipf_nat_map_rules, 560255332Scy sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz); 561255332Scy softn->ipf_nat_map_rules = NULL; 562255332Scy } 563255332Scy if (softn->ipf_nat_rdr_rules != NULL) { 564255332Scy KFREES(softn->ipf_nat_rdr_rules, 565255332Scy sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz); 566255332Scy softn->ipf_nat_rdr_rules = NULL; 567255332Scy } 568255332Scy if (softn->ipf_hm_maptable != NULL) { 569255332Scy KFREES(softn->ipf_hm_maptable, 570255332Scy sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); 571255332Scy softn->ipf_hm_maptable = NULL; 572255332Scy } 573255332Scy if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) { 574255332Scy KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, 575255332Scy sizeof(u_int) * softn->ipf_nat_table_sz); 576255332Scy softn->ipf_nat_stats.ns_side[0].ns_bucketlen = NULL; 577255332Scy } 578255332Scy if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) { 579255332Scy KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, 580255332Scy sizeof(u_int) * softn->ipf_nat_table_sz); 581255332Scy softn->ipf_nat_stats.ns_side[1].ns_bucketlen = NULL; 582255332Scy } 583255332Scy 584255332Scy if (softn->ipf_nat_inited == 1) { 585255332Scy softn->ipf_nat_inited = 0; 586255332Scy ipf_sttab_destroy(softn->ipf_nat_tcptq); 587255332Scy 588255332Scy MUTEX_DESTROY(&softn->ipf_nat_new); 589255332Scy MUTEX_DESTROY(&softn->ipf_nat_io); 590255332Scy 591255332Scy MUTEX_DESTROY(&softn->ipf_nat_udptq.ifq_lock); 592255332Scy MUTEX_DESTROY(&softn->ipf_nat_udpacktq.ifq_lock); 593255332Scy MUTEX_DESTROY(&softn->ipf_nat_icmptq.ifq_lock); 594255332Scy MUTEX_DESTROY(&softn->ipf_nat_icmpacktq.ifq_lock); 595255332Scy MUTEX_DESTROY(&softn->ipf_nat_iptq.ifq_lock); 596255332Scy MUTEX_DESTROY(&softn->ipf_nat_pending.ifq_lock); 597255332Scy } 598255332Scy 599255332Scy return 0; 600255332Scy} 601255332Scy 602255332Scy 603255332Scy/* ------------------------------------------------------------------------ */ 604255332Scy/* Function: ipf_nat_setlock */ 605255332Scy/* Returns: Nil */ 606255332Scy/* Parameters: arg(I) - pointer to soft state information */ 607255332Scy/* tmp(I) - new lock value */ 608255332Scy/* */ 609255332Scy/* Set the "lock status" of NAT to the value in tmp. */ 610255332Scy/* ------------------------------------------------------------------------ */ 611255332Scyvoid 612255332Scyipf_nat_setlock(arg, tmp) 613255332Scy void *arg; 614255332Scy int tmp; 615255332Scy{ 616255332Scy ipf_nat_softc_t *softn = arg; 617255332Scy 618255332Scy softn->ipf_nat_lock = tmp; 619255332Scy} 620255332Scy 621255332Scy 622255332Scy/* ------------------------------------------------------------------------ */ 623255332Scy/* Function: ipf_nat_addrdr */ 624255332Scy/* Returns: Nil */ 625145522Sdarrenr/* Parameters: n(I) - pointer to NAT rule to add */ 626145522Sdarrenr/* */ 627145522Sdarrenr/* Adds a redirect rule to the hash table of redirect rules and the list of */ 628145522Sdarrenr/* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ 629145522Sdarrenr/* use by redirect rules. */ 630145522Sdarrenr/* ------------------------------------------------------------------------ */ 631255332Scystatic void 632255332Scyipf_nat_addrdr(softn, n) 633255332Scy ipf_nat_softc_t *softn; 634255332Scy ipnat_t *n; 63553642Sguido{ 63660852Sdarrenr ipnat_t **np; 63760852Sdarrenr u_32_t j; 63853642Sguido u_int hv; 639255332Scy u_int rhv; 64060852Sdarrenr int k; 64153642Sguido 642255332Scy if (n->in_odstatype == FRI_NORMAL) { 643255332Scy k = count4bits(n->in_odstmsk); 644255332Scy ipf_inet_mask_add(k, &softn->ipf_nat_rdr_mask); 645255332Scy j = (n->in_odstaddr & n->in_odstmsk); 646255332Scy rhv = NAT_HASH_FN(j, 0, 0xffffffff); 647255332Scy } else { 648255332Scy ipf_inet_mask_add(0, &softn->ipf_nat_rdr_mask); 649255332Scy j = 0; 650255332Scy rhv = 0; 651255332Scy } 652255332Scy hv = rhv % softn->ipf_nat_rdrrules_sz; 653255332Scy np = softn->ipf_nat_rdr_rules + hv; 65460852Sdarrenr while (*np != NULL) 65560852Sdarrenr np = &(*np)->in_rnext; 65660852Sdarrenr n->in_rnext = NULL; 65760852Sdarrenr n->in_prnext = np; 658255332Scy n->in_hv[0] = hv; 659255332Scy n->in_use++; 66060852Sdarrenr *np = n; 66153642Sguido} 66253642Sguido 66353642Sguido 664145522Sdarrenr/* ------------------------------------------------------------------------ */ 665255332Scy/* Function: ipf_nat_addmap */ 666145522Sdarrenr/* Returns: Nil */ 667145522Sdarrenr/* Parameters: n(I) - pointer to NAT rule to add */ 668145522Sdarrenr/* */ 669145522Sdarrenr/* Adds a NAT map rule to the hash table of rules and the list of loaded */ 670145522Sdarrenr/* NAT rules. Updates the bitmask indicating which netmasks are in use by */ 671145522Sdarrenr/* redirect rules. */ 672145522Sdarrenr/* ------------------------------------------------------------------------ */ 673255332Scystatic void 674255332Scyipf_nat_addmap(softn, n) 675255332Scy ipf_nat_softc_t *softn; 676255332Scy ipnat_t *n; 67760852Sdarrenr{ 67860852Sdarrenr ipnat_t **np; 67960852Sdarrenr u_32_t j; 68060852Sdarrenr u_int hv; 681255332Scy u_int rhv; 68260852Sdarrenr int k; 68360852Sdarrenr 684255332Scy if (n->in_osrcatype == FRI_NORMAL) { 685255332Scy k = count4bits(n->in_osrcmsk); 686255332Scy ipf_inet_mask_add(k, &softn->ipf_nat_map_mask); 687255332Scy j = (n->in_osrcaddr & n->in_osrcmsk); 688255332Scy rhv = NAT_HASH_FN(j, 0, 0xffffffff); 689255332Scy } else { 690255332Scy ipf_inet_mask_add(0, &softn->ipf_nat_map_mask); 691255332Scy j = 0; 692255332Scy rhv = 0; 693255332Scy } 694255332Scy hv = rhv % softn->ipf_nat_maprules_sz; 695255332Scy np = softn->ipf_nat_map_rules + hv; 69660852Sdarrenr while (*np != NULL) 69760852Sdarrenr np = &(*np)->in_mnext; 69860852Sdarrenr n->in_mnext = NULL; 69960852Sdarrenr n->in_pmnext = np; 700255332Scy n->in_hv[1] = rhv; 701255332Scy n->in_use++; 70260852Sdarrenr *np = n; 70360852Sdarrenr} 70460852Sdarrenr 70560852Sdarrenr 706145522Sdarrenr/* ------------------------------------------------------------------------ */ 707255332Scy/* Function: ipf_nat_delrdr */ 708145522Sdarrenr/* Returns: Nil */ 709145522Sdarrenr/* Parameters: n(I) - pointer to NAT rule to delete */ 710145522Sdarrenr/* */ 711145522Sdarrenr/* Removes a redirect rule from the hash table of redirect rules. */ 712145522Sdarrenr/* ------------------------------------------------------------------------ */ 713255332Scyvoid 714255332Scyipf_nat_delrdr(softn, n) 715255332Scy ipf_nat_softc_t *softn; 716255332Scy ipnat_t *n; 71760852Sdarrenr{ 718255332Scy if (n->in_odstatype == FRI_NORMAL) { 719255332Scy int k = count4bits(n->in_odstmsk); 720255332Scy ipf_inet_mask_del(k, &softn->ipf_nat_rdr_mask); 721255332Scy } else { 722255332Scy ipf_inet_mask_del(0, &softn->ipf_nat_rdr_mask); 723255332Scy } 72460852Sdarrenr if (n->in_rnext) 72560852Sdarrenr n->in_rnext->in_prnext = n->in_prnext; 72660852Sdarrenr *n->in_prnext = n->in_rnext; 727255332Scy n->in_use--; 72860852Sdarrenr} 72960852Sdarrenr 73060852Sdarrenr 731145522Sdarrenr/* ------------------------------------------------------------------------ */ 732255332Scy/* Function: ipf_nat_delmap */ 733145522Sdarrenr/* Returns: Nil */ 734145522Sdarrenr/* Parameters: n(I) - pointer to NAT rule to delete */ 735145522Sdarrenr/* */ 736145522Sdarrenr/* Removes a NAT map rule from the hash table of NAT map rules. */ 737145522Sdarrenr/* ------------------------------------------------------------------------ */ 738255332Scyvoid 739255332Scyipf_nat_delmap(softn, n) 740255332Scy ipf_nat_softc_t *softn; 741255332Scy ipnat_t *n; 74253642Sguido{ 743255332Scy if (n->in_osrcatype == FRI_NORMAL) { 744255332Scy int k = count4bits(n->in_osrcmsk); 745255332Scy ipf_inet_mask_del(k, &softn->ipf_nat_map_mask); 746255332Scy } else { 747255332Scy ipf_inet_mask_del(0, &softn->ipf_nat_map_mask); 748255332Scy } 749145522Sdarrenr if (n->in_mnext != NULL) 75060852Sdarrenr n->in_mnext->in_pmnext = n->in_pmnext; 75160852Sdarrenr *n->in_pmnext = n->in_mnext; 752255332Scy n->in_use--; 75360852Sdarrenr} 75460852Sdarrenr 75560852Sdarrenr 756145522Sdarrenr/* ------------------------------------------------------------------------ */ 757255332Scy/* Function: ipf_nat_hostmap */ 758145522Sdarrenr/* Returns: struct hostmap* - NULL if no hostmap could be created, */ 759145522Sdarrenr/* else a pointer to the hostmapping to use */ 760145522Sdarrenr/* Parameters: np(I) - pointer to NAT rule */ 761145522Sdarrenr/* real(I) - real IP address */ 762145522Sdarrenr/* map(I) - mapped IP address */ 763145522Sdarrenr/* port(I) - destination port number */ 764145522Sdarrenr/* Write Locks: ipf_nat */ 765145522Sdarrenr/* */ 766145522Sdarrenr/* Check if an ip address has already been allocated for a given mapping */ 767145522Sdarrenr/* that is not doing port based translation. If is not yet allocated, then */ 768145522Sdarrenr/* create a new entry if a non-NULL NAT rule pointer has been supplied. */ 769145522Sdarrenr/* ------------------------------------------------------------------------ */ 770255332Scystatic struct hostmap * 771255332Scyipf_nat_hostmap(softn, np, src, dst, map, port) 772255332Scy ipf_nat_softc_t *softn; 773255332Scy ipnat_t *np; 774255332Scy struct in_addr src; 775255332Scy struct in_addr dst; 776255332Scy struct in_addr map; 777255332Scy u_32_t port; 77860852Sdarrenr{ 77960852Sdarrenr hostmap_t *hm; 780255332Scy u_int hv, rhv; 78153642Sguido 782145522Sdarrenr hv = (src.s_addr ^ dst.s_addr); 783145522Sdarrenr hv += src.s_addr; 784145522Sdarrenr hv += dst.s_addr; 785255332Scy rhv = hv; 786255332Scy hv %= softn->ipf_nat_hostmap_sz; 787255332Scy for (hm = softn->ipf_hm_maptable[hv]; hm; hm = hm->hm_hnext) 788255332Scy if ((hm->hm_osrcip.s_addr == src.s_addr) && 789255332Scy (hm->hm_odstip.s_addr == dst.s_addr) && 790145522Sdarrenr ((np == NULL) || (np == hm->hm_ipnat)) && 791145522Sdarrenr ((port == 0) || (port == hm->hm_port))) { 792255332Scy softn->ipf_nat_stats.ns_hm_addref++; 79360852Sdarrenr hm->hm_ref++; 79460852Sdarrenr return hm; 79560852Sdarrenr } 79660852Sdarrenr 797255332Scy if (np == NULL) { 798255332Scy softn->ipf_nat_stats.ns_hm_nullnp++; 799145522Sdarrenr return NULL; 800255332Scy } 801145522Sdarrenr 80260852Sdarrenr KMALLOC(hm, hostmap_t *); 80360852Sdarrenr if (hm) { 804255332Scy hm->hm_next = softn->ipf_hm_maplist; 805255332Scy hm->hm_pnext = &softn->ipf_hm_maplist; 806255332Scy if (softn->ipf_hm_maplist != NULL) 807255332Scy softn->ipf_hm_maplist->hm_pnext = &hm->hm_next; 808255332Scy softn->ipf_hm_maplist = hm; 809255332Scy hm->hm_hnext = softn->ipf_hm_maptable[hv]; 810255332Scy hm->hm_phnext = softn->ipf_hm_maptable + hv; 811255332Scy if (softn->ipf_hm_maptable[hv] != NULL) 812255332Scy softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; 813255332Scy softn->ipf_hm_maptable[hv] = hm; 81460852Sdarrenr hm->hm_ipnat = np; 815255332Scy np->in_use++; 816255332Scy hm->hm_osrcip = src; 817255332Scy hm->hm_odstip = dst; 818255332Scy hm->hm_nsrcip = map; 819255332Scy hm->hm_ndstip.s_addr = 0; 82060852Sdarrenr hm->hm_ref = 1; 821145522Sdarrenr hm->hm_port = port; 822255332Scy hm->hm_hv = rhv; 823255332Scy hm->hm_v = 4; 824255332Scy softn->ipf_nat_stats.ns_hm_new++; 825255332Scy } else { 826255332Scy softn->ipf_nat_stats.ns_hm_newfail++; 82760852Sdarrenr } 82860852Sdarrenr return hm; 82953642Sguido} 83053642Sguido 83153642Sguido 832145522Sdarrenr/* ------------------------------------------------------------------------ */ 833255332Scy/* Function: ipf_nat_hostmapdel */ 834145522Sdarrenr/* Returns: Nil */ 835170268Sdarrenr/* Parameters: hmp(I) - pointer to hostmap structure pointer */ 836145522Sdarrenr/* Write Locks: ipf_nat */ 837145522Sdarrenr/* */ 838145522Sdarrenr/* Decrement the references to this hostmap structure by one. If this */ 839145522Sdarrenr/* reaches zero then remove it and free it. */ 840145522Sdarrenr/* ------------------------------------------------------------------------ */ 841255332Scyvoid 842255332Scyipf_nat_hostmapdel(softc, hmp) 843255332Scy ipf_main_softc_t *softc; 844255332Scy struct hostmap **hmp; 84560852Sdarrenr{ 846170268Sdarrenr struct hostmap *hm; 847170268Sdarrenr 848170268Sdarrenr hm = *hmp; 849170268Sdarrenr *hmp = NULL; 850170268Sdarrenr 851145522Sdarrenr hm->hm_ref--; 85260852Sdarrenr if (hm->hm_ref == 0) { 853255332Scy ipf_nat_rule_deref(softc, &hm->hm_ipnat); 854170268Sdarrenr if (hm->hm_hnext) 855170268Sdarrenr hm->hm_hnext->hm_phnext = hm->hm_phnext; 856170268Sdarrenr *hm->hm_phnext = hm->hm_hnext; 85760852Sdarrenr if (hm->hm_next) 85860852Sdarrenr hm->hm_next->hm_pnext = hm->hm_pnext; 85960852Sdarrenr *hm->hm_pnext = hm->hm_next; 86060852Sdarrenr KFREE(hm); 86160852Sdarrenr } 86260852Sdarrenr} 86360852Sdarrenr 86460852Sdarrenr 865145522Sdarrenr/* ------------------------------------------------------------------------ */ 866255332Scy/* Function: ipf_fix_outcksum */ 867145522Sdarrenr/* Returns: Nil */ 868145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 869145522Sdarrenr/* sp(I) - location of 16bit checksum to update */ 870145522Sdarrenr/* n((I) - amount to adjust checksum by */ 871145522Sdarrenr/* */ 872145522Sdarrenr/* Adjusts the 16bit checksum by "n" for packets going out. */ 873145522Sdarrenr/* ------------------------------------------------------------------------ */ 874255332Scyvoid 875255332Scyipf_fix_outcksum(cksum, sp, n, partial) 876255332Scy int cksum; 877255332Scy u_short *sp; 878255332Scy u_32_t n, partial; 87953642Sguido{ 880145522Sdarrenr u_short sumshort; 881145522Sdarrenr u_32_t sum1; 88253642Sguido 883145522Sdarrenr if (n == 0) 88453642Sguido return; 885145522Sdarrenr 886255332Scy if (cksum == 4) { 887255332Scy *sp = 0; 88855929Sguido return; 88955929Sguido } 890255332Scy if (cksum == 2) { 891255332Scy sum1 = partial; 892255332Scy sum1 = (sum1 & 0xffff) + (sum1 >> 16); 893255332Scy *sp = htons(sum1); 894255332Scy return; 895255332Scy } 89653642Sguido sum1 = (~ntohs(*sp)) & 0xffff; 89753642Sguido sum1 += (n); 89853642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 89953642Sguido /* Again */ 90053642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 90153642Sguido sumshort = ~(u_short)sum1; 90253642Sguido *(sp) = htons(sumshort); 90353642Sguido} 90453642Sguido 90553642Sguido 906145522Sdarrenr/* ------------------------------------------------------------------------ */ 907255332Scy/* Function: ipf_fix_incksum */ 908145522Sdarrenr/* Returns: Nil */ 909145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 910145522Sdarrenr/* sp(I) - location of 16bit checksum to update */ 911145522Sdarrenr/* n((I) - amount to adjust checksum by */ 912145522Sdarrenr/* */ 913145522Sdarrenr/* Adjusts the 16bit checksum by "n" for packets going in. */ 914145522Sdarrenr/* ------------------------------------------------------------------------ */ 915255332Scyvoid 916255332Scyipf_fix_incksum(cksum, sp, n, partial) 917255332Scy int cksum; 918255332Scy u_short *sp; 919255332Scy u_32_t n, partial; 92053642Sguido{ 921145522Sdarrenr u_short sumshort; 922145522Sdarrenr u_32_t sum1; 92353642Sguido 924145522Sdarrenr if (n == 0) 92553642Sguido return; 926145522Sdarrenr 927255332Scy if (cksum == 4) { 928255332Scy *sp = 0; 92955929Sguido return; 93055929Sguido } 931255332Scy if (cksum == 2) { 932255332Scy sum1 = partial; 933255332Scy sum1 = (sum1 & 0xffff) + (sum1 >> 16); 934255332Scy *sp = htons(sum1); 935255332Scy return; 936255332Scy } 937255332Scy 93853642Sguido sum1 = (~ntohs(*sp)) & 0xffff; 93953642Sguido sum1 += ~(n) & 0xffff; 94053642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 94153642Sguido /* Again */ 94253642Sguido sum1 = (sum1 >> 16) + (sum1 & 0xffff); 94353642Sguido sumshort = ~(u_short)sum1; 94453642Sguido *(sp) = htons(sumshort); 94553642Sguido} 94653642Sguido 94753642Sguido 948145522Sdarrenr/* ------------------------------------------------------------------------ */ 949255332Scy/* Function: ipf_fix_datacksum */ 950145522Sdarrenr/* Returns: Nil */ 951145522Sdarrenr/* Parameters: sp(I) - location of 16bit checksum to update */ 952145522Sdarrenr/* n((I) - amount to adjust checksum by */ 953145522Sdarrenr/* */ 954145522Sdarrenr/* Fix_datacksum is used *only* for the adjustments of checksums in the */ 955145522Sdarrenr/* data section of an IP packet. */ 956145522Sdarrenr/* */ 957145522Sdarrenr/* The only situation in which you need to do this is when NAT'ing an */ 958145522Sdarrenr/* ICMP error message. Such a message, contains in its body the IP header */ 959145522Sdarrenr/* of the original IP packet, that causes the error. */ 960145522Sdarrenr/* */ 961145522Sdarrenr/* You can't use fix_incksum or fix_outcksum in that case, because for the */ 962145522Sdarrenr/* kernel the data section of the ICMP error is just data, and no special */ 963145522Sdarrenr/* processing like hardware cksum or ntohs processing have been done by the */ 964145522Sdarrenr/* kernel on the data section. */ 965145522Sdarrenr/* ------------------------------------------------------------------------ */ 966255332Scyvoid 967255332Scyipf_fix_datacksum(sp, n) 968255332Scy u_short *sp; 969255332Scy u_32_t n; 97067614Sdarrenr{ 971145522Sdarrenr u_short sumshort; 972145522Sdarrenr u_32_t sum1; 97367614Sdarrenr 974145522Sdarrenr if (n == 0) 97567614Sdarrenr return; 97667614Sdarrenr 97767614Sdarrenr sum1 = (~ntohs(*sp)) & 0xffff; 97867614Sdarrenr sum1 += (n); 97967614Sdarrenr sum1 = (sum1 >> 16) + (sum1 & 0xffff); 98067614Sdarrenr /* Again */ 98167614Sdarrenr sum1 = (sum1 >> 16) + (sum1 & 0xffff); 98267614Sdarrenr sumshort = ~(u_short)sum1; 98367614Sdarrenr *(sp) = htons(sumshort); 98467614Sdarrenr} 98567614Sdarrenr 98653642Sguido 987145522Sdarrenr/* ------------------------------------------------------------------------ */ 988255332Scy/* Function: ipf_nat_ioctl */ 989145522Sdarrenr/* Returns: int - 0 == success, != 0 == failure */ 990255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 991255332Scy/* data(I) - pointer to ioctl data */ 992255332Scy/* cmd(I) - ioctl command integer */ 993255332Scy/* mode(I) - file mode bits used with open */ 994255332Scy/* uid(I) - uid of calling process */ 995255332Scy/* ctx(I) - pointer used as key for finding context */ 996145522Sdarrenr/* */ 997145522Sdarrenr/* Processes an ioctl call made to operate on the IP Filter NAT device. */ 998145522Sdarrenr/* ------------------------------------------------------------------------ */ 999255332Scyint 1000255332Scyipf_nat_ioctl(softc, data, cmd, mode, uid, ctx) 1001255332Scy ipf_main_softc_t *softc; 1002255332Scy ioctlcmd_t cmd; 1003255332Scy caddr_t data; 1004255332Scy int mode, uid; 1005255332Scy void *ctx; 100653642Sguido{ 1007255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 100895418Sdarrenr int error = 0, ret, arg, getlock; 1009255332Scy ipnat_t *nat, *nt, *n; 101053642Sguido ipnat_t natd; 1011170268Sdarrenr SPL_INT(s); 101253642Sguido 1013358666Scy#if !SOLARIS && defined(_KERNEL) 1014255332Scy# if NETBSD_GE_REV(399002000) 1015170268Sdarrenr if ((mode & FWRITE) && 1016170268Sdarrenr kauth_authorize_network(curlwp->l_cred, KAUTH_NETWORK_FIREWALL, 1017170268Sdarrenr KAUTH_REQ_NETWORK_FIREWALL_FW, 1018255332Scy NULL, NULL, NULL)) 1019170268Sdarrenr# else 1020369277Scy# if defined(__FreeBSD__) 1021255332Scy if (securelevel_ge(curthread->td_ucred, 3) && (mode & FWRITE)) 1022192895Sjamie# else 1023255332Scy if ((securelevel >= 3) && (mode & FWRITE)) 1024192895Sjamie# endif 1025255332Scy# endif 1026255332Scy { 1027255332Scy IPFERROR(60001); 1028170268Sdarrenr return EPERM; 1029170268Sdarrenr } 103053642Sguido#endif 103153642Sguido 1032145522Sdarrenr getlock = (mode & NAT_LOCKHELD) ? 0 : 1; 1033145522Sdarrenr 1034255332Scy n = NULL; 1035255332Scy nt = NULL; 1036255332Scy nat = NULL; 1037145522Sdarrenr 1038255332Scy if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT) || 1039255332Scy (cmd == (ioctlcmd_t)SIOCPURGENAT)) { 104095418Sdarrenr if (mode & NAT_SYSSPACE) { 104195418Sdarrenr bcopy(data, (char *)&natd, sizeof(natd)); 1042255332Scy nat = &natd; 104395418Sdarrenr error = 0; 104495418Sdarrenr } else { 1045255332Scy bzero(&natd, sizeof(natd)); 1046255332Scy error = ipf_inobj(softc, data, NULL, &natd, 1047255332Scy IPFOBJ_IPNAT); 1048255332Scy if (error != 0) 1049255332Scy goto done; 1050255332Scy 1051255332Scy if (natd.in_size < sizeof(ipnat_t)) { 1052255332Scy error = EINVAL; 1053255332Scy goto done; 1054255332Scy } 1055255332Scy KMALLOCS(nt, ipnat_t *, natd.in_size); 1056255332Scy if (nt == NULL) { 1057255332Scy IPFERROR(60070); 1058255332Scy error = ENOMEM; 1059255332Scy goto done; 1060255332Scy } 1061255332Scy bzero(nt, natd.in_size); 1062255332Scy error = ipf_inobjsz(softc, data, nt, IPFOBJ_IPNAT, 1063255332Scy natd.in_size); 1064255332Scy if (error) 1065255332Scy goto done; 1066255332Scy nat = nt; 106795418Sdarrenr } 106853642Sguido 1069255332Scy /* 1070255332Scy * For add/delete, look to see if the NAT entry is 1071255332Scy * already present 1072255332Scy */ 107353642Sguido nat->in_flags &= IPN_USERFLAGS; 107453642Sguido if ((nat->in_redir & NAT_MAPBLK) == 0) { 1075255332Scy if (nat->in_osrcatype == FRI_NORMAL || 1076255332Scy nat->in_osrcatype == FRI_NONE) 1077255332Scy nat->in_osrcaddr &= nat->in_osrcmsk; 1078255332Scy if (nat->in_odstatype == FRI_NORMAL || 1079255332Scy nat->in_odstatype == FRI_NONE) 1080255332Scy nat->in_odstaddr &= nat->in_odstmsk; 1081255332Scy if ((nat->in_flags & (IPN_SPLIT|IPN_SIPRANGE)) == 0) { 1082255332Scy if (nat->in_nsrcatype == FRI_NORMAL) 1083255332Scy nat->in_nsrcaddr &= nat->in_nsrcmsk; 1084255332Scy if (nat->in_ndstatype == FRI_NORMAL) 1085255332Scy nat->in_ndstaddr &= nat->in_ndstmsk; 1086255332Scy } 108753642Sguido } 1088255332Scy 1089255332Scy error = ipf_nat_rule_init(softc, softn, nat); 1090255332Scy if (error != 0) 1091255332Scy goto done; 1092255332Scy 1093255332Scy MUTEX_ENTER(&softn->ipf_nat_io); 1094255332Scy for (n = softn->ipf_nat_list; n != NULL; n = n->in_next) 1095255332Scy if (ipf_nat_cmp_rules(nat, n) == 0) 109653642Sguido break; 109753642Sguido } 109853642Sguido 109953642Sguido switch (cmd) 110053642Sguido { 110155929Sguido#ifdef IPFILTER_LOG 110255929Sguido case SIOCIPFFB : 110360852Sdarrenr { 110460852Sdarrenr int tmp; 110560852Sdarrenr 1106255332Scy if (!(mode & FWRITE)) { 1107255332Scy IPFERROR(60002); 110855929Sguido error = EPERM; 1109255332Scy } else { 1110255332Scy tmp = ipf_log_clear(softc, IPL_LOGNAT); 1111255332Scy error = BCOPYOUT(&tmp, data, sizeof(tmp)); 1112255332Scy if (error != 0) { 1113255332Scy IPFERROR(60057); 1114170268Sdarrenr error = EFAULT; 1115255332Scy } 111660852Sdarrenr } 111755929Sguido break; 111860852Sdarrenr } 1119170268Sdarrenr 1120145522Sdarrenr case SIOCSETLG : 1121255332Scy if (!(mode & FWRITE)) { 1122255332Scy IPFERROR(60003); 1123145522Sdarrenr error = EPERM; 1124255332Scy } else { 1125255332Scy error = BCOPYIN(data, &softn->ipf_nat_logging, 1126255332Scy sizeof(softn->ipf_nat_logging)); 1127170268Sdarrenr if (error != 0) 1128170268Sdarrenr error = EFAULT; 1129145522Sdarrenr } 1130145522Sdarrenr break; 1131170268Sdarrenr 1132145522Sdarrenr case SIOCGETLG : 1133255332Scy error = BCOPYOUT(&softn->ipf_nat_logging, data, 1134255332Scy sizeof(softn->ipf_nat_logging)); 1135255332Scy if (error != 0) { 1136255332Scy IPFERROR(60004); 1137170268Sdarrenr error = EFAULT; 1138255332Scy } 1139145522Sdarrenr break; 1140170268Sdarrenr 1141145522Sdarrenr case FIONREAD : 1142255332Scy arg = ipf_log_bytesused(softc, IPL_LOGNAT); 1143170268Sdarrenr error = BCOPYOUT(&arg, data, sizeof(arg)); 1144255332Scy if (error != 0) { 1145255332Scy IPFERROR(60005); 1146170268Sdarrenr error = EFAULT; 1147255332Scy } 1148145522Sdarrenr break; 114955929Sguido#endif 115053642Sguido case SIOCADNAT : 115153642Sguido if (!(mode & FWRITE)) { 1152255332Scy IPFERROR(60006); 115353642Sguido error = EPERM; 1154145522Sdarrenr } else if (n != NULL) { 1155255332Scy natd.in_flineno = n->in_flineno; 1156255332Scy (void) ipf_outobj(softc, data, &natd, IPFOBJ_IPNAT); 1157255332Scy IPFERROR(60007); 115853642Sguido error = EEXIST; 1159145522Sdarrenr } else if (nt == NULL) { 1160255332Scy IPFERROR(60008); 1161145522Sdarrenr error = ENOMEM; 116253642Sguido } 1163145522Sdarrenr if (error != 0) { 1164255332Scy MUTEX_EXIT(&softn->ipf_nat_io); 116553642Sguido break; 116653642Sguido } 1167255332Scy if (nat != nt) 1168255332Scy bcopy((char *)nat, (char *)nt, sizeof(*n)); 1169255332Scy error = ipf_nat_siocaddnat(softc, softn, nt, getlock); 1170255332Scy MUTEX_EXIT(&softn->ipf_nat_io); 1171255332Scy if (error == 0) { 1172255332Scy nat = NULL; 1173145522Sdarrenr nt = NULL; 1174255332Scy } 117553642Sguido break; 1176170268Sdarrenr 117753642Sguido case SIOCRMNAT : 1178255332Scy case SIOCPURGENAT : 117953642Sguido if (!(mode & FWRITE)) { 1180255332Scy IPFERROR(60009); 118153642Sguido error = EPERM; 118253642Sguido n = NULL; 1183145522Sdarrenr } else if (n == NULL) { 1184255332Scy IPFERROR(60010); 1185145522Sdarrenr error = ESRCH; 118653642Sguido } 1187145522Sdarrenr 1188145522Sdarrenr if (error != 0) { 1189255332Scy MUTEX_EXIT(&softn->ipf_nat_io); 119053642Sguido break; 119153642Sguido } 1192255332Scy if (cmd == (ioctlcmd_t)SIOCPURGENAT) { 1193255332Scy error = ipf_outobjsz(softc, data, n, IPFOBJ_IPNAT, 1194255332Scy n->in_size); 1195255332Scy if (error) { 1196255332Scy MUTEX_EXIT(&softn->ipf_nat_io); 1197255332Scy goto done; 1198255332Scy } 1199255332Scy n->in_flags |= IPN_PURGE; 1200255332Scy } 1201255332Scy ipf_nat_siocdelnat(softc, softn, n, getlock); 1202145522Sdarrenr 1203255332Scy MUTEX_EXIT(&softn->ipf_nat_io); 120453642Sguido n = NULL; 120553642Sguido break; 1206170268Sdarrenr 120753642Sguido case SIOCGNATS : 1208255332Scy { 1209255332Scy natstat_t *nsp = &softn->ipf_nat_stats; 1210255332Scy 1211255332Scy nsp->ns_side[0].ns_table = softn->ipf_nat_table[0]; 1212255332Scy nsp->ns_side[1].ns_table = softn->ipf_nat_table[1]; 1213255332Scy nsp->ns_list = softn->ipf_nat_list; 1214255332Scy nsp->ns_maptable = softn->ipf_hm_maptable; 1215255332Scy nsp->ns_maplist = softn->ipf_hm_maplist; 1216255332Scy nsp->ns_nattab_sz = softn->ipf_nat_table_sz; 1217255332Scy nsp->ns_nattab_max = softn->ipf_nat_table_max; 1218255332Scy nsp->ns_rultab_sz = softn->ipf_nat_maprules_sz; 1219255332Scy nsp->ns_rdrtab_sz = softn->ipf_nat_rdrrules_sz; 1220255332Scy nsp->ns_hostmap_sz = softn->ipf_nat_hostmap_sz; 1221255332Scy nsp->ns_instances = softn->ipf_nat_instances; 1222255332Scy nsp->ns_ticks = softc->ipf_ticks; 1223255332Scy#ifdef IPFILTER_LOGGING 1224255332Scy nsp->ns_log_ok = ipf_log_logok(softc, IPF_LOGNAT); 1225255332Scy nsp->ns_log_fail = ipf_log_failures(softc, IPF_LOGNAT); 1226255332Scy#else 1227255332Scy nsp->ns_log_ok = 0; 1228255332Scy nsp->ns_log_fail = 0; 1229255332Scy#endif 1230255332Scy error = ipf_outobj(softc, data, nsp, IPFOBJ_NATSTAT); 123153642Sguido break; 1232255332Scy } 1233170268Sdarrenr 123453642Sguido case SIOCGNATL : 123553642Sguido { 123653642Sguido natlookup_t nl; 123753642Sguido 1238255332Scy error = ipf_inobj(softc, data, NULL, &nl, IPFOBJ_NATLOOKUP); 1239145522Sdarrenr if (error == 0) { 1240173181Sdarrenr void *ptr; 1241173181Sdarrenr 1242173181Sdarrenr if (getlock) { 1243255332Scy READ_ENTER(&softc->ipf_nat); 1244173181Sdarrenr } 1245255332Scy 1246255332Scy switch (nl.nl_v) 1247255332Scy { 1248255332Scy case 4 : 1249255332Scy ptr = ipf_nat_lookupredir(&nl); 1250255332Scy break; 1251255332Scy#ifdef USE_INET6 1252255332Scy case 6 : 1253255332Scy ptr = ipf_nat6_lookupredir(&nl); 1254255332Scy break; 1255255332Scy#endif 1256255332Scy default: 1257255332Scy ptr = NULL; 1258255332Scy break; 1259255332Scy } 1260255332Scy 1261173181Sdarrenr if (getlock) { 1262255332Scy RWLOCK_EXIT(&softc->ipf_nat); 1263173181Sdarrenr } 1264173181Sdarrenr if (ptr != NULL) { 1265255332Scy error = ipf_outobj(softc, data, &nl, 1266255332Scy IPFOBJ_NATLOOKUP); 1267145522Sdarrenr } else { 1268255332Scy IPFERROR(60011); 1269145522Sdarrenr error = ESRCH; 1270145522Sdarrenr } 1271145522Sdarrenr } 127253642Sguido break; 127353642Sguido } 1274170268Sdarrenr 127560852Sdarrenr case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */ 127653642Sguido if (!(mode & FWRITE)) { 1277255332Scy IPFERROR(60012); 127853642Sguido error = EPERM; 127953642Sguido break; 128053642Sguido } 1281145522Sdarrenr if (getlock) { 1282255332Scy WRITE_ENTER(&softc->ipf_nat); 1283145522Sdarrenr } 1284170268Sdarrenr 1285170268Sdarrenr error = BCOPYIN(data, &arg, sizeof(arg)); 1286255332Scy if (error != 0) { 1287255332Scy IPFERROR(60013); 1288170268Sdarrenr error = EFAULT; 1289255332Scy } else { 1290170268Sdarrenr if (arg == 0) 1291255332Scy ret = ipf_nat_flushtable(softc, softn); 1292170268Sdarrenr else if (arg == 1) 1293255332Scy ret = ipf_nat_clearlist(softc, softn); 1294170268Sdarrenr else 1295255332Scy ret = ipf_nat_extraflush(softc, softn, arg); 1296255332Scy ipf_proxy_flush(softc->ipf_proxy_soft, arg); 1297170268Sdarrenr } 1298170268Sdarrenr 1299145522Sdarrenr if (getlock) { 1300255332Scy RWLOCK_EXIT(&softc->ipf_nat); 130160852Sdarrenr } 1302145522Sdarrenr if (error == 0) { 1303170268Sdarrenr error = BCOPYOUT(&ret, data, sizeof(ret)); 1304145522Sdarrenr } 130553642Sguido break; 1306170268Sdarrenr 1307255332Scy case SIOCMATCHFLUSH : 1308255332Scy if (!(mode & FWRITE)) { 1309255332Scy IPFERROR(60014); 1310255332Scy error = EPERM; 1311255332Scy break; 1312255332Scy } 1313255332Scy if (getlock) { 1314255332Scy WRITE_ENTER(&softc->ipf_nat); 1315255332Scy } 1316255332Scy 1317255332Scy error = ipf_nat_matchflush(softc, softn, data); 1318255332Scy 1319255332Scy if (getlock) { 1320255332Scy RWLOCK_EXIT(&softc->ipf_nat); 1321255332Scy } 1322255332Scy break; 1323255332Scy 1324145522Sdarrenr case SIOCPROXY : 1325255332Scy error = ipf_proxy_ioctl(softc, data, cmd, mode, ctx); 1326145522Sdarrenr break; 1327170268Sdarrenr 132860852Sdarrenr case SIOCSTLCK : 1329153876Sguido if (!(mode & FWRITE)) { 1330255332Scy IPFERROR(60015); 1331153876Sguido error = EPERM; 1332153876Sguido } else { 1333255332Scy error = ipf_lock(data, &softn->ipf_nat_lock); 1334153876Sguido } 133553642Sguido break; 1336170268Sdarrenr 133760852Sdarrenr case SIOCSTPUT : 1338153876Sguido if ((mode & FWRITE) != 0) { 1339255332Scy error = ipf_nat_putent(softc, data, getlock); 1340145522Sdarrenr } else { 1341255332Scy IPFERROR(60016); 134260852Sdarrenr error = EACCES; 1343145522Sdarrenr } 134460852Sdarrenr break; 1345170268Sdarrenr 134660852Sdarrenr case SIOCSTGSZ : 1347255332Scy if (softn->ipf_nat_lock) { 1348255332Scy error = ipf_nat_getsz(softc, data, getlock); 1349255332Scy } else { 1350255332Scy IPFERROR(60017); 135160852Sdarrenr error = EACCES; 1352255332Scy } 135360852Sdarrenr break; 1354170268Sdarrenr 135560852Sdarrenr case SIOCSTGET : 1356255332Scy if (softn->ipf_nat_lock) { 1357255332Scy error = ipf_nat_getent(softc, data, getlock); 1358255332Scy } else { 1359255332Scy IPFERROR(60018); 136060852Sdarrenr error = EACCES; 1361255332Scy } 136260852Sdarrenr break; 1363170268Sdarrenr 1364170268Sdarrenr case SIOCGENITER : 1365170268Sdarrenr { 1366170268Sdarrenr ipfgeniter_t iter; 1367170268Sdarrenr ipftoken_t *token; 1368255332Scy ipfobj_t obj; 1369170268Sdarrenr 1370255332Scy error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER); 1371255332Scy if (error != 0) 1372255332Scy break; 1373255332Scy 1374170268Sdarrenr SPL_SCHED(s); 1375255332Scy token = ipf_token_find(softc, iter.igi_type, uid, ctx); 1376255332Scy if (token != NULL) { 1377255332Scy error = ipf_nat_iterator(softc, token, &iter, &obj); 1378255332Scy WRITE_ENTER(&softc->ipf_tokens); 1379255332Scy ipf_token_deref(softc, token); 1380255332Scy RWLOCK_EXIT(&softc->ipf_tokens); 1381170268Sdarrenr } 1382170268Sdarrenr SPL_X(s); 1383170268Sdarrenr break; 1384170268Sdarrenr } 1385170268Sdarrenr 1386170268Sdarrenr case SIOCIPFDELTOK : 1387255332Scy error = BCOPYIN(data, &arg, sizeof(arg)); 1388170268Sdarrenr if (error == 0) { 1389170268Sdarrenr SPL_SCHED(s); 1390255332Scy error = ipf_token_del(softc, arg, uid, ctx); 1391170268Sdarrenr SPL_X(s); 1392170268Sdarrenr } else { 1393255332Scy IPFERROR(60019); 1394170268Sdarrenr error = EFAULT; 1395170268Sdarrenr } 1396170268Sdarrenr break; 1397170268Sdarrenr 1398170268Sdarrenr case SIOCGTQTAB : 1399255332Scy error = ipf_outobj(softc, data, softn->ipf_nat_tcptq, 1400255332Scy IPFOBJ_STATETQTAB); 1401170268Sdarrenr break; 1402170268Sdarrenr 1403172776Sdarrenr case SIOCGTABL : 1404255332Scy error = ipf_nat_gettable(softc, softn, data); 1405172776Sdarrenr break; 1406172776Sdarrenr 140753642Sguido default : 1408255332Scy IPFERROR(60020); 140953642Sguido error = EINVAL; 141053642Sguido break; 141153642Sguido } 141260852Sdarrenrdone: 1413255332Scy if (nat != NULL) 1414255332Scy ipf_nat_rule_fini(softc, nat); 1415170268Sdarrenr if (nt != NULL) 1416255332Scy KFREES(nt, nt->in_size); 141753642Sguido return error; 141853642Sguido} 141953642Sguido 142053642Sguido 1421145522Sdarrenr/* ------------------------------------------------------------------------ */ 1422255332Scy/* Function: ipf_nat_siocaddnat */ 1423145522Sdarrenr/* Returns: int - 0 == success, != 0 == failure */ 1424255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1425255332Scy/* softn(I) - pointer to NAT context structure */ 1426255332Scy/* n(I) - pointer to new NAT rule */ 1427145522Sdarrenr/* np(I) - pointer to where to insert new NAT rule */ 1428255332Scy/* getlock(I) - flag indicating if lock on is held */ 1429255332Scy/* Mutex Locks: ipf_nat_io */ 1430145522Sdarrenr/* */ 1431145522Sdarrenr/* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 1432145522Sdarrenr/* from information passed to the kernel, then add it to the appropriate */ 1433145522Sdarrenr/* NAT rule table(s). */ 1434145522Sdarrenr/* ------------------------------------------------------------------------ */ 1435255332Scystatic int 1436255332Scyipf_nat_siocaddnat(softc, softn, n, getlock) 1437255332Scy ipf_main_softc_t *softc; 1438255332Scy ipf_nat_softc_t *softn; 1439255332Scy ipnat_t *n; 1440255332Scy int getlock; 1441145522Sdarrenr{ 1442255332Scy int error = 0; 1443145522Sdarrenr 1444255332Scy if (ipf_nat_resolverule(softc, n) != 0) { 1445255332Scy IPFERROR(60022); 1446161356Sguido return ENOENT; 1447255332Scy } 1448145522Sdarrenr 1449255332Scy if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) { 1450255332Scy IPFERROR(60023); 1451145522Sdarrenr return EINVAL; 1452255332Scy } 1453145522Sdarrenr 1454255332Scy if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) { 1455145522Sdarrenr /* 1456255332Scy * Prerecord whether or not the destination of the divert 1457255332Scy * is local or not to the interface the packet is going 1458255332Scy * to be sent out. 1459145522Sdarrenr */ 1460255332Scy n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1], 1461255332Scy n->in_ifps[1], &n->in_ndstip6); 1462145522Sdarrenr } 1463145522Sdarrenr 1464145522Sdarrenr if (getlock) { 1465255332Scy WRITE_ENTER(&softc->ipf_nat); 1466145522Sdarrenr } 1467145522Sdarrenr n->in_next = NULL; 1468255332Scy n->in_pnext = softn->ipf_nat_list_tail; 1469255332Scy *n->in_pnext = n; 1470255332Scy softn->ipf_nat_list_tail = &n->in_next; 1471255332Scy n->in_use++; 1472145522Sdarrenr 1473145522Sdarrenr if (n->in_redir & NAT_REDIRECT) { 1474145522Sdarrenr n->in_flags &= ~IPN_NOTDST; 1475255332Scy switch (n->in_v[0]) 1476255332Scy { 1477255332Scy case 4 : 1478255332Scy ipf_nat_addrdr(softn, n); 1479255332Scy break; 1480255332Scy#ifdef USE_INET6 1481255332Scy case 6 : 1482255332Scy ipf_nat6_addrdr(softn, n); 1483255332Scy break; 1484255332Scy#endif 1485255332Scy default : 1486255332Scy break; 1487255332Scy } 1488255332Scy ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_rdr); 1489145522Sdarrenr } 1490255332Scy 1491145522Sdarrenr if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { 1492145522Sdarrenr n->in_flags &= ~IPN_NOTSRC; 1493255332Scy switch (n->in_v[0]) 1494255332Scy { 1495255332Scy case 4 : 1496255332Scy ipf_nat_addmap(softn, n); 1497255332Scy break; 1498255332Scy#ifdef USE_INET6 1499255332Scy case 6 : 1500255332Scy ipf_nat6_addmap(softn, n); 1501255332Scy break; 1502255332Scy#endif 1503255332Scy default : 1504255332Scy break; 1505255332Scy } 1506255332Scy ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_map); 1507145522Sdarrenr } 1508255332Scy 1509255332Scy if (n->in_age[0] != 0) 1510255332Scy n->in_tqehead[0] = ipf_addtimeoutqueue(softc, 1511255332Scy &softn->ipf_nat_utqe, 1512255332Scy n->in_age[0]); 1513255332Scy 1514255332Scy if (n->in_age[1] != 0) 1515255332Scy n->in_tqehead[1] = ipf_addtimeoutqueue(softc, 1516255332Scy &softn->ipf_nat_utqe, 1517255332Scy n->in_age[1]); 1518255332Scy 1519170268Sdarrenr MUTEX_INIT(&n->in_lock, "ipnat rule lock"); 1520170268Sdarrenr 1521145522Sdarrenr n = NULL; 1522255332Scy ATOMIC_INC32(softn->ipf_nat_stats.ns_rules); 1523255332Scy#if SOLARIS && !defined(INSTANCES) 1524145522Sdarrenr pfil_delayed_copy = 0; 1525145522Sdarrenr#endif 1526145522Sdarrenr if (getlock) { 1527255332Scy RWLOCK_EXIT(&softc->ipf_nat); /* WRITE */ 1528145522Sdarrenr } 1529145522Sdarrenr 1530145522Sdarrenr return error; 1531145522Sdarrenr} 1532145522Sdarrenr 1533145522Sdarrenr 1534145522Sdarrenr/* ------------------------------------------------------------------------ */ 1535255332Scy/* Function: ipf_nat_ruleaddrinit */ 1536255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1537255332Scy/* softn(I) - pointer to NAT context structure */ 1538255332Scy/* n(I) - pointer to NAT rule */ 1539255332Scy/* */ 1540255332Scy/* Initialise all of the NAT address structures in a NAT rule. */ 1541255332Scy/* ------------------------------------------------------------------------ */ 1542255332Scystatic int 1543255332Scyipf_nat_ruleaddrinit(softc, softn, n) 1544255332Scy ipf_main_softc_t *softc; 1545255332Scy ipf_nat_softc_t *softn; 1546255332Scy ipnat_t *n; 1547255332Scy{ 1548255332Scy int idx, error; 1549255332Scy 1550255332Scy if ((n->in_ndst.na_atype == FRI_LOOKUP) && 1551255332Scy (n->in_ndst.na_type != IPLT_DSTLIST)) { 1552255332Scy IPFERROR(60071); 1553255332Scy return EINVAL; 1554255332Scy } 1555255332Scy if ((n->in_nsrc.na_atype == FRI_LOOKUP) && 1556255332Scy (n->in_nsrc.na_type != IPLT_DSTLIST)) { 1557255332Scy IPFERROR(60069); 1558255332Scy return EINVAL; 1559255332Scy } 1560255332Scy 1561255332Scy if (n->in_redir == NAT_BIMAP) { 1562255332Scy n->in_ndstaddr = n->in_osrcaddr; 1563255332Scy n->in_ndstmsk = n->in_osrcmsk; 1564255332Scy n->in_odstaddr = n->in_nsrcaddr; 1565255332Scy n->in_odstmsk = n->in_nsrcmsk; 1566255332Scy 1567255332Scy } 1568255332Scy 1569255332Scy if (n->in_redir & NAT_REDIRECT) 1570255332Scy idx = 1; 1571255332Scy else 1572255332Scy idx = 0; 1573255332Scy /* 1574255332Scy * Initialise all of the address fields. 1575255332Scy */ 1576255332Scy error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, 1, 1577255332Scy n->in_ifps[idx]); 1578255332Scy if (error != 0) 1579255332Scy return error; 1580255332Scy 1581255332Scy error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, 1, 1582255332Scy n->in_ifps[idx]); 1583255332Scy if (error != 0) 1584255332Scy return error; 1585255332Scy 1586255332Scy error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, 1, 1587255332Scy n->in_ifps[idx]); 1588255332Scy if (error != 0) 1589255332Scy return error; 1590255332Scy 1591255332Scy error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, 1, 1592255332Scy n->in_ifps[idx]); 1593255332Scy if (error != 0) 1594255332Scy return error; 1595255332Scy 1596255332Scy if (n->in_redir & NAT_DIVERTUDP) 1597255332Scy ipf_nat_builddivertmp(softn, n); 1598255332Scy 1599255332Scy return 0; 1600255332Scy} 1601255332Scy 1602255332Scy 1603255332Scy/* ------------------------------------------------------------------------ */ 1604255332Scy/* Function: ipf_nat_resolvrule */ 1605145522Sdarrenr/* Returns: Nil */ 1606255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1607255332Scy/* n(I) - pointer to NAT rule */ 1608145522Sdarrenr/* */ 1609145522Sdarrenr/* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 1610145522Sdarrenr/* from information passed to the kernel, then add it to the appropriate */ 1611145522Sdarrenr/* NAT rule table(s). */ 1612145522Sdarrenr/* ------------------------------------------------------------------------ */ 1613255332Scystatic int 1614255332Scyipf_nat_resolverule(softc, n) 1615255332Scy ipf_main_softc_t *softc; 1616255332Scy ipnat_t *n; 1617145522Sdarrenr{ 1618255332Scy char *base; 1619145522Sdarrenr 1620255332Scy base = n->in_names; 1621255332Scy 1622255332Scy n->in_ifps[0] = ipf_resolvenic(softc, base + n->in_ifnames[0], 1623255332Scy n->in_v[0]); 1624255332Scy 1625255332Scy if (n->in_ifnames[1] == -1) { 1626255332Scy n->in_ifnames[1] = n->in_ifnames[0]; 1627145522Sdarrenr n->in_ifps[1] = n->in_ifps[0]; 1628145522Sdarrenr } else { 1629255332Scy n->in_ifps[1] = ipf_resolvenic(softc, base + n->in_ifnames[1], 1630255332Scy n->in_v[1]); 1631145522Sdarrenr } 1632145522Sdarrenr 1633255332Scy if (n->in_plabel != -1) { 1634255332Scy if (n->in_redir & NAT_REDIRECT) 1635255332Scy n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft, 1636255332Scy n->in_pr[0], 1637255332Scy base + n->in_plabel); 1638255332Scy else 1639255332Scy n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft, 1640255332Scy n->in_pr[1], 1641255332Scy base + n->in_plabel); 1642161356Sguido if (n->in_apr == NULL) 1643161356Sguido return -1; 1644145522Sdarrenr } 1645161356Sguido return 0; 1646145522Sdarrenr} 1647145522Sdarrenr 1648145522Sdarrenr 1649145522Sdarrenr/* ------------------------------------------------------------------------ */ 1650255332Scy/* Function: ipf_nat_siocdelnat */ 1651145522Sdarrenr/* Returns: int - 0 == success, != 0 == failure */ 1652255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1653255332Scy/* softn(I) - pointer to NAT context structure */ 1654255332Scy/* n(I) - pointer to new NAT rule */ 1655255332Scy/* getlock(I) - flag indicating if lock on is held */ 1656255332Scy/* Mutex Locks: ipf_nat_io */ 1657145522Sdarrenr/* */ 1658145522Sdarrenr/* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ 1659145522Sdarrenr/* from information passed to the kernel, then add it to the appropriate */ 1660145522Sdarrenr/* NAT rule table(s). */ 1661145522Sdarrenr/* ------------------------------------------------------------------------ */ 1662255332Scystatic void 1663255332Scyipf_nat_siocdelnat(softc, softn, n, getlock) 1664255332Scy ipf_main_softc_t *softc; 1665255332Scy ipf_nat_softc_t *softn; 1666255332Scy ipnat_t *n; 1667255332Scy int getlock; 1668145522Sdarrenr{ 1669145522Sdarrenr if (getlock) { 1670255332Scy WRITE_ENTER(&softc->ipf_nat); 1671145522Sdarrenr } 1672145522Sdarrenr 1673255332Scy ipf_nat_delrule(softc, softn, n, 1); 1674145522Sdarrenr 1675145522Sdarrenr if (getlock) { 1676255332Scy RWLOCK_EXIT(&softc->ipf_nat); /* READ/WRITE */ 1677145522Sdarrenr } 1678145522Sdarrenr} 1679145522Sdarrenr 1680145522Sdarrenr 1681145522Sdarrenr/* ------------------------------------------------------------------------ */ 1682255332Scy/* Function: ipf_nat_getsz */ 1683145522Sdarrenr/* Returns: int - 0 == success, != 0 is the error value. */ 1684255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1685255332Scy/* data(I) - pointer to natget structure with kernel */ 1686255332Scy/* pointer get the size of. */ 1687255332Scy/* getlock(I) - flag indicating whether or not the caller */ 1688255332Scy/* holds a lock on ipf_nat */ 1689145522Sdarrenr/* */ 1690145522Sdarrenr/* Handle SIOCSTGSZ. */ 1691145522Sdarrenr/* Return the size of the nat list entry to be copied back to user space. */ 1692145522Sdarrenr/* The size of the entry is stored in the ng_sz field and the enture natget */ 1693145522Sdarrenr/* structure is copied back to the user. */ 1694145522Sdarrenr/* ------------------------------------------------------------------------ */ 1695255332Scystatic int 1696255332Scyipf_nat_getsz(softc, data, getlock) 1697255332Scy ipf_main_softc_t *softc; 1698255332Scy caddr_t data; 1699255332Scy int getlock; 170060852Sdarrenr{ 1701255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 170260852Sdarrenr ap_session_t *aps; 170360852Sdarrenr nat_t *nat, *n; 170460852Sdarrenr natget_t ng; 1705255332Scy int error; 170660852Sdarrenr 1707255332Scy error = BCOPYIN(data, &ng, sizeof(ng)); 1708255332Scy if (error != 0) { 1709255332Scy IPFERROR(60024); 1710170268Sdarrenr return EFAULT; 1711255332Scy } 171260852Sdarrenr 1713173181Sdarrenr if (getlock) { 1714255332Scy READ_ENTER(&softc->ipf_nat); 1715173181Sdarrenr } 1716173181Sdarrenr 171760852Sdarrenr nat = ng.ng_ptr; 171860852Sdarrenr if (!nat) { 1719255332Scy nat = softn->ipf_nat_instances; 172060852Sdarrenr ng.ng_sz = 0; 1721145522Sdarrenr /* 1722145522Sdarrenr * Empty list so the size returned is 0. Simple. 1723145522Sdarrenr */ 172460852Sdarrenr if (nat == NULL) { 1725173181Sdarrenr if (getlock) { 1726255332Scy RWLOCK_EXIT(&softc->ipf_nat); 1727173181Sdarrenr } 1728255332Scy error = BCOPYOUT(&ng, data, sizeof(ng)); 1729255332Scy if (error != 0) { 1730255332Scy IPFERROR(60025); 1731170268Sdarrenr return EFAULT; 1732255332Scy } 1733145522Sdarrenr return 0; 173460852Sdarrenr } 173560852Sdarrenr } else { 173660852Sdarrenr /* 173760852Sdarrenr * Make sure the pointer we're copying from exists in the 173860852Sdarrenr * current list of entries. Security precaution to prevent 173960852Sdarrenr * copying of random kernel data. 174060852Sdarrenr */ 1741255332Scy for (n = softn->ipf_nat_instances; n; n = n->nat_next) 174260852Sdarrenr if (n == nat) 174360852Sdarrenr break; 1744173181Sdarrenr if (n == NULL) { 1745173181Sdarrenr if (getlock) { 1746255332Scy RWLOCK_EXIT(&softc->ipf_nat); 1747173181Sdarrenr } 1748255332Scy IPFERROR(60026); 174960852Sdarrenr return ESRCH; 1750173181Sdarrenr } 175160852Sdarrenr } 175260852Sdarrenr 1753145522Sdarrenr /* 1754145522Sdarrenr * Incluse any space required for proxy data structures. 1755145522Sdarrenr */ 175660852Sdarrenr ng.ng_sz = sizeof(nat_save_t); 175760852Sdarrenr aps = nat->nat_aps; 1758145522Sdarrenr if (aps != NULL) { 1759145522Sdarrenr ng.ng_sz += sizeof(ap_session_t) - 4; 1760145522Sdarrenr if (aps->aps_data != 0) 1761145522Sdarrenr ng.ng_sz += aps->aps_psiz; 176260852Sdarrenr } 1763173181Sdarrenr if (getlock) { 1764255332Scy RWLOCK_EXIT(&softc->ipf_nat); 1765173181Sdarrenr } 176660852Sdarrenr 1767255332Scy error = BCOPYOUT(&ng, data, sizeof(ng)); 1768255332Scy if (error != 0) { 1769255332Scy IPFERROR(60027); 1770170268Sdarrenr return EFAULT; 1771255332Scy } 1772145522Sdarrenr return 0; 177360852Sdarrenr} 177460852Sdarrenr 177560852Sdarrenr 1776145522Sdarrenr/* ------------------------------------------------------------------------ */ 1777255332Scy/* Function: ipf_nat_getent */ 1778145522Sdarrenr/* Returns: int - 0 == success, != 0 is the error value. */ 1779255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1780255332Scy/* data(I) - pointer to natget structure with kernel pointer*/ 1781255332Scy/* to NAT structure to copy out. */ 1782255332Scy/* getlock(I) - flag indicating whether or not the caller */ 1783255332Scy/* holds a lock on ipf_nat */ 1784145522Sdarrenr/* */ 1785145522Sdarrenr/* Handle SIOCSTGET. */ 1786145522Sdarrenr/* Copies out NAT entry to user space. Any additional data held for a */ 1787145522Sdarrenr/* proxy is also copied, as to is the NAT rule which was responsible for it */ 1788145522Sdarrenr/* ------------------------------------------------------------------------ */ 1789255332Scystatic int 1790255332Scyipf_nat_getent(softc, data, getlock) 1791255332Scy ipf_main_softc_t *softc; 1792255332Scy caddr_t data; 1793255332Scy int getlock; 179460852Sdarrenr{ 1795255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 1796145522Sdarrenr int error, outsize; 179760852Sdarrenr ap_session_t *aps; 1798145522Sdarrenr nat_save_t *ipn, ipns; 1799145522Sdarrenr nat_t *n, *nat; 180060852Sdarrenr 1801255332Scy error = ipf_inobj(softc, data, NULL, &ipns, IPFOBJ_NATSAVE); 1802145522Sdarrenr if (error != 0) 1803145522Sdarrenr return error; 180460852Sdarrenr 1805255332Scy if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) { 1806255332Scy IPFERROR(60028); 1807145522Sdarrenr return EINVAL; 1808255332Scy } 1809145522Sdarrenr 1810145522Sdarrenr KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize); 1811255332Scy if (ipn == NULL) { 1812255332Scy IPFERROR(60029); 1813145522Sdarrenr return ENOMEM; 1814255332Scy } 1815145522Sdarrenr 1816173181Sdarrenr if (getlock) { 1817255332Scy READ_ENTER(&softc->ipf_nat); 1818173181Sdarrenr } 1819173181Sdarrenr 1820145522Sdarrenr ipn->ipn_dsize = ipns.ipn_dsize; 1821145522Sdarrenr nat = ipns.ipn_next; 1822145522Sdarrenr if (nat == NULL) { 1823255332Scy nat = softn->ipf_nat_instances; 182460852Sdarrenr if (nat == NULL) { 1825255332Scy if (softn->ipf_nat_instances == NULL) { 1826255332Scy IPFERROR(60030); 1827145522Sdarrenr error = ENOENT; 1828255332Scy } 1829145522Sdarrenr goto finished; 183060852Sdarrenr } 183160852Sdarrenr } else { 183260852Sdarrenr /* 183360852Sdarrenr * Make sure the pointer we're copying from exists in the 183460852Sdarrenr * current list of entries. Security precaution to prevent 183560852Sdarrenr * copying of random kernel data. 183660852Sdarrenr */ 1837255332Scy for (n = softn->ipf_nat_instances; n; n = n->nat_next) 183860852Sdarrenr if (n == nat) 183960852Sdarrenr break; 1840145522Sdarrenr if (n == NULL) { 1841255332Scy IPFERROR(60031); 1842145522Sdarrenr error = ESRCH; 1843145522Sdarrenr goto finished; 1844145522Sdarrenr } 184560852Sdarrenr } 1846145522Sdarrenr ipn->ipn_next = nat->nat_next; 184760852Sdarrenr 1848145522Sdarrenr /* 1849145522Sdarrenr * Copy the NAT structure. 1850145522Sdarrenr */ 1851145522Sdarrenr bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat)); 185260852Sdarrenr 1853145522Sdarrenr /* 1854145522Sdarrenr * If we have a pointer to the NAT rule it belongs to, save that too. 1855145522Sdarrenr */ 1856145522Sdarrenr if (nat->nat_ptr != NULL) 1857145522Sdarrenr bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat, 1858255332Scy ipn->ipn_ipnat.in_size); 185960852Sdarrenr 1860145522Sdarrenr /* 1861145522Sdarrenr * If we also know the NAT entry has an associated filter rule, 1862145522Sdarrenr * save that too. 1863145522Sdarrenr */ 1864145522Sdarrenr if (nat->nat_fr != NULL) 1865145522Sdarrenr bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr, 1866145522Sdarrenr sizeof(ipn->ipn_fr)); 186760852Sdarrenr 1868145522Sdarrenr /* 1869145522Sdarrenr * Last but not least, if there is an application proxy session set 1870145522Sdarrenr * up for this NAT entry, then copy that out too, including any 1871145522Sdarrenr * private data saved along side it by the proxy. 1872145522Sdarrenr */ 1873145522Sdarrenr aps = nat->nat_aps; 1874145522Sdarrenr outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data); 1875145522Sdarrenr if (aps != NULL) { 1876145522Sdarrenr char *s; 187760852Sdarrenr 1878145522Sdarrenr if (outsize < sizeof(*aps)) { 1879255332Scy IPFERROR(60032); 1880145522Sdarrenr error = ENOBUFS; 1881145522Sdarrenr goto finished; 188260852Sdarrenr } 1883145522Sdarrenr 1884145522Sdarrenr s = ipn->ipn_data; 1885145522Sdarrenr bcopy((char *)aps, s, sizeof(*aps)); 1886145522Sdarrenr s += sizeof(*aps); 1887145522Sdarrenr outsize -= sizeof(*aps); 1888145522Sdarrenr if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz)) 1889145522Sdarrenr bcopy(aps->aps_data, s, aps->aps_psiz); 1890255332Scy else { 1891255332Scy IPFERROR(60033); 1892145522Sdarrenr error = ENOBUFS; 1893255332Scy } 189460852Sdarrenr } 1895145522Sdarrenr if (error == 0) { 1896255332Scy error = ipf_outobjsz(softc, data, ipn, IPFOBJ_NATSAVE, 1897255332Scy ipns.ipn_dsize); 1898145522Sdarrenr } 1899145522Sdarrenr 1900145522Sdarrenrfinished: 1901145522Sdarrenr if (ipn != NULL) { 1902145522Sdarrenr KFREES(ipn, ipns.ipn_dsize); 1903145522Sdarrenr } 1904344113Scy if (getlock) { 1905344113Scy RWLOCK_EXIT(&softc->ipf_nat); 1906344113Scy } 190764580Sdarrenr return error; 190860852Sdarrenr} 190960852Sdarrenr 191060852Sdarrenr 1911145522Sdarrenr/* ------------------------------------------------------------------------ */ 1912255332Scy/* Function: ipf_nat_putent */ 1913145522Sdarrenr/* Returns: int - 0 == success, != 0 is the error value. */ 1914255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1915255332Scy/* data(I) - pointer to natget structure with NAT */ 1916255332Scy/* structure information to load into the kernel */ 1917145522Sdarrenr/* getlock(I) - flag indicating whether or not a write lock */ 1918255332Scy/* on is already held. */ 1919145522Sdarrenr/* */ 1920145522Sdarrenr/* Handle SIOCSTPUT. */ 1921145522Sdarrenr/* Loads a NAT table entry from user space, including a NAT rule, proxy and */ 1922145522Sdarrenr/* firewall rule data structures, if pointers to them indicate so. */ 1923145522Sdarrenr/* ------------------------------------------------------------------------ */ 1924255332Scystatic int 1925255332Scyipf_nat_putent(softc, data, getlock) 1926255332Scy ipf_main_softc_t *softc; 1927255332Scy caddr_t data; 1928255332Scy int getlock; 192960852Sdarrenr{ 1930255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 1931145522Sdarrenr nat_save_t ipn, *ipnn; 193260852Sdarrenr ap_session_t *aps; 1933145522Sdarrenr nat_t *n, *nat; 193460852Sdarrenr frentry_t *fr; 1935145522Sdarrenr fr_info_t fin; 193660852Sdarrenr ipnat_t *in; 193760852Sdarrenr int error; 193860852Sdarrenr 1939255332Scy error = ipf_inobj(softc, data, NULL, &ipn, IPFOBJ_NATSAVE); 1940145522Sdarrenr if (error != 0) 1941145522Sdarrenr return error; 1942145522Sdarrenr 1943145522Sdarrenr /* 1944145522Sdarrenr * Initialise early because of code at junkput label. 1945145522Sdarrenr */ 1946255332Scy n = NULL; 1947145522Sdarrenr in = NULL; 1948145522Sdarrenr aps = NULL; 194964580Sdarrenr nat = NULL; 1950145522Sdarrenr ipnn = NULL; 1951170268Sdarrenr fr = NULL; 1952145522Sdarrenr 1953145522Sdarrenr /* 1954145522Sdarrenr * New entry, copy in the rest of the NAT entry if it's size is more 1955145522Sdarrenr * than just the nat_t structure. 1956145522Sdarrenr */ 1957145522Sdarrenr if (ipn.ipn_dsize > sizeof(ipn)) { 1958145522Sdarrenr if (ipn.ipn_dsize > 81920) { 1959255332Scy IPFERROR(60034); 1960145522Sdarrenr error = ENOMEM; 1961145522Sdarrenr goto junkput; 1962145522Sdarrenr } 1963145522Sdarrenr 1964145522Sdarrenr KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize); 1965255332Scy if (ipnn == NULL) { 1966255332Scy IPFERROR(60035); 196760852Sdarrenr return ENOMEM; 1968255332Scy } 1969145522Sdarrenr 1970255332Scy bzero(ipnn, ipn.ipn_dsize); 1971255332Scy error = ipf_inobjsz(softc, data, ipnn, IPFOBJ_NATSAVE, 1972255332Scy ipn.ipn_dsize); 1973145522Sdarrenr if (error != 0) { 197464580Sdarrenr goto junkput; 197564580Sdarrenr } 197660852Sdarrenr } else 1977145522Sdarrenr ipnn = &ipn; 197860852Sdarrenr 197960852Sdarrenr KMALLOC(nat, nat_t *); 198064580Sdarrenr if (nat == NULL) { 1981255332Scy IPFERROR(60037); 1982145522Sdarrenr error = ENOMEM; 198364580Sdarrenr goto junkput; 198464580Sdarrenr } 198560852Sdarrenr 1986145522Sdarrenr bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat)); 1987255332Scy 1988255332Scy switch (nat->nat_v[0]) 1989255332Scy { 1990255332Scy case 4: 1991255332Scy#ifdef USE_INET6 1992255332Scy case 6 : 1993255332Scy#endif 1994255332Scy break; 1995255332Scy default : 1996255332Scy IPFERROR(60061); 1997255332Scy error = EPROTONOSUPPORT; 1998255332Scy goto junkput; 1999255332Scy /*NOTREACHED*/ 2000255332Scy } 2001255332Scy 200260852Sdarrenr /* 2003255332Scy * Initialize all these so that ipf_nat_delete() doesn't cause a crash. 200460852Sdarrenr */ 2005145522Sdarrenr bzero((char *)nat, offsetof(struct nat, nat_tqe)); 2006145522Sdarrenr nat->nat_tqe.tqe_pnext = NULL; 2007145522Sdarrenr nat->nat_tqe.tqe_next = NULL; 2008145522Sdarrenr nat->nat_tqe.tqe_ifq = NULL; 2009145522Sdarrenr nat->nat_tqe.tqe_parent = nat; 201060852Sdarrenr 201160852Sdarrenr /* 201260852Sdarrenr * Restore the rule associated with this nat session 201360852Sdarrenr */ 2014145522Sdarrenr in = ipnn->ipn_nat.nat_ptr; 2015145522Sdarrenr if (in != NULL) { 2016255332Scy KMALLOCS(in, ipnat_t *, ipnn->ipn_ipnat.in_size); 2017145522Sdarrenr nat->nat_ptr = in; 201860852Sdarrenr if (in == NULL) { 2019255332Scy IPFERROR(60038); 202060852Sdarrenr error = ENOMEM; 202160852Sdarrenr goto junkput; 202260852Sdarrenr } 2023255332Scy bcopy((char *)&ipnn->ipn_ipnat, (char *)in, 2024255332Scy ipnn->ipn_ipnat.in_size); 202560852Sdarrenr in->in_use = 1; 202660852Sdarrenr in->in_flags |= IPN_DELETE; 2027145522Sdarrenr 2028255332Scy ATOMIC_INC32(softn->ipf_nat_stats.ns_rules); 2029145522Sdarrenr 2030255332Scy if (ipf_nat_resolverule(softc, in) != 0) { 2031255332Scy IPFERROR(60039); 2032161356Sguido error = ESRCH; 2033161356Sguido goto junkput; 2034161356Sguido } 2035145522Sdarrenr } 2036145522Sdarrenr 2037145522Sdarrenr /* 2038145522Sdarrenr * Check that the NAT entry doesn't already exist in the kernel. 2039161356Sguido * 2040161356Sguido * For NAT_OUTBOUND, we're lookup for a duplicate MAP entry. To do 2041161356Sguido * this, we check to see if the inbound combination of addresses and 2042161356Sguido * ports is already known. Similar logic is applied for NAT_INBOUND. 2043255332Scy * 2044145522Sdarrenr */ 2045145522Sdarrenr bzero((char *)&fin, sizeof(fin)); 2046255332Scy fin.fin_v = nat->nat_v[0]; 2047255332Scy fin.fin_p = nat->nat_pr[0]; 2048255332Scy fin.fin_rev = nat->nat_rev; 2049255332Scy fin.fin_ifp = nat->nat_ifps[0]; 2050255332Scy fin.fin_data[0] = ntohs(nat->nat_ndport); 2051255332Scy fin.fin_data[1] = ntohs(nat->nat_nsport); 2052255332Scy 2053255332Scy switch (nat->nat_dir) 2054255332Scy { 2055255332Scy case NAT_OUTBOUND : 2056255332Scy case NAT_DIVERTOUT : 2057153876Sguido if (getlock) { 2058255332Scy READ_ENTER(&softc->ipf_nat); 2059153876Sguido } 2060255332Scy 2061255332Scy fin.fin_v = nat->nat_v[1]; 2062255332Scy if (nat->nat_v[1] == 4) { 2063255332Scy n = ipf_nat_inlookup(&fin, nat->nat_flags, fin.fin_p, 2064255332Scy nat->nat_ndstip, nat->nat_nsrcip); 2065255332Scy#ifdef USE_INET6 2066255332Scy } else if (nat->nat_v[1] == 6) { 2067255332Scy n = ipf_nat6_inlookup(&fin, nat->nat_flags, fin.fin_p, 2068255332Scy &nat->nat_ndst6.in6, 2069255332Scy &nat->nat_nsrc6.in6); 2070255332Scy#endif 2071255332Scy } 2072255332Scy 2073153876Sguido if (getlock) { 2074255332Scy RWLOCK_EXIT(&softc->ipf_nat); 2075153876Sguido } 2076153876Sguido if (n != NULL) { 2077255332Scy IPFERROR(60040); 2078145522Sdarrenr error = EEXIST; 2079145522Sdarrenr goto junkput; 208060852Sdarrenr } 2081255332Scy break; 2082255332Scy 2083255332Scy case NAT_INBOUND : 2084255332Scy case NAT_DIVERTIN : 2085153876Sguido if (getlock) { 2086255332Scy READ_ENTER(&softc->ipf_nat); 2087153876Sguido } 2088255332Scy 2089255332Scy if (fin.fin_v == 4) { 2090255332Scy n = ipf_nat_outlookup(&fin, nat->nat_flags, fin.fin_p, 2091255332Scy nat->nat_ndstip, 2092255332Scy nat->nat_nsrcip); 2093255332Scy#ifdef USE_INET6 2094255332Scy } else if (fin.fin_v == 6) { 2095255332Scy n = ipf_nat6_outlookup(&fin, nat->nat_flags, fin.fin_p, 2096255332Scy &nat->nat_ndst6.in6, 2097255332Scy &nat->nat_nsrc6.in6); 2098255332Scy#endif 2099255332Scy } 2100255332Scy 2101153876Sguido if (getlock) { 2102255332Scy RWLOCK_EXIT(&softc->ipf_nat); 2103153876Sguido } 2104153876Sguido if (n != NULL) { 2105255332Scy IPFERROR(60041); 2106145522Sdarrenr error = EEXIST; 2107145522Sdarrenr goto junkput; 2108145522Sdarrenr } 2109255332Scy break; 2110255332Scy 2111255332Scy default : 2112255332Scy IPFERROR(60042); 2113145522Sdarrenr error = EINVAL; 2114145522Sdarrenr goto junkput; 211560852Sdarrenr } 211660852Sdarrenr 211760852Sdarrenr /* 211860852Sdarrenr * Restore ap_session_t structure. Include the private data allocated 211960852Sdarrenr * if it was there. 212060852Sdarrenr */ 2121145522Sdarrenr aps = nat->nat_aps; 2122145522Sdarrenr if (aps != NULL) { 212360852Sdarrenr KMALLOC(aps, ap_session_t *); 2124145522Sdarrenr nat->nat_aps = aps; 212560852Sdarrenr if (aps == NULL) { 2126255332Scy IPFERROR(60043); 212760852Sdarrenr error = ENOMEM; 212860852Sdarrenr goto junkput; 212960852Sdarrenr } 213060852Sdarrenr bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps)); 2131145522Sdarrenr if (in != NULL) 213260852Sdarrenr aps->aps_apr = in->in_apr; 2133145522Sdarrenr else 2134145522Sdarrenr aps->aps_apr = NULL; 2135145522Sdarrenr if (aps->aps_psiz != 0) { 2136145522Sdarrenr if (aps->aps_psiz > 81920) { 2137255332Scy IPFERROR(60044); 2138145522Sdarrenr error = ENOMEM; 2139145522Sdarrenr goto junkput; 2140145522Sdarrenr } 214160852Sdarrenr KMALLOCS(aps->aps_data, void *, aps->aps_psiz); 214260852Sdarrenr if (aps->aps_data == NULL) { 2143255332Scy IPFERROR(60045); 214460852Sdarrenr error = ENOMEM; 214560852Sdarrenr goto junkput; 214660852Sdarrenr } 214760852Sdarrenr bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data, 214860852Sdarrenr aps->aps_psiz); 214960852Sdarrenr } else { 215060852Sdarrenr aps->aps_psiz = 0; 215160852Sdarrenr aps->aps_data = NULL; 215260852Sdarrenr } 215360852Sdarrenr } 215460852Sdarrenr 215560852Sdarrenr /* 215660852Sdarrenr * If there was a filtering rule associated with this entry then 215760852Sdarrenr * build up a new one. 215860852Sdarrenr */ 2159145522Sdarrenr fr = nat->nat_fr; 216060852Sdarrenr if (fr != NULL) { 2161145522Sdarrenr if ((nat->nat_flags & SI_NEWFR) != 0) { 216260852Sdarrenr KMALLOC(fr, frentry_t *); 216360852Sdarrenr nat->nat_fr = fr; 216460852Sdarrenr if (fr == NULL) { 2165255332Scy IPFERROR(60046); 216660852Sdarrenr error = ENOMEM; 216760852Sdarrenr goto junkput; 216860852Sdarrenr } 2169145522Sdarrenr ipnn->ipn_nat.nat_fr = fr; 2170145522Sdarrenr fr->fr_ref = 1; 2171255332Scy (void) ipf_outobj(softc, data, ipnn, IPFOBJ_NATSAVE); 2172145522Sdarrenr bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr)); 2173161356Sguido 2174161356Sguido fr->fr_ref = 1; 2175161356Sguido fr->fr_dsize = 0; 2176161356Sguido fr->fr_data = NULL; 2177161356Sguido fr->fr_type = FR_T_NONE; 2178161356Sguido 2179145522Sdarrenr MUTEX_NUKE(&fr->fr_lock); 2180145522Sdarrenr MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock"); 218160852Sdarrenr } else { 2182153876Sguido if (getlock) { 2183255332Scy READ_ENTER(&softc->ipf_nat); 2184153876Sguido } 2185255332Scy for (n = softn->ipf_nat_instances; n; n = n->nat_next) 218660852Sdarrenr if (n->nat_fr == fr) 218760852Sdarrenr break; 2188145522Sdarrenr 2189145522Sdarrenr if (n != NULL) { 2190145522Sdarrenr MUTEX_ENTER(&fr->fr_lock); 2191145522Sdarrenr fr->fr_ref++; 2192145522Sdarrenr MUTEX_EXIT(&fr->fr_lock); 2193145522Sdarrenr } 2194153876Sguido if (getlock) { 2195255332Scy RWLOCK_EXIT(&softc->ipf_nat); 2196153876Sguido } 2197145522Sdarrenr 2198255332Scy if (n == NULL) { 2199255332Scy IPFERROR(60047); 220060852Sdarrenr error = ESRCH; 220160852Sdarrenr goto junkput; 220260852Sdarrenr } 220360852Sdarrenr } 220460852Sdarrenr } 220560852Sdarrenr 2206145522Sdarrenr if (ipnn != &ipn) { 2207145522Sdarrenr KFREES(ipnn, ipn.ipn_dsize); 2208145522Sdarrenr ipnn = NULL; 2209145522Sdarrenr } 2210145522Sdarrenr 2211145522Sdarrenr if (getlock) { 2212255332Scy WRITE_ENTER(&softc->ipf_nat); 2213145522Sdarrenr } 2214255332Scy 2215255332Scy if (fin.fin_v == 4) 2216255332Scy error = ipf_nat_finalise(&fin, nat); 2217255332Scy#ifdef USE_INET6 2218255332Scy else 2219255332Scy error = ipf_nat6_finalise(&fin, nat); 2220255332Scy#endif 2221255332Scy 2222145522Sdarrenr if (getlock) { 2223255332Scy RWLOCK_EXIT(&softc->ipf_nat); 2224145522Sdarrenr } 2225145522Sdarrenr 2226145522Sdarrenr if (error == 0) 2227145522Sdarrenr return 0; 2228145522Sdarrenr 2229255332Scy IPFERROR(60048); 2230145522Sdarrenr error = ENOMEM; 2231145522Sdarrenr 223260852Sdarrenrjunkput: 2233255332Scy if (fr != NULL) { 2234255332Scy (void) ipf_derefrule(softc, &fr); 2235255332Scy } 2236145522Sdarrenr 2237145522Sdarrenr if ((ipnn != NULL) && (ipnn != &ipn)) { 2238145522Sdarrenr KFREES(ipnn, ipn.ipn_dsize); 2239145522Sdarrenr } 2240145522Sdarrenr if (nat != NULL) { 2241145522Sdarrenr if (aps != NULL) { 2242145522Sdarrenr if (aps->aps_data != NULL) { 2243145522Sdarrenr KFREES(aps->aps_data, aps->aps_psiz); 2244145522Sdarrenr } 2245145522Sdarrenr KFREE(aps); 2246145522Sdarrenr } 2247145522Sdarrenr if (in != NULL) { 2248145522Sdarrenr if (in->in_apr) 2249255332Scy ipf_proxy_deref(in->in_apr); 2250255332Scy KFREES(in, in->in_size); 2251145522Sdarrenr } 2252145522Sdarrenr KFREE(nat); 2253145522Sdarrenr } 225460852Sdarrenr return error; 225560852Sdarrenr} 225660852Sdarrenr 225760852Sdarrenr 2258145522Sdarrenr/* ------------------------------------------------------------------------ */ 2259255332Scy/* Function: ipf_nat_delete */ 2260145522Sdarrenr/* Returns: Nil */ 2261255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 2262255332Scy/* nat(I) - pointer to NAT structure to delete */ 2263145522Sdarrenr/* logtype(I) - type of LOG record to create before deleting */ 2264145522Sdarrenr/* Write Lock: ipf_nat */ 2265145522Sdarrenr/* */ 2266145522Sdarrenr/* Delete a nat entry from the various lists and table. If NAT logging is */ 2267145522Sdarrenr/* enabled then generate a NAT log record for this event. */ 2268145522Sdarrenr/* ------------------------------------------------------------------------ */ 2269255332Scyvoid 2270255332Scyipf_nat_delete(softc, nat, logtype) 2271255332Scy ipf_main_softc_t *softc; 2272255332Scy struct nat *nat; 2273255332Scy int logtype; 227453642Sguido{ 2275255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 2276255332Scy int madeorphan = 0, bkt, removed = 0; 2277255332Scy nat_stat_side_t *nss; 227853642Sguido struct ipnat *ipn; 227953642Sguido 2280255332Scy if (logtype != 0 && softn->ipf_nat_logging != 0) 2281255332Scy ipf_nat_log(softc, softn, nat, logtype); 228253642Sguido 2283145522Sdarrenr /* 2284145522Sdarrenr * Take it as a general indication that all the pointers are set if 2285145522Sdarrenr * nat_pnext is set. 2286145522Sdarrenr */ 2287145522Sdarrenr if (nat->nat_pnext != NULL) { 2288172776Sdarrenr removed = 1; 2289172776Sdarrenr 2290255332Scy bkt = nat->nat_hv[0] % softn->ipf_nat_table_sz; 2291255332Scy nss = &softn->ipf_nat_stats.ns_side[0]; 2292338171Scy if (nss->ns_bucketlen[bkt] > 0) 2293338171Scy nss->ns_bucketlen[bkt]--; 2294255332Scy if (nss->ns_bucketlen[bkt] == 0) { 2295255332Scy nss->ns_inuse--; 2296255332Scy } 2297145522Sdarrenr 2298255332Scy bkt = nat->nat_hv[1] % softn->ipf_nat_table_sz; 2299255332Scy nss = &softn->ipf_nat_stats.ns_side[1]; 2300338171Scy if (nss->ns_bucketlen[bkt] > 0) 2301338171Scy nss->ns_bucketlen[bkt]--; 2302255332Scy if (nss->ns_bucketlen[bkt] == 0) { 2303255332Scy nss->ns_inuse--; 2304255332Scy } 2305255332Scy 2306145522Sdarrenr *nat->nat_pnext = nat->nat_next; 2307145522Sdarrenr if (nat->nat_next != NULL) { 2308145522Sdarrenr nat->nat_next->nat_pnext = nat->nat_pnext; 2309145522Sdarrenr nat->nat_next = NULL; 2310145522Sdarrenr } 2311145522Sdarrenr nat->nat_pnext = NULL; 2312145522Sdarrenr 2313145522Sdarrenr *nat->nat_phnext[0] = nat->nat_hnext[0]; 2314145522Sdarrenr if (nat->nat_hnext[0] != NULL) { 2315145522Sdarrenr nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 2316145522Sdarrenr nat->nat_hnext[0] = NULL; 2317145522Sdarrenr } 2318145522Sdarrenr nat->nat_phnext[0] = NULL; 2319145522Sdarrenr 2320145522Sdarrenr *nat->nat_phnext[1] = nat->nat_hnext[1]; 2321145522Sdarrenr if (nat->nat_hnext[1] != NULL) { 2322145522Sdarrenr nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 2323145522Sdarrenr nat->nat_hnext[1] = NULL; 2324145522Sdarrenr } 2325145522Sdarrenr nat->nat_phnext[1] = NULL; 2326145522Sdarrenr 2327255332Scy if ((nat->nat_flags & SI_WILDP) != 0) { 2328255332Scy ATOMIC_DEC32(softn->ipf_nat_stats.ns_wilds); 2329255332Scy } 2330255332Scy madeorphan = 1; 233153642Sguido } 233260852Sdarrenr 2333145522Sdarrenr if (nat->nat_me != NULL) { 2334145522Sdarrenr *nat->nat_me = NULL; 2335145522Sdarrenr nat->nat_me = NULL; 2336255332Scy nat->nat_ref--; 2337255332Scy ASSERT(nat->nat_ref >= 0); 2338145522Sdarrenr } 233960852Sdarrenr 2340255332Scy if (nat->nat_tqe.tqe_ifq != NULL) { 2341255332Scy /* 2342255332Scy * No call to ipf_freetimeoutqueue() is made here, they are 2343255332Scy * garbage collected in ipf_nat_expire(). 2344255332Scy */ 2345255332Scy (void) ipf_deletequeueentry(&nat->nat_tqe); 2346255332Scy } 2347145522Sdarrenr 2348255332Scy if (nat->nat_sync) { 2349255332Scy ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); 2350255332Scy nat->nat_sync = NULL; 2351255332Scy } 2352255332Scy 2353170268Sdarrenr if (logtype == NL_EXPIRE) 2354255332Scy softn->ipf_nat_stats.ns_expire++; 2355170268Sdarrenr 2356172776Sdarrenr MUTEX_ENTER(&nat->nat_lock); 2357172776Sdarrenr /* 2358172776Sdarrenr * NL_DESTROY should only be passed in when we've got nat_ref >= 2. 2359172776Sdarrenr * This happens when a nat'd packet is blocked and we want to throw 2360172776Sdarrenr * away the NAT session. 2361172776Sdarrenr */ 2362172776Sdarrenr if (logtype == NL_DESTROY) { 2363172776Sdarrenr if (nat->nat_ref > 2) { 2364172776Sdarrenr nat->nat_ref -= 2; 2365172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 2366172776Sdarrenr if (removed) 2367255332Scy softn->ipf_nat_stats.ns_orphans++; 2368172776Sdarrenr return; 2369172776Sdarrenr } 2370172776Sdarrenr } else if (nat->nat_ref > 1) { 2371172776Sdarrenr nat->nat_ref--; 2372172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 2373255332Scy if (madeorphan == 1) 2374255332Scy softn->ipf_nat_stats.ns_orphans++; 2375145522Sdarrenr return; 2376145522Sdarrenr } 2377255332Scy ASSERT(nat->nat_ref >= 0); 2378172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 2379170268Sdarrenr 2380255332Scy nat->nat_ref = 0; 2381255332Scy 2382255332Scy if (madeorphan == 0) 2383255332Scy softn->ipf_nat_stats.ns_orphans--; 2384255332Scy 2385161356Sguido /* 2386255332Scy * At this point, nat_ref can be either 0 or -1 2387161356Sguido */ 2388255332Scy softn->ipf_nat_stats.ns_proto[nat->nat_pr[0]]--; 2389145522Sdarrenr 2390255332Scy if (nat->nat_fr != NULL) { 2391255332Scy (void) ipf_derefrule(softc, &nat->nat_fr); 2392255332Scy } 2393145522Sdarrenr 2394255332Scy if (nat->nat_hm != NULL) { 2395255332Scy ipf_nat_hostmapdel(softc, &nat->nat_hm); 2396255332Scy } 2397145522Sdarrenr 239853642Sguido /* 239953642Sguido * If there is an active reference from the nat entry to its parent 240053642Sguido * rule, decrement the rule's reference count and free it too if no 240153642Sguido * longer being used. 240253642Sguido */ 2403145522Sdarrenr ipn = nat->nat_ptr; 2404255332Scy nat->nat_ptr = NULL; 2405255332Scy 240653642Sguido if (ipn != NULL) { 2407255332Scy ipn->in_space++; 2408255332Scy ipf_nat_rule_deref(softc, &ipn); 240953642Sguido } 241053642Sguido 2411255332Scy if (nat->nat_aps != NULL) { 2412255332Scy ipf_proxy_free(softc, nat->nat_aps); 2413255332Scy nat->nat_aps = NULL; 2414255332Scy } 2415255332Scy 2416145522Sdarrenr MUTEX_DESTROY(&nat->nat_lock); 2417145522Sdarrenr 2418255332Scy softn->ipf_nat_stats.ns_active--; 2419145522Sdarrenr 242053642Sguido /* 242153642Sguido * If there's a fragment table entry too for this nat entry, then 2422145522Sdarrenr * dereference that as well. This is after nat_lock is released 2423145522Sdarrenr * because of Tru64. 242453642Sguido */ 2425255332Scy ipf_frag_natforget(softc, (void *)nat); 2426145522Sdarrenr 2427145522Sdarrenr KFREE(nat); 242853642Sguido} 242953642Sguido 243053642Sguido 2431145522Sdarrenr/* ------------------------------------------------------------------------ */ 2432255332Scy/* Function: ipf_nat_flushtable */ 2433145522Sdarrenr/* Returns: int - number of NAT rules deleted */ 2434255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 2435255332Scy/* softn(I) - pointer to NAT context structure */ 2436255332Scy/* Write Lock: ipf_nat */ 2437145522Sdarrenr/* */ 2438145522Sdarrenr/* Deletes all currently active NAT sessions. In deleting each NAT entry a */ 2439255332Scy/* log record should be emitted in ipf_nat_delete() if NAT logging is */ 2440255332Scy/* enabled. */ 2441145522Sdarrenr/* ------------------------------------------------------------------------ */ 244253642Sguido/* 244353642Sguido * nat_flushtable - clear the NAT table of all mapping entries. 244453642Sguido */ 2445255332Scystatic int 2446255332Scyipf_nat_flushtable(softc, softn) 2447255332Scy ipf_main_softc_t *softc; 2448255332Scy ipf_nat_softc_t *softn; 244953642Sguido{ 2450145522Sdarrenr nat_t *nat; 2451145522Sdarrenr int j = 0; 245267614Sdarrenr 245353642Sguido /* 245453642Sguido * ALL NAT mappings deleted, so lets just make the deletions 245553642Sguido * quicker. 245653642Sguido */ 2457255332Scy if (softn->ipf_nat_table[0] != NULL) 2458255332Scy bzero((char *)softn->ipf_nat_table[0], 2459255332Scy sizeof(softn->ipf_nat_table[0]) * 2460255332Scy softn->ipf_nat_table_sz); 2461255332Scy if (softn->ipf_nat_table[1] != NULL) 2462255332Scy bzero((char *)softn->ipf_nat_table[1], 2463255332Scy sizeof(softn->ipf_nat_table[1]) * 2464255332Scy softn->ipf_nat_table_sz); 246553642Sguido 2466255332Scy while ((nat = softn->ipf_nat_instances) != NULL) { 2467255332Scy ipf_nat_delete(softc, nat, NL_FLUSH); 246853642Sguido j++; 246953642Sguido } 2470145522Sdarrenr 247153642Sguido return j; 247253642Sguido} 247353642Sguido 247453642Sguido 2475145522Sdarrenr/* ------------------------------------------------------------------------ */ 2476255332Scy/* Function: ipf_nat_clearlist */ 2477145522Sdarrenr/* Returns: int - number of NAT/RDR rules deleted */ 2478255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 2479255332Scy/* softn(I) - pointer to NAT context structure */ 2480145522Sdarrenr/* */ 2481145522Sdarrenr/* Delete all rules in the current list of rules. There is nothing elegant */ 2482145522Sdarrenr/* about this cleanup: simply free all entries on the list of rules and */ 2483145522Sdarrenr/* clear out the tables used for hashed NAT rule lookups. */ 2484145522Sdarrenr/* ------------------------------------------------------------------------ */ 2485255332Scystatic int 2486255332Scyipf_nat_clearlist(softc, softn) 2487255332Scy ipf_main_softc_t *softc; 2488255332Scy ipf_nat_softc_t *softn; 248953642Sguido{ 2490255332Scy ipnat_t *n; 249153642Sguido int i = 0; 249253642Sguido 2493255332Scy if (softn->ipf_nat_map_rules != NULL) { 2494255332Scy bzero((char *)softn->ipf_nat_map_rules, 2495255332Scy sizeof(*softn->ipf_nat_map_rules) * 2496255332Scy softn->ipf_nat_maprules_sz); 2497255332Scy } 2498255332Scy if (softn->ipf_nat_rdr_rules != NULL) { 2499255332Scy bzero((char *)softn->ipf_nat_rdr_rules, 2500255332Scy sizeof(*softn->ipf_nat_rdr_rules) * 2501255332Scy softn->ipf_nat_rdrrules_sz); 2502255332Scy } 250353642Sguido 2504255332Scy while ((n = softn->ipf_nat_list) != NULL) { 2505255332Scy ipf_nat_delrule(softc, softn, n, 0); 250653642Sguido i++; 250753642Sguido } 2508255332Scy#if SOLARIS && !defined(INSTANCES) 2509145522Sdarrenr pfil_delayed_copy = 1; 2510145522Sdarrenr#endif 251153642Sguido return i; 251253642Sguido} 251353642Sguido 251453642Sguido 2515145522Sdarrenr/* ------------------------------------------------------------------------ */ 2516255332Scy/* Function: ipf_nat_delrule */ 2517255332Scy/* Returns: Nil */ 2518255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 2519255332Scy/* softn(I) - pointer to NAT context structure */ 2520255332Scy/* np(I) - pointer to NAT rule to delete */ 2521255332Scy/* purge(I) - 1 == allow purge, 0 == prevent purge */ 2522255332Scy/* Locks: WRITE(ipf_nat) */ 2523255332Scy/* */ 2524255332Scy/* Preventing "purge" from occuring is allowed because when all of the NAT */ 2525255332Scy/* rules are being removed, allowing the "purge" to walk through the list */ 2526255332Scy/* of NAT sessions, possibly multiple times, would be a large performance */ 2527255332Scy/* hit, on the order of O(N^2). */ 2528255332Scy/* ------------------------------------------------------------------------ */ 2529255332Scystatic void 2530255332Scyipf_nat_delrule(softc, softn, np, purge) 2531255332Scy ipf_main_softc_t *softc; 2532255332Scy ipf_nat_softc_t *softn; 2533255332Scy ipnat_t *np; 2534255332Scy int purge; 2535255332Scy{ 2536255332Scy 2537255332Scy if (np->in_pnext != NULL) { 2538255332Scy *np->in_pnext = np->in_next; 2539255332Scy if (np->in_next != NULL) 2540255332Scy np->in_next->in_pnext = np->in_pnext; 2541255332Scy if (softn->ipf_nat_list_tail == &np->in_next) 2542255332Scy softn->ipf_nat_list_tail = np->in_pnext; 2543255332Scy } 2544255332Scy 2545255332Scy if ((purge == 1) && ((np->in_flags & IPN_PURGE) != 0)) { 2546255332Scy nat_t *next; 2547255332Scy nat_t *nat; 2548255332Scy 2549255332Scy for (next = softn->ipf_nat_instances; (nat = next) != NULL;) { 2550255332Scy next = nat->nat_next; 2551255332Scy if (nat->nat_ptr == np) 2552255332Scy ipf_nat_delete(softc, nat, NL_PURGE); 2553255332Scy } 2554255332Scy } 2555255332Scy 2556255332Scy if ((np->in_flags & IPN_DELETE) == 0) { 2557255332Scy if (np->in_redir & NAT_REDIRECT) { 2558255332Scy switch (np->in_v[0]) 2559255332Scy { 2560255332Scy case 4 : 2561255332Scy ipf_nat_delrdr(softn, np); 2562255332Scy break; 2563255332Scy#ifdef USE_INET6 2564255332Scy case 6 : 2565255332Scy ipf_nat6_delrdr(softn, np); 2566255332Scy break; 2567255332Scy#endif 2568255332Scy } 2569255332Scy } 2570255332Scy if (np->in_redir & (NAT_MAPBLK|NAT_MAP)) { 2571255332Scy switch (np->in_v[0]) 2572255332Scy { 2573255332Scy case 4 : 2574255332Scy ipf_nat_delmap(softn, np); 2575255332Scy break; 2576255332Scy#ifdef USE_INET6 2577255332Scy case 6 : 2578255332Scy ipf_nat6_delmap(softn, np); 2579255332Scy break; 2580255332Scy#endif 2581255332Scy } 2582255332Scy } 2583255332Scy } 2584255332Scy 2585255332Scy np->in_flags |= IPN_DELETE; 2586255332Scy ipf_nat_rule_deref(softc, &np); 2587255332Scy} 2588255332Scy 2589255332Scy 2590255332Scy/* ------------------------------------------------------------------------ */ 2591255332Scy/* Function: ipf_nat_newmap */ 2592145522Sdarrenr/* Returns: int - -1 == error, 0 == success */ 2593145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2594145522Sdarrenr/* nat(I) - pointer to NAT entry */ 2595145522Sdarrenr/* ni(I) - pointer to structure with misc. information needed */ 2596145522Sdarrenr/* to create new NAT entry. */ 2597145522Sdarrenr/* */ 2598145522Sdarrenr/* Given an empty NAT structure, populate it with new information about a */ 2599145522Sdarrenr/* new NAT session, as defined by the matching NAT rule. */ 2600145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 2601145522Sdarrenr/* to the new IP address for the translation. */ 2602145522Sdarrenr/* ------------------------------------------------------------------------ */ 2603255332Scystatic int 2604255332Scyipf_nat_newmap(fin, nat, ni) 2605255332Scy fr_info_t *fin; 2606255332Scy nat_t *nat; 2607255332Scy natinfo_t *ni; 2608145522Sdarrenr{ 2609255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 2610255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 2611145522Sdarrenr u_short st_port, dport, sport, port, sp, dp; 2612145522Sdarrenr struct in_addr in, inb; 2613145522Sdarrenr hostmap_t *hm; 2614145522Sdarrenr u_32_t flags; 2615145522Sdarrenr u_32_t st_ip; 2616145522Sdarrenr ipnat_t *np; 2617145522Sdarrenr nat_t *natl; 2618145522Sdarrenr int l; 2619145522Sdarrenr 2620145522Sdarrenr /* 2621145522Sdarrenr * If it's an outbound packet which doesn't match any existing 2622145522Sdarrenr * record, then create a new port 2623145522Sdarrenr */ 2624145522Sdarrenr l = 0; 2625145522Sdarrenr hm = NULL; 2626145522Sdarrenr np = ni->nai_np; 2627255332Scy st_ip = np->in_snip; 2628255332Scy st_port = np->in_spnext; 2629255332Scy flags = nat->nat_flags; 2630145522Sdarrenr 2631255332Scy if (flags & IPN_ICMPQUERY) { 2632255332Scy sport = fin->fin_data[1]; 2633255332Scy dport = 0; 2634255332Scy } else { 2635255332Scy sport = htons(fin->fin_data[0]); 2636255332Scy dport = htons(fin->fin_data[1]); 2637255332Scy } 2638255332Scy 2639145522Sdarrenr /* 2640145522Sdarrenr * Do a loop until we either run out of entries to try or we find 2641145522Sdarrenr * a NAT mapping that isn't currently being used. This is done 2642145522Sdarrenr * because the change to the source is not (usually) being fixed. 2643145522Sdarrenr */ 2644145522Sdarrenr do { 2645145522Sdarrenr port = 0; 2646255332Scy in.s_addr = htonl(np->in_snip); 2647145522Sdarrenr if (l == 0) { 2648145522Sdarrenr /* 2649145522Sdarrenr * Check to see if there is an existing NAT 2650145522Sdarrenr * setup for this IP address pair. 2651145522Sdarrenr */ 2652255332Scy hm = ipf_nat_hostmap(softn, np, fin->fin_src, 2653255332Scy fin->fin_dst, in, 0); 2654145522Sdarrenr if (hm != NULL) 2655255332Scy in.s_addr = hm->hm_nsrcip.s_addr; 2656145522Sdarrenr } else if ((l == 1) && (hm != NULL)) { 2657255332Scy ipf_nat_hostmapdel(softc, &hm); 2658145522Sdarrenr } 2659145522Sdarrenr in.s_addr = ntohl(in.s_addr); 2660145522Sdarrenr 2661145522Sdarrenr nat->nat_hm = hm; 2662145522Sdarrenr 2663255332Scy if ((np->in_nsrcmsk == 0xffffffff) && (np->in_spnext == 0)) { 2664255332Scy if (l > 0) { 2665255332Scy NBUMPSIDEX(1, ns_exhausted, ns_exhausted_1); 2666338170Scy DT4(ns_exhausted_1, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); 2667145522Sdarrenr return -1; 2668255332Scy } 2669145522Sdarrenr } 2670145522Sdarrenr 2671145522Sdarrenr if (np->in_redir == NAT_BIMAP && 2672255332Scy np->in_osrcmsk == np->in_nsrcmsk) { 2673145522Sdarrenr /* 2674145522Sdarrenr * map the address block in a 1:1 fashion 2675145522Sdarrenr */ 2676255332Scy in.s_addr = np->in_nsrcaddr; 2677255332Scy in.s_addr |= fin->fin_saddr & ~np->in_osrcmsk; 2678145522Sdarrenr in.s_addr = ntohl(in.s_addr); 2679145522Sdarrenr 2680145522Sdarrenr } else if (np->in_redir & NAT_MAPBLK) { 2681145522Sdarrenr if ((l >= np->in_ppip) || ((l > 0) && 2682255332Scy !(flags & IPN_TCPUDP))) { 2683255332Scy NBUMPSIDEX(1, ns_exhausted, ns_exhausted_2); 2684338170Scy DT4(ns_exhausted_2, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); 2685145522Sdarrenr return -1; 2686255332Scy } 2687145522Sdarrenr /* 2688145522Sdarrenr * map-block - Calculate destination address. 2689145522Sdarrenr */ 2690145522Sdarrenr in.s_addr = ntohl(fin->fin_saddr); 2691255332Scy in.s_addr &= ntohl(~np->in_osrcmsk); 2692145522Sdarrenr inb.s_addr = in.s_addr; 2693145522Sdarrenr in.s_addr /= np->in_ippip; 2694255332Scy in.s_addr &= ntohl(~np->in_nsrcmsk); 2695255332Scy in.s_addr += ntohl(np->in_nsrcaddr); 2696145522Sdarrenr /* 2697145522Sdarrenr * Calculate destination port. 2698145522Sdarrenr */ 2699145522Sdarrenr if ((flags & IPN_TCPUDP) && 2700145522Sdarrenr (np->in_ppip != 0)) { 2701145522Sdarrenr port = ntohs(sport) + l; 2702145522Sdarrenr port %= np->in_ppip; 2703145522Sdarrenr port += np->in_ppip * 2704145522Sdarrenr (inb.s_addr % np->in_ippip); 2705145522Sdarrenr port += MAPBLK_MINPORT; 2706145522Sdarrenr port = htons(port); 2707145522Sdarrenr } 2708145522Sdarrenr 2709255332Scy } else if ((np->in_nsrcaddr == 0) && 2710255332Scy (np->in_nsrcmsk == 0xffffffff)) { 2711255332Scy i6addr_t in6; 2712255332Scy 2713145522Sdarrenr /* 2714145522Sdarrenr * 0/32 - use the interface's IP address. 2715145522Sdarrenr */ 2716145522Sdarrenr if ((l > 0) || 2717255332Scy ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp, 2718255332Scy &in6, NULL) == -1) { 2719255332Scy NBUMPSIDEX(1, ns_new_ifpaddr, ns_new_ifpaddr_1); 2720338170Scy DT4(ns_new_ifpaddr_1, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); 2721145522Sdarrenr return -1; 2722255332Scy } 2723255332Scy in.s_addr = ntohl(in6.in4.s_addr); 2724145522Sdarrenr 2725255332Scy } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) { 2726145522Sdarrenr /* 2727145522Sdarrenr * 0/0 - use the original source address/port. 2728145522Sdarrenr */ 2729255332Scy if (l > 0) { 2730255332Scy NBUMPSIDEX(1, ns_exhausted, ns_exhausted_3); 2731338170Scy DT4(ns_exhausted_3, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); 2732145522Sdarrenr return -1; 2733255332Scy } 2734145522Sdarrenr in.s_addr = ntohl(fin->fin_saddr); 2735145522Sdarrenr 2736255332Scy } else if ((np->in_nsrcmsk != 0xffffffff) && 2737255332Scy (np->in_spnext == 0) && ((l > 0) || (hm == NULL))) 2738255332Scy np->in_snip++; 2739145522Sdarrenr 2740145522Sdarrenr natl = NULL; 2741145522Sdarrenr 2742145522Sdarrenr if ((flags & IPN_TCPUDP) && 2743145522Sdarrenr ((np->in_redir & NAT_MAPBLK) == 0) && 2744145522Sdarrenr (np->in_flags & IPN_AUTOPORTMAP)) { 2745145522Sdarrenr /* 2746145522Sdarrenr * "ports auto" (without map-block) 2747145522Sdarrenr */ 2748145522Sdarrenr if ((l > 0) && (l % np->in_ppip == 0)) { 2749255332Scy if ((l > np->in_ppip) && 2750255332Scy np->in_nsrcmsk != 0xffffffff) 2751255332Scy np->in_snip++; 2752145522Sdarrenr } 2753145522Sdarrenr if (np->in_ppip != 0) { 2754145522Sdarrenr port = ntohs(sport); 2755145522Sdarrenr port += (l % np->in_ppip); 2756145522Sdarrenr port %= np->in_ppip; 2757145522Sdarrenr port += np->in_ppip * 2758145522Sdarrenr (ntohl(fin->fin_saddr) % 2759145522Sdarrenr np->in_ippip); 2760145522Sdarrenr port += MAPBLK_MINPORT; 2761145522Sdarrenr port = htons(port); 2762145522Sdarrenr } 2763145522Sdarrenr 2764145522Sdarrenr } else if (((np->in_redir & NAT_MAPBLK) == 0) && 2765255332Scy (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) { 2766145522Sdarrenr /* 2767145522Sdarrenr * Standard port translation. Select next port. 2768145522Sdarrenr */ 2769180778Sdarrenr if (np->in_flags & IPN_SEQUENTIAL) { 2770255332Scy port = np->in_spnext; 2771180778Sdarrenr } else { 2772255332Scy port = ipf_random() % (np->in_spmax - 2773255332Scy np->in_spmin + 1); 2774255332Scy port += np->in_spmin; 2775180778Sdarrenr } 2776180832Sdarrenr port = htons(port); 2777255332Scy np->in_spnext++; 2778145522Sdarrenr 2779255332Scy if (np->in_spnext > np->in_spmax) { 2780255332Scy np->in_spnext = np->in_spmin; 2781255332Scy if (np->in_nsrcmsk != 0xffffffff) 2782255332Scy np->in_snip++; 2783145522Sdarrenr } 2784145522Sdarrenr } 2785145522Sdarrenr 2786255332Scy if (np->in_flags & IPN_SIPRANGE) { 2787255332Scy if (np->in_snip > ntohl(np->in_nsrcmsk)) 2788255332Scy np->in_snip = ntohl(np->in_nsrcaddr); 2789145522Sdarrenr } else { 2790255332Scy if ((np->in_nsrcmsk != 0xffffffff) && 2791255332Scy ((np->in_snip + 1) & ntohl(np->in_nsrcmsk)) > 2792255332Scy ntohl(np->in_nsrcaddr)) 2793255332Scy np->in_snip = ntohl(np->in_nsrcaddr) + 1; 2794145522Sdarrenr } 2795145522Sdarrenr 2796145522Sdarrenr if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) 2797145522Sdarrenr port = sport; 2798145522Sdarrenr 2799145522Sdarrenr /* 2800145522Sdarrenr * Here we do a lookup of the connection as seen from 2801145522Sdarrenr * the outside. If an IP# pair already exists, try 2802145522Sdarrenr * again. So if you have A->B becomes C->B, you can 2803145522Sdarrenr * also have D->E become C->E but not D->B causing 2804145522Sdarrenr * another C->B. Also take protocol and ports into 2805145522Sdarrenr * account when determining whether a pre-existing 2806145522Sdarrenr * NAT setup will cause an external conflict where 2807145522Sdarrenr * this is appropriate. 2808145522Sdarrenr */ 2809145522Sdarrenr inb.s_addr = htonl(in.s_addr); 2810145522Sdarrenr sp = fin->fin_data[0]; 2811145522Sdarrenr dp = fin->fin_data[1]; 2812145522Sdarrenr fin->fin_data[0] = fin->fin_data[1]; 2813255332Scy fin->fin_data[1] = ntohs(port); 2814255332Scy natl = ipf_nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 2815255332Scy (u_int)fin->fin_p, fin->fin_dst, inb); 2816145522Sdarrenr fin->fin_data[0] = sp; 2817145522Sdarrenr fin->fin_data[1] = dp; 2818145522Sdarrenr 2819145522Sdarrenr /* 2820145522Sdarrenr * Has the search wrapped around and come back to the 2821145522Sdarrenr * start ? 2822145522Sdarrenr */ 2823145522Sdarrenr if ((natl != NULL) && 2824255332Scy (np->in_spnext != 0) && (st_port == np->in_spnext) && 2825255332Scy (np->in_snip != 0) && (st_ip == np->in_snip)) { 2826255332Scy NBUMPSIDED(1, ns_wrap); 2827338170Scy DT4(ns_wrap, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); 2828145522Sdarrenr return -1; 2829255332Scy } 2830145522Sdarrenr l++; 2831145522Sdarrenr } while (natl != NULL); 2832145522Sdarrenr 2833145522Sdarrenr /* Setup the NAT table */ 2834255332Scy nat->nat_osrcip = fin->fin_src; 2835255332Scy nat->nat_nsrcaddr = htonl(in.s_addr); 2836255332Scy nat->nat_odstip = fin->fin_dst; 2837255332Scy nat->nat_ndstip = fin->fin_dst; 2838145522Sdarrenr if (nat->nat_hm == NULL) 2839255332Scy nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src, 2840255332Scy fin->fin_dst, nat->nat_nsrcip, 2841255332Scy 0); 2842145522Sdarrenr 2843145522Sdarrenr if (flags & IPN_TCPUDP) { 2844255332Scy nat->nat_osport = sport; 2845255332Scy nat->nat_nsport = port; /* sport */ 2846255332Scy nat->nat_odport = dport; 2847255332Scy nat->nat_ndport = dport; 2848145522Sdarrenr ((tcphdr_t *)fin->fin_dp)->th_sport = port; 2849145522Sdarrenr } else if (flags & IPN_ICMPQUERY) { 2850255332Scy nat->nat_oicmpid = fin->fin_data[1]; 2851145522Sdarrenr ((icmphdr_t *)fin->fin_dp)->icmp_id = port; 2852255332Scy nat->nat_nicmpid = port; 2853145522Sdarrenr } 2854145522Sdarrenr return 0; 2855145522Sdarrenr} 2856145522Sdarrenr 2857145522Sdarrenr 2858145522Sdarrenr/* ------------------------------------------------------------------------ */ 2859255332Scy/* Function: ipf_nat_newrdr */ 2860145522Sdarrenr/* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ 2861145522Sdarrenr/* allow rule to be moved if IPN_ROUNDR is set. */ 2862145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2863145522Sdarrenr/* nat(I) - pointer to NAT entry */ 2864145522Sdarrenr/* ni(I) - pointer to structure with misc. information needed */ 2865145522Sdarrenr/* to create new NAT entry. */ 2866145522Sdarrenr/* */ 2867145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ 2868145522Sdarrenr/* to the new IP address for the translation. */ 2869145522Sdarrenr/* ------------------------------------------------------------------------ */ 2870255332Scystatic int 2871255332Scyipf_nat_newrdr(fin, nat, ni) 2872255332Scy fr_info_t *fin; 2873255332Scy nat_t *nat; 2874255332Scy natinfo_t *ni; 2875145522Sdarrenr{ 2876255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 2877255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 2878145522Sdarrenr u_short nport, dport, sport; 2879170268Sdarrenr struct in_addr in, inb; 2880170268Sdarrenr u_short sp, dp; 2881145522Sdarrenr hostmap_t *hm; 2882145522Sdarrenr u_32_t flags; 2883145522Sdarrenr ipnat_t *np; 2884170268Sdarrenr nat_t *natl; 2885145522Sdarrenr int move; 2886145522Sdarrenr 2887145522Sdarrenr move = 1; 2888145522Sdarrenr hm = NULL; 2889145522Sdarrenr in.s_addr = 0; 2890145522Sdarrenr np = ni->nai_np; 2891255332Scy flags = nat->nat_flags; 2892145522Sdarrenr 2893255332Scy if (flags & IPN_ICMPQUERY) { 2894255332Scy dport = fin->fin_data[1]; 2895255332Scy sport = 0; 2896255332Scy } else { 2897255332Scy sport = htons(fin->fin_data[0]); 2898255332Scy dport = htons(fin->fin_data[1]); 2899255332Scy } 2900255332Scy 2901255332Scy /* TRACE sport, dport */ 2902255332Scy 2903255332Scy 2904145522Sdarrenr /* 2905145522Sdarrenr * If the matching rule has IPN_STICKY set, then we want to have the 2906145522Sdarrenr * same rule kick in as before. Why would this happen? If you have 2907145522Sdarrenr * a collection of rdr rules with "round-robin sticky", the current 2908145522Sdarrenr * packet might match a different one to the previous connection but 2909145522Sdarrenr * we want the same destination to be used. 2910145522Sdarrenr */ 2911153876Sguido if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) && 2912153876Sguido ((np->in_flags & IPN_STICKY) != 0)) { 2913255332Scy hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, fin->fin_dst, 2914255332Scy in, (u_32_t)dport); 2915145522Sdarrenr if (hm != NULL) { 2916255332Scy in.s_addr = ntohl(hm->hm_ndstip.s_addr); 2917145522Sdarrenr np = hm->hm_ipnat; 2918145522Sdarrenr ni->nai_np = np; 2919145522Sdarrenr move = 0; 2920255332Scy ipf_nat_hostmapdel(softc, &hm); 2921145522Sdarrenr } 2922145522Sdarrenr } 2923145522Sdarrenr 2924145522Sdarrenr /* 2925145522Sdarrenr * Otherwise, it's an inbound packet. Most likely, we don't 2926145522Sdarrenr * want to rewrite source ports and source addresses. Instead, 2927145522Sdarrenr * we want to rewrite to a fixed internal address and fixed 2928145522Sdarrenr * internal port. 2929145522Sdarrenr */ 2930145522Sdarrenr if (np->in_flags & IPN_SPLIT) { 2931255332Scy in.s_addr = np->in_dnip; 2932272555Scy inb.s_addr = htonl(in.s_addr); 2933145522Sdarrenr 2934145522Sdarrenr if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { 2935255332Scy hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, 2936272555Scy fin->fin_dst, inb, (u_32_t)dport); 2937145522Sdarrenr if (hm != NULL) { 2938255332Scy in.s_addr = hm->hm_ndstip.s_addr; 2939145522Sdarrenr move = 0; 2940145522Sdarrenr } 2941145522Sdarrenr } 2942145522Sdarrenr 2943145522Sdarrenr if (hm == NULL || hm->hm_ref == 1) { 2944255332Scy if (np->in_ndstaddr == htonl(in.s_addr)) { 2945255332Scy np->in_dnip = ntohl(np->in_ndstmsk); 2946145522Sdarrenr move = 0; 2947145522Sdarrenr } else { 2948255332Scy np->in_dnip = ntohl(np->in_ndstaddr); 2949145522Sdarrenr } 2950145522Sdarrenr } 2951255332Scy if (hm != NULL) 2952255332Scy ipf_nat_hostmapdel(softc, &hm); 2953145522Sdarrenr 2954255332Scy } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) { 2955255332Scy i6addr_t in6; 2956255332Scy 2957145522Sdarrenr /* 2958145522Sdarrenr * 0/32 - use the interface's IP address. 2959145522Sdarrenr */ 2960255332Scy if (ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp, 2961255332Scy &in6, NULL) == -1) { 2962255332Scy NBUMPSIDEX(0, ns_new_ifpaddr, ns_new_ifpaddr_2); 2963338170Scy DT3(ns_new_ifpaddr_2, fr_info_t *, fin, nat_t *, nat, natinfo_t, ni); 2964145522Sdarrenr return -1; 2965255332Scy } 2966255332Scy in.s_addr = ntohl(in6.in4.s_addr); 2967145522Sdarrenr 2968255332Scy } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk== 0)) { 2969145522Sdarrenr /* 2970145522Sdarrenr * 0/0 - use the original destination address/port. 2971145522Sdarrenr */ 2972145522Sdarrenr in.s_addr = ntohl(fin->fin_daddr); 2973145522Sdarrenr 2974145522Sdarrenr } else if (np->in_redir == NAT_BIMAP && 2975255332Scy np->in_ndstmsk == np->in_odstmsk) { 2976145522Sdarrenr /* 2977145522Sdarrenr * map the address block in a 1:1 fashion 2978145522Sdarrenr */ 2979255332Scy in.s_addr = np->in_ndstaddr; 2980255332Scy in.s_addr |= fin->fin_daddr & ~np->in_ndstmsk; 2981145522Sdarrenr in.s_addr = ntohl(in.s_addr); 2982145522Sdarrenr } else { 2983255332Scy in.s_addr = ntohl(np->in_ndstaddr); 2984145522Sdarrenr } 2985145522Sdarrenr 2986255332Scy if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) 2987145522Sdarrenr nport = dport; 2988145522Sdarrenr else { 2989145522Sdarrenr /* 2990145522Sdarrenr * Whilst not optimized for the case where 2991145522Sdarrenr * pmin == pmax, the gain is not significant. 2992145522Sdarrenr */ 2993145522Sdarrenr if (((np->in_flags & IPN_FIXEDDPORT) == 0) && 2994255332Scy (np->in_odport != np->in_dtop)) { 2995255332Scy nport = ntohs(dport) - np->in_odport + np->in_dpmax; 2996145522Sdarrenr nport = htons(nport); 2997255332Scy } else { 2998255332Scy nport = htons(np->in_dpnext); 2999255332Scy np->in_dpnext++; 3000255332Scy if (np->in_dpnext > np->in_dpmax) 3001255332Scy np->in_dpnext = np->in_dpmin; 3002255332Scy } 3003145522Sdarrenr } 3004145522Sdarrenr 3005145522Sdarrenr /* 3006145522Sdarrenr * When the redirect-to address is set to 0.0.0.0, just 3007145522Sdarrenr * assume a blank `forwarding' of the packet. We don't 3008145522Sdarrenr * setup any translation for this either. 3009145522Sdarrenr */ 3010145522Sdarrenr if (in.s_addr == 0) { 3011255332Scy if (nport == dport) { 3012255332Scy NBUMPSIDED(0, ns_xlate_null); 3013145522Sdarrenr return -1; 3014255332Scy } 3015145522Sdarrenr in.s_addr = ntohl(fin->fin_daddr); 3016145522Sdarrenr } 3017145522Sdarrenr 3018170268Sdarrenr /* 3019170268Sdarrenr * Check to see if this redirect mapping already exists and if 3020170268Sdarrenr * it does, return "failure" (allowing it to be created will just 3021170268Sdarrenr * cause one or both of these "connections" to stop working.) 3022170268Sdarrenr */ 3023170268Sdarrenr inb.s_addr = htonl(in.s_addr); 3024170268Sdarrenr sp = fin->fin_data[0]; 3025170268Sdarrenr dp = fin->fin_data[1]; 3026170268Sdarrenr fin->fin_data[1] = fin->fin_data[0]; 3027170268Sdarrenr fin->fin_data[0] = ntohs(nport); 3028255332Scy natl = ipf_nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), 3029170268Sdarrenr (u_int)fin->fin_p, inb, fin->fin_src); 3030170268Sdarrenr fin->fin_data[0] = sp; 3031170268Sdarrenr fin->fin_data[1] = dp; 3032255332Scy if (natl != NULL) { 3033255332Scy DT2(ns_new_xlate_exists, fr_info_t *, fin, nat_t *, natl); 3034255332Scy NBUMPSIDE(0, ns_xlate_exists); 3035170268Sdarrenr return -1; 3036255332Scy } 3037170268Sdarrenr 3038272555Scy inb.s_addr = htonl(in.s_addr); 3039255332Scy nat->nat_ndstaddr = htonl(in.s_addr); 3040255332Scy nat->nat_odstip = fin->fin_dst; 3041255332Scy nat->nat_nsrcip = fin->fin_src; 3042255332Scy nat->nat_osrcip = fin->fin_src; 3043153876Sguido if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0)) 3044255332Scy nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src, 3045272555Scy fin->fin_dst, inb, (u_32_t)dport); 3046145522Sdarrenr 3047145522Sdarrenr if (flags & IPN_TCPUDP) { 3048255332Scy nat->nat_odport = dport; 3049255332Scy nat->nat_ndport = nport; 3050255332Scy nat->nat_osport = sport; 3051255332Scy nat->nat_nsport = sport; 3052145522Sdarrenr ((tcphdr_t *)fin->fin_dp)->th_dport = nport; 3053145522Sdarrenr } else if (flags & IPN_ICMPQUERY) { 3054255332Scy nat->nat_oicmpid = fin->fin_data[1]; 3055145522Sdarrenr ((icmphdr_t *)fin->fin_dp)->icmp_id = nport; 3056255332Scy nat->nat_nicmpid = nport; 3057145522Sdarrenr } 3058145522Sdarrenr 3059145522Sdarrenr return move; 3060145522Sdarrenr} 3061145522Sdarrenr 3062145522Sdarrenr/* ------------------------------------------------------------------------ */ 3063255332Scy/* Function: ipf_nat_add */ 3064145522Sdarrenr/* Returns: nat_t* - NULL == failure to create new NAT structure, */ 3065145522Sdarrenr/* else pointer to new NAT structure */ 3066145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3067145522Sdarrenr/* np(I) - pointer to NAT rule */ 3068145522Sdarrenr/* natsave(I) - pointer to where to store NAT struct pointer */ 3069145522Sdarrenr/* flags(I) - flags describing the current packet */ 3070145522Sdarrenr/* direction(I) - direction of packet (in/out) */ 3071145522Sdarrenr/* Write Lock: ipf_nat */ 3072145522Sdarrenr/* */ 3073145522Sdarrenr/* Attempts to create a new NAT entry. Does not actually change the packet */ 3074145522Sdarrenr/* in any way. */ 3075145522Sdarrenr/* */ 3076351634Scy/* This function is in three main parts: (1) deal with creating a new NAT */ 3077145522Sdarrenr/* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */ 3078145522Sdarrenr/* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */ 3079145522Sdarrenr/* and (3) building that structure and putting it into the NAT table(s). */ 3080161356Sguido/* */ 3081351635Scy/* NOTE: natsave should NOT be used to point back to an ipstate_t struct */ 3082161356Sguido/* as it can result in memory being corrupted. */ 3083145522Sdarrenr/* ------------------------------------------------------------------------ */ 3084255332Scynat_t * 3085255332Scyipf_nat_add(fin, np, natsave, flags, direction) 3086255332Scy fr_info_t *fin; 3087255332Scy ipnat_t *np; 3088255332Scy nat_t **natsave; 3089255332Scy u_int flags; 3090255332Scy int direction; 309153642Sguido{ 3092255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3093255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 309460852Sdarrenr hostmap_t *hm = NULL; 309560852Sdarrenr nat_t *nat, *natl; 3096255332Scy natstat_t *nsp; 3097145522Sdarrenr u_int nflags; 3098145522Sdarrenr natinfo_t ni; 3099145522Sdarrenr int move; 310053642Sguido 3101255332Scy nsp = &softn->ipf_nat_stats; 3102255332Scy 3103255332Scy if ((nsp->ns_active * 100 / softn->ipf_nat_table_max) > 3104255332Scy softn->ipf_nat_table_wm_high) { 3105255332Scy softn->ipf_nat_doflush = 1; 3106255332Scy } 3107255332Scy 3108255332Scy if (nsp->ns_active >= softn->ipf_nat_table_max) { 3109255332Scy NBUMPSIDED(fin->fin_out, ns_table_max); 3110338170Scy DT2(ns_table_max, nat_stat_t *, nsp, ipf_nat_softc_t *, softn); 3111130886Sdarrenr return NULL; 3112130886Sdarrenr } 3113130886Sdarrenr 3114145522Sdarrenr move = 1; 3115145522Sdarrenr nflags = np->in_flags & flags; 3116145522Sdarrenr nflags &= NAT_FROMRULE; 311753642Sguido 3118145522Sdarrenr ni.nai_np = np; 3119170268Sdarrenr ni.nai_dport = 0; 3120170268Sdarrenr ni.nai_sport = 0; 3121145522Sdarrenr 312253642Sguido /* Give me a new nat */ 312353642Sguido KMALLOC(nat, nat_t *); 312460852Sdarrenr if (nat == NULL) { 3125338170Scy DT(ns_memfail); 3126255332Scy NBUMPSIDED(fin->fin_out, ns_memfail); 3127130886Sdarrenr /* 3128130886Sdarrenr * Try to automatically tune the max # of entries in the 3129130886Sdarrenr * table allowed to be less than what will cause kmem_alloc() 3130130886Sdarrenr * to fail and try to eliminate panics due to out of memory 3131130886Sdarrenr * conditions arising. 3132130886Sdarrenr */ 3133255332Scy if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) && 3134255332Scy (nsp->ns_active > 100)) { 3135255332Scy softn->ipf_nat_table_max = nsp->ns_active - 100; 3136255332Scy printf("table_max reduced to %d\n", 3137255332Scy softn->ipf_nat_table_max); 3138130886Sdarrenr } 313953642Sguido return NULL; 314060852Sdarrenr } 314153642Sguido 3142255332Scy if (flags & IPN_ICMPQUERY) { 3143145522Sdarrenr /* 3144145522Sdarrenr * In the ICMP query NAT code, we translate the ICMP id fields 3145145522Sdarrenr * to make them unique. This is indepedent of the ICMP type 3146145522Sdarrenr * (e.g. in the unlikely event that a host sends an echo and 3147145522Sdarrenr * an tstamp request with the same id, both packets will have 3148145522Sdarrenr * their ip address/id field changed in the same way). 3149145522Sdarrenr */ 3150145522Sdarrenr /* The icmp_id field is used by the sender to identify the 3151145522Sdarrenr * process making the icmp request. (the receiver justs 3152145522Sdarrenr * copies it back in its response). So, it closely matches 3153145522Sdarrenr * the concept of source port. We overlay sport, so we can 3154145522Sdarrenr * maximally reuse the existing code. 3155145522Sdarrenr */ 3156255332Scy ni.nai_sport = fin->fin_data[1]; 3157255332Scy ni.nai_dport = 0; 3158145522Sdarrenr } 3159145522Sdarrenr 316053642Sguido bzero((char *)nat, sizeof(*nat)); 316153642Sguido nat->nat_flags = flags; 3162170268Sdarrenr nat->nat_redir = np->in_redir; 3163255332Scy nat->nat_dir = direction; 3164255332Scy nat->nat_pr[0] = fin->fin_p; 3165255332Scy nat->nat_pr[1] = fin->fin_p; 3166145522Sdarrenr 316753642Sguido /* 3168255332Scy * Search the current table for a match and create a new mapping 3169255332Scy * if there is none found. 317053642Sguido */ 3171255332Scy if (np->in_redir & NAT_DIVERTUDP) { 3172255332Scy move = ipf_nat_newdivert(fin, nat, &ni); 3173255332Scy 3174255332Scy } else if (np->in_redir & NAT_REWRITE) { 3175255332Scy move = ipf_nat_newrewrite(fin, nat, &ni); 3176255332Scy 3177255332Scy } else if (direction == NAT_OUTBOUND) { 317853642Sguido /* 3179145522Sdarrenr * We can now arrange to call this for the same connection 3180145522Sdarrenr * because ipf_nat_new doesn't protect the code path into 3181145522Sdarrenr * this function. 318253642Sguido */ 3183255332Scy natl = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p, 3184145522Sdarrenr fin->fin_src, fin->fin_dst); 3185145522Sdarrenr if (natl != NULL) { 3186161356Sguido KFREE(nat); 3187145522Sdarrenr nat = natl; 3188145522Sdarrenr goto done; 3189145522Sdarrenr } 319053642Sguido 3191255332Scy move = ipf_nat_newmap(fin, nat, &ni); 319253642Sguido } else { 319353642Sguido /* 3194255332Scy * NAT_INBOUND is used for redirects rules 319553642Sguido */ 3196255332Scy natl = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p, 3197255332Scy fin->fin_src, fin->fin_dst); 3198145522Sdarrenr if (natl != NULL) { 3199161356Sguido KFREE(nat); 3200145522Sdarrenr nat = natl; 3201145522Sdarrenr goto done; 320260852Sdarrenr } 320353642Sguido 3204255332Scy move = ipf_nat_newrdr(fin, nat, &ni); 3205145522Sdarrenr } 3206255332Scy if (move == -1) 3207255332Scy goto badnat; 320853642Sguido 3209255332Scy np = ni.nai_np; 3210255332Scy 3211255332Scy nat->nat_mssclamp = np->in_mssclamp; 3212255332Scy nat->nat_me = natsave; 3213255332Scy nat->nat_fr = fin->fin_fr; 3214255332Scy nat->nat_rev = fin->fin_rev; 3215255332Scy nat->nat_ptr = np; 3216255332Scy nat->nat_dlocal = np->in_dlocal; 3217255332Scy 3218255332Scy if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) { 3219255332Scy if (ipf_proxy_new(fin, nat) == -1) { 3220255332Scy NBUMPSIDED(fin->fin_out, ns_appr_fail); 3221338170Scy DT3(ns_appr_fail, fr_info_t *, fin, nat_t *, nat, ipnat_t *, np); 3222255332Scy goto badnat; 322353642Sguido } 322453642Sguido } 322553642Sguido 3226255332Scy nat->nat_ifps[0] = np->in_ifps[0]; 3227255332Scy if (np->in_ifps[0] != NULL) { 3228255332Scy COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]); 3229145522Sdarrenr } 3230145522Sdarrenr 3231255332Scy nat->nat_ifps[1] = np->in_ifps[1]; 3232255332Scy if (np->in_ifps[1] != NULL) { 3233255332Scy COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]); 3234255332Scy } 323553642Sguido 3236255332Scy if (ipf_nat_finalise(fin, nat) == -1) { 3237255332Scy goto badnat; 3238255332Scy } 323953642Sguido 3240255332Scy np->in_use++; 324153642Sguido 3242255332Scy if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { 3243255332Scy if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) { 3244255332Scy ipf_nat_delrdr(softn, np); 3245255332Scy ipf_nat_addrdr(softn, np); 3246255332Scy } else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) { 3247255332Scy ipf_nat_delmap(softn, np); 3248255332Scy ipf_nat_addmap(softn, np); 3249145522Sdarrenr } 3250145522Sdarrenr } 325153642Sguido 3252145522Sdarrenr if (flags & SI_WILDP) 3253255332Scy nsp->ns_wilds++; 3254255332Scy nsp->ns_proto[nat->nat_pr[0]]++; 3255255332Scy 3256145522Sdarrenr goto done; 3257145522Sdarrenrbadnat: 3258338169Scy DT3(ns_badnatnew, fr_info_t *, fin, nat_t *, nat, ipnat_t *, np); 3259255332Scy NBUMPSIDE(fin->fin_out, ns_badnatnew); 3260145522Sdarrenr if ((hm = nat->nat_hm) != NULL) 3261255332Scy ipf_nat_hostmapdel(softc, &hm); 3262145522Sdarrenr KFREE(nat); 3263145522Sdarrenr nat = NULL; 3264145522Sdarrenrdone: 3265255332Scy if (nat != NULL && np != NULL) 3266255332Scy np->in_hits++; 3267255332Scy if (natsave != NULL) 3268255332Scy *natsave = nat; 3269145522Sdarrenr return nat; 3270145522Sdarrenr} 327160852Sdarrenr 327260852Sdarrenr 3273145522Sdarrenr/* ------------------------------------------------------------------------ */ 3274255332Scy/* Function: ipf_nat_finalise */ 3275145522Sdarrenr/* Returns: int - 0 == sucess, -1 == failure */ 3276145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3277145522Sdarrenr/* nat(I) - pointer to NAT entry */ 3278145522Sdarrenr/* Write Lock: ipf_nat */ 3279145522Sdarrenr/* */ 3280145522Sdarrenr/* This is the tail end of constructing a new NAT entry and is the same */ 3281145522Sdarrenr/* for both IPv4 and IPv6. */ 3282145522Sdarrenr/* ------------------------------------------------------------------------ */ 3283145522Sdarrenr/*ARGSUSED*/ 3284255332Scystatic int 3285255332Scyipf_nat_finalise(fin, nat) 3286255332Scy fr_info_t *fin; 3287255332Scy nat_t *nat; 3288145522Sdarrenr{ 3289255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3290255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 3291255332Scy u_32_t sum1, sum2, sumd; 3292145522Sdarrenr frentry_t *fr; 3293255332Scy u_32_t flags; 3294344833Scy#if SOLARIS && defined(_KERNEL) && defined(ICK_M_CTL_MAGIC) 3295255332Scy qpktinfo_t *qpi = fin->fin_qpi; 3296255332Scy#endif 3297145522Sdarrenr 3298255332Scy flags = nat->nat_flags; 3299145522Sdarrenr 3300255332Scy switch (nat->nat_pr[0]) 3301255332Scy { 3302255332Scy case IPPROTO_ICMP : 3303255332Scy sum1 = LONG_SUM(ntohs(nat->nat_oicmpid)); 3304255332Scy sum2 = LONG_SUM(ntohs(nat->nat_nicmpid)); 3305255332Scy CALC_SUMD(sum1, sum2, sumd); 3306255332Scy nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 3307255332Scy 3308255332Scy break; 3309255332Scy 3310255332Scy default : 3311255332Scy sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr) + \ 3312255332Scy ntohs(nat->nat_osport)); 3313255332Scy sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr) + \ 3314255332Scy ntohs(nat->nat_nsport)); 3315255332Scy CALC_SUMD(sum1, sum2, sumd); 3316255332Scy nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 3317255332Scy 3318255332Scy sum1 = LONG_SUM(ntohl(nat->nat_odstaddr) + \ 3319255332Scy ntohs(nat->nat_odport)); 3320255332Scy sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr) + \ 3321255332Scy ntohs(nat->nat_ndport)); 3322255332Scy CALC_SUMD(sum1, sum2, sumd); 3323255332Scy nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); 3324255332Scy break; 3325161356Sguido } 3326255332Scy 3327255332Scy /* 3328255332Scy * Compute the partial checksum, just in case. 3329255332Scy * This is only ever placed into outbound packets so care needs 3330255332Scy * to be taken over which pair of addresses are used. 3331255332Scy */ 3332255332Scy if (nat->nat_dir == NAT_OUTBOUND) { 3333255332Scy sum1 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); 3334255332Scy sum1 += LONG_SUM(ntohl(nat->nat_ndstaddr)); 3335255332Scy } else { 3336255332Scy sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr)); 3337255332Scy sum1 += LONG_SUM(ntohl(nat->nat_odstaddr)); 3338161356Sguido } 3339255332Scy sum1 += nat->nat_pr[1]; 3340255332Scy nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16); 3341145522Sdarrenr 3342255332Scy sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr)); 3343255332Scy sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); 3344255332Scy CALC_SUMD(sum1, sum2, sumd); 3345255332Scy nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); 334692685Sdarrenr 3347255332Scy sum1 = LONG_SUM(ntohl(nat->nat_odstaddr)); 3348255332Scy sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr)); 3349255332Scy CALC_SUMD(sum1, sum2, sumd); 3350255332Scy nat->nat_ipsumd += (sumd & 0xffff) + (sumd >> 16); 335192685Sdarrenr 3352255332Scy nat->nat_v[0] = 4; 3353255332Scy nat->nat_v[1] = 4; 3354255332Scy 3355255332Scy if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { 3356255332Scy nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); 3357255332Scy } 3358255332Scy 3359255332Scy if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { 3360255332Scy nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); 3361255332Scy } 3362255332Scy 3363255332Scy if ((nat->nat_flags & SI_CLONE) == 0) 3364255332Scy nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat); 3365255332Scy 3366255332Scy if (ipf_nat_insert(softc, softn, nat) == 0) { 3367255332Scy if (softn->ipf_nat_logging) 3368255332Scy ipf_nat_log(softc, softn, nat, NL_NEW); 3369255332Scy fr = nat->nat_fr; 3370145522Sdarrenr if (fr != NULL) { 3371145522Sdarrenr MUTEX_ENTER(&fr->fr_lock); 3372145522Sdarrenr fr->fr_ref++; 3373145522Sdarrenr MUTEX_EXIT(&fr->fr_lock); 3374145522Sdarrenr } 3375145522Sdarrenr return 0; 3376145522Sdarrenr } 337792685Sdarrenr 3378255332Scy NBUMPSIDED(fin->fin_out, ns_unfinalised); 3379338170Scy DT2(ns_unfinalised, fr_info_t *, fin, nat_t *, nat); 3380145522Sdarrenr /* 3381145522Sdarrenr * nat_insert failed, so cleanup time... 3382145522Sdarrenr */ 3383255332Scy if (nat->nat_sync != NULL) 3384255332Scy ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); 3385145522Sdarrenr return -1; 338653642Sguido} 338753642Sguido 338853642Sguido 3389145522Sdarrenr/* ------------------------------------------------------------------------ */ 3390255332Scy/* Function: ipf_nat_insert */ 3391255332Scy/* Returns: int - 0 == sucess, -1 == failure */ 3392255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 3393255332Scy/* softn(I) - pointer to NAT context structure */ 3394255332Scy/* nat(I) - pointer to NAT structure */ 3395255332Scy/* Write Lock: ipf_nat */ 3396145522Sdarrenr/* */ 3397145522Sdarrenr/* Insert a NAT entry into the hash tables for searching and add it to the */ 3398145522Sdarrenr/* list of active NAT entries. Adjust global counters when complete. */ 3399145522Sdarrenr/* ------------------------------------------------------------------------ */ 3400255332Scyint 3401255332Scyipf_nat_insert(softc, softn, nat) 3402255332Scy ipf_main_softc_t *softc; 3403255332Scy ipf_nat_softc_t *softn; 3404255332Scy nat_t *nat; 340560852Sdarrenr{ 3406255332Scy u_int hv0, hv1; 3407255332Scy u_int sp, dp; 3408255332Scy ipnat_t *in; 3409351635Scy int ret; 341060852Sdarrenr 3411145522Sdarrenr /* 3412145522Sdarrenr * Try and return an error as early as possible, so calculate the hash 3413145522Sdarrenr * entry numbers first and then proceed. 3414145522Sdarrenr */ 3415145522Sdarrenr if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { 3416255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 3417255332Scy sp = nat->nat_osport; 3418255332Scy dp = nat->nat_odport; 3419255332Scy } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { 3420255332Scy sp = 0; 3421255332Scy dp = nat->nat_oicmpid; 3422255332Scy } else { 3423255332Scy sp = 0; 3424255332Scy dp = 0; 3425255332Scy } 3426255332Scy hv0 = NAT_HASH_FN(nat->nat_osrcaddr, sp, 0xffffffff); 3427255332Scy hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0 + dp, 0xffffffff); 3428255332Scy /* 3429255332Scy * TRACE nat_osrcaddr, nat_osport, nat_odstaddr, 3430255332Scy * nat_odport, hv0 3431255332Scy */ 3432255332Scy 3433255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 3434255332Scy sp = nat->nat_nsport; 3435255332Scy dp = nat->nat_ndport; 3436255332Scy } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { 3437255332Scy sp = 0; 3438255332Scy dp = nat->nat_nicmpid; 3439255332Scy } else { 3440255332Scy sp = 0; 3441255332Scy dp = 0; 3442255332Scy } 3443255332Scy hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, sp, 0xffffffff); 3444255332Scy hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1 + dp, 0xffffffff); 3445255332Scy /* 3446255332Scy * TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, 3447255332Scy * nat_ndport, hv1 3448255332Scy */ 344980482Sdarrenr } else { 3450255332Scy hv0 = NAT_HASH_FN(nat->nat_osrcaddr, 0, 0xffffffff); 3451255332Scy hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0, 0xffffffff); 3452255332Scy /* TRACE nat_osrcaddr, nat_odstaddr, hv0 */ 345380482Sdarrenr 3454255332Scy hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, 0, 0xffffffff); 3455255332Scy hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1, 0xffffffff); 3456255332Scy /* TRACE nat_nsrcaddr, nat_ndstaddr, hv1 */ 3457145522Sdarrenr } 3458145522Sdarrenr 3459255332Scy nat->nat_hv[0] = hv0; 3460255332Scy nat->nat_hv[1] = hv1; 3461145522Sdarrenr 3462145522Sdarrenr MUTEX_INIT(&nat->nat_lock, "nat entry lock"); 3463145522Sdarrenr 3464255332Scy in = nat->nat_ptr; 3465255332Scy nat->nat_ref = nat->nat_me ? 2 : 1; 3466145522Sdarrenr 3467145522Sdarrenr nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; 3468255332Scy nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0], 4); 3469145522Sdarrenr 3470161356Sguido if (nat->nat_ifnames[1][0] != '\0') { 3471145522Sdarrenr nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 3472255332Scy nat->nat_ifps[1] = ipf_resolvenic(softc, 3473255332Scy nat->nat_ifnames[1], 4); 3474255332Scy } else if (in->in_ifnames[1] != -1) { 3475255332Scy char *name; 3476255332Scy 3477255332Scy name = in->in_names + in->in_ifnames[1]; 3478255332Scy if (name[1] != '\0' && name[0] != '-' && name[0] != '*') { 3479255332Scy (void) strncpy(nat->nat_ifnames[1], 3480255332Scy nat->nat_ifnames[0], LIFNAMSIZ); 3481255332Scy nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; 3482255332Scy nat->nat_ifps[1] = nat->nat_ifps[0]; 3483255332Scy } 3484145522Sdarrenr } 3485255332Scy if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { 3486255332Scy nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); 3487255332Scy } 3488255332Scy if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { 3489255332Scy nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); 3490255332Scy } 3491145522Sdarrenr 3492351635Scy ret = ipf_nat_hashtab_add(softc, softn, nat); 3493351635Scy if (ret == -1) 3494351635Scy MUTEX_DESTROY(&nat->nat_lock); 3495351635Scy return ret; 3496255332Scy} 3497145522Sdarrenr 3498255332Scy 3499255332Scy/* ------------------------------------------------------------------------ */ 3500255332Scy/* Function: ipf_nat_hashtab_add */ 3501351636Scy/* Returns: int - 0 == sucess, -1 == failure */ 3502255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 3503255332Scy/* softn(I) - pointer to NAT context structure */ 3504255332Scy/* nat(I) - pointer to NAT structure */ 3505255332Scy/* */ 3506255332Scy/* Handle the insertion of a NAT entry into the table/list. */ 3507255332Scy/* ------------------------------------------------------------------------ */ 3508255332Scyint 3509255332Scyipf_nat_hashtab_add(softc, softn, nat) 3510255332Scy ipf_main_softc_t *softc; 3511255332Scy ipf_nat_softc_t *softn; 3512255332Scy nat_t *nat; 3513255332Scy{ 3514255332Scy nat_t **natp; 3515255332Scy u_int hv0; 3516255332Scy u_int hv1; 3517255332Scy 3518255332Scy hv0 = nat->nat_hv[0] % softn->ipf_nat_table_sz; 3519255332Scy hv1 = nat->nat_hv[1] % softn->ipf_nat_table_sz; 3520255332Scy 3521255332Scy if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) { 3522255332Scy u_int swap; 3523255332Scy 3524255332Scy swap = hv0; 3525255332Scy hv0 = hv1; 3526255332Scy hv1 = swap; 3527255332Scy } 3528255332Scy 3529255332Scy if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0] >= 3530255332Scy softn->ipf_nat_maxbucket) { 3531255332Scy DT1(ns_bucket_max_0, int, 3532255332Scy softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]); 3533255332Scy NBUMPSIDE(0, ns_bucket_max); 3534255332Scy return -1; 3535255332Scy } 3536255332Scy 3537255332Scy if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1] >= 3538255332Scy softn->ipf_nat_maxbucket) { 3539255332Scy DT1(ns_bucket_max_1, int, 3540255332Scy softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]); 3541255332Scy NBUMPSIDE(1, ns_bucket_max); 3542255332Scy return -1; 3543255332Scy } 3544255332Scy 3545255332Scy /* 3546255332Scy * The ordering of operations in the list and hash table insertion 3547255332Scy * is very important. The last operation for each task should be 3548255332Scy * to update the top of the list, after all the "nexts" have been 3549255332Scy * done so that walking the list while it is being done does not 3550255332Scy * find strange pointers. 3551255332Scy * 3552255332Scy * Global list of NAT instances 3553255332Scy */ 3554255332Scy nat->nat_next = softn->ipf_nat_instances; 3555255332Scy nat->nat_pnext = &softn->ipf_nat_instances; 3556255332Scy if (softn->ipf_nat_instances) 3557255332Scy softn->ipf_nat_instances->nat_pnext = &nat->nat_next; 3558255332Scy softn->ipf_nat_instances = nat; 3559255332Scy 3560255332Scy /* 3561255332Scy * Inbound hash table. 3562255332Scy */ 3563255332Scy natp = &softn->ipf_nat_table[0][hv0]; 356467614Sdarrenr nat->nat_phnext[0] = natp; 356560852Sdarrenr nat->nat_hnext[0] = *natp; 3566255332Scy if (*natp) { 3567255332Scy (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 3568255332Scy } else { 3569255332Scy NBUMPSIDE(0, ns_inuse); 3570255332Scy } 357160852Sdarrenr *natp = nat; 3572255332Scy NBUMPSIDE(0, ns_bucketlen[hv0]); 357367614Sdarrenr 3574255332Scy /* 3575255332Scy * Outbound hash table. 3576255332Scy */ 3577255332Scy natp = &softn->ipf_nat_table[1][hv1]; 3578255332Scy nat->nat_phnext[1] = natp; 3579255332Scy nat->nat_hnext[1] = *natp; 358067614Sdarrenr if (*natp) 358167614Sdarrenr (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 3582255332Scy else { 3583255332Scy NBUMPSIDE(1, ns_inuse); 3584255332Scy } 358560852Sdarrenr *natp = nat; 3586255332Scy NBUMPSIDE(1, ns_bucketlen[hv1]); 358760852Sdarrenr 3588255332Scy ipf_nat_setqueue(softc, softn, nat); 3589145522Sdarrenr 3590255332Scy if (nat->nat_dir & NAT_OUTBOUND) { 3591255332Scy NBUMPSIDE(1, ns_added); 3592255332Scy } else { 3593255332Scy NBUMPSIDE(0, ns_added); 3594255332Scy } 3595255332Scy softn->ipf_nat_stats.ns_active++; 3596145522Sdarrenr return 0; 359760852Sdarrenr} 359860852Sdarrenr 359960852Sdarrenr 3600145522Sdarrenr/* ------------------------------------------------------------------------ */ 3601255332Scy/* Function: ipf_nat_icmperrorlookup */ 3602145522Sdarrenr/* Returns: nat_t* - point to matching NAT structure */ 3603145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3604145522Sdarrenr/* dir(I) - direction of packet (in/out) */ 3605145522Sdarrenr/* */ 3606145522Sdarrenr/* Check if the ICMP error message is related to an existing TCP, UDP or */ 3607145522Sdarrenr/* ICMP query nat entry. It is assumed that the packet is already of the */ 3608145522Sdarrenr/* the required length. */ 3609145522Sdarrenr/* ------------------------------------------------------------------------ */ 3610255332Scynat_t * 3611255332Scyipf_nat_icmperrorlookup(fin, dir) 3612255332Scy fr_info_t *fin; 3613255332Scy int dir; 361453642Sguido{ 3615255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3616255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 3617145522Sdarrenr int flags = 0, type, minlen; 3618145522Sdarrenr icmphdr_t *icmp, *orgicmp; 3619255332Scy nat_stat_side_t *nside; 362053642Sguido tcphdr_t *tcp = NULL; 3621145522Sdarrenr u_short data[2]; 3622145522Sdarrenr nat_t *nat; 362353642Sguido ip_t *oip; 3624145522Sdarrenr u_int p; 362553642Sguido 3626145522Sdarrenr icmp = fin->fin_dp; 3627145522Sdarrenr type = icmp->icmp_type; 3628255332Scy nside = &softn->ipf_nat_stats.ns_side[fin->fin_out]; 362953642Sguido /* 363053642Sguido * Does it at least have the return (basic) IP header ? 363153642Sguido * Only a basic IP header (no options) should be with an ICMP error 3632145522Sdarrenr * header. Also, if it's not an error type, then return. 363353642Sguido */ 3634255332Scy if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) { 3635255332Scy ATOMIC_INCL(nside->ns_icmp_basic); 363653642Sguido return NULL; 3637255332Scy } 3638145522Sdarrenr 363953642Sguido /* 3640145522Sdarrenr * Check packet size 364153642Sguido */ 364253642Sguido oip = (ip_t *)((char *)fin->fin_dp + 8); 3643145522Sdarrenr minlen = IP_HL(oip) << 2; 3644145522Sdarrenr if ((minlen < sizeof(ip_t)) || 3645255332Scy (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) { 3646255332Scy ATOMIC_INCL(nside->ns_icmp_size); 364753642Sguido return NULL; 3648255332Scy } 3649255332Scy 365064580Sdarrenr /* 365164580Sdarrenr * Is the buffer big enough for all of it ? It's the size of the IP 365264580Sdarrenr * header claimed in the encapsulated part which is of concern. It 365364580Sdarrenr * may be too big to be in this buffer but not so big that it's 365464580Sdarrenr * outside the ICMP packet, leading to TCP deref's causing problems. 365564580Sdarrenr * This is possible because we don't know how big oip_hl is when we 3656255332Scy * do the pullup early in ipf_check() and thus can't gaurantee it is 365764580Sdarrenr * all here now. 365864580Sdarrenr */ 3659255332Scy#ifdef ipf_nat_KERNEL 366064580Sdarrenr { 366164580Sdarrenr mb_t *m; 366264580Sdarrenr 3663145522Sdarrenr m = fin->fin_m; 3664369272Scy# if SOLARIS 3665255332Scy if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > 3666255332Scy (char *)m->b_wptr) { 3667255332Scy ATOMIC_INCL(nside->ns_icmp_mbuf); 366864580Sdarrenr return NULL; 3669255332Scy } 367064580Sdarrenr# else 367164580Sdarrenr if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > 3672255332Scy (char *)fin->fin_ip + M_LEN(m)) { 3673255332Scy ATOMIC_INCL(nside->ns_icmp_mbuf); 367464580Sdarrenr return NULL; 3675255332Scy } 367664580Sdarrenr# endif 367764580Sdarrenr } 367864580Sdarrenr#endif 367964580Sdarrenr 3680255332Scy if (fin->fin_daddr != oip->ip_src.s_addr) { 3681255332Scy ATOMIC_INCL(nside->ns_icmp_address); 3682145522Sdarrenr return NULL; 3683255332Scy } 3684145522Sdarrenr 3685145522Sdarrenr p = oip->ip_p; 3686145522Sdarrenr if (p == IPPROTO_TCP) 368753642Sguido flags = IPN_TCP; 3688145522Sdarrenr else if (p == IPPROTO_UDP) 368953642Sguido flags = IPN_UDP; 3690145522Sdarrenr else if (p == IPPROTO_ICMP) { 3691145522Sdarrenr orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 3692145522Sdarrenr 3693145522Sdarrenr /* see if this is related to an ICMP query */ 3694255332Scy if (ipf_nat_icmpquerytype(orgicmp->icmp_type)) { 3695145522Sdarrenr data[0] = fin->fin_data[0]; 3696145522Sdarrenr data[1] = fin->fin_data[1]; 3697145522Sdarrenr fin->fin_data[0] = 0; 3698145522Sdarrenr fin->fin_data[1] = orgicmp->icmp_id; 3699145522Sdarrenr 3700145522Sdarrenr flags = IPN_ICMPERR|IPN_ICMPQUERY; 3701145522Sdarrenr /* 3702145522Sdarrenr * NOTE : dir refers to the direction of the original 3703145522Sdarrenr * ip packet. By definition the icmp error 3704145522Sdarrenr * message flows in the opposite direction. 3705145522Sdarrenr */ 3706145522Sdarrenr if (dir == NAT_INBOUND) 3707255332Scy nat = ipf_nat_inlookup(fin, flags, p, 3708255332Scy oip->ip_dst, 3709255332Scy oip->ip_src); 3710145522Sdarrenr else 3711255332Scy nat = ipf_nat_outlookup(fin, flags, p, 3712255332Scy oip->ip_dst, 3713255332Scy oip->ip_src); 3714145522Sdarrenr fin->fin_data[0] = data[0]; 3715145522Sdarrenr fin->fin_data[1] = data[1]; 3716145522Sdarrenr return nat; 3717145522Sdarrenr } 3718145522Sdarrenr } 3719255332Scy 372053642Sguido if (flags & IPN_TCPUDP) { 372164580Sdarrenr minlen += 8; /* + 64bits of data to get ports */ 3722255332Scy /* TRACE (fin,minlen) */ 3723255332Scy if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) { 3724255332Scy ATOMIC_INCL(nside->ns_icmp_short); 372564580Sdarrenr return NULL; 3726255332Scy } 372792685Sdarrenr 372892685Sdarrenr data[0] = fin->fin_data[0]; 372992685Sdarrenr data[1] = fin->fin_data[1]; 3730145522Sdarrenr tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 373192685Sdarrenr fin->fin_data[0] = ntohs(tcp->th_dport); 373292685Sdarrenr fin->fin_data[1] = ntohs(tcp->th_sport); 373392685Sdarrenr 373492685Sdarrenr if (dir == NAT_INBOUND) { 3735255332Scy nat = ipf_nat_inlookup(fin, flags, p, oip->ip_dst, 3736255332Scy oip->ip_src); 373792685Sdarrenr } else { 3738255332Scy nat = ipf_nat_outlookup(fin, flags, p, oip->ip_dst, 3739145522Sdarrenr oip->ip_src); 374092685Sdarrenr } 374192685Sdarrenr fin->fin_data[0] = data[0]; 374292685Sdarrenr fin->fin_data[1] = data[1]; 374392685Sdarrenr return nat; 374453642Sguido } 374560852Sdarrenr if (dir == NAT_INBOUND) 3746255332Scy nat = ipf_nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src); 374760852Sdarrenr else 3748255332Scy nat = ipf_nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src); 3749255332Scy 3750255332Scy return nat; 375153642Sguido} 375253642Sguido 375353642Sguido 3754145522Sdarrenr/* ------------------------------------------------------------------------ */ 3755255332Scy/* Function: ipf_nat_icmperror */ 3756145522Sdarrenr/* Returns: nat_t* - point to matching NAT structure */ 3757145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3758145522Sdarrenr/* nflags(I) - NAT flags for this packet */ 3759145522Sdarrenr/* dir(I) - direction of packet (in/out) */ 3760145522Sdarrenr/* */ 3761145522Sdarrenr/* Fix up an ICMP packet which is an error message for an existing NAT */ 3762145522Sdarrenr/* session. This will correct both packet header data and checksums. */ 3763145522Sdarrenr/* */ 3764145522Sdarrenr/* This should *ONLY* be used for incoming ICMP error packets to make sure */ 3765145522Sdarrenr/* a NAT'd ICMP packet gets correctly recognised. */ 3766145522Sdarrenr/* ------------------------------------------------------------------------ */ 3767255332Scynat_t * 3768255332Scyipf_nat_icmperror(fin, nflags, dir) 3769255332Scy fr_info_t *fin; 3770255332Scy u_int *nflags; 3771255332Scy int dir; 377253642Sguido{ 3773255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3774255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 3775145522Sdarrenr u_32_t sum1, sum2, sumd, sumd2; 3776255332Scy struct in_addr a1, a2, a3, a4; 3777170268Sdarrenr int flags, dlen, odst; 3778145522Sdarrenr icmphdr_t *icmp; 3779145522Sdarrenr u_short *csump; 378095418Sdarrenr tcphdr_t *tcp; 378153642Sguido nat_t *nat; 378253642Sguido ip_t *oip; 3783145522Sdarrenr void *dp; 378453642Sguido 3785255332Scy if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { 3786255332Scy NBUMPSIDED(fin->fin_out, ns_icmp_short); 378763523Sdarrenr return NULL; 3788255332Scy } 3789255332Scy 379067614Sdarrenr /* 3791255332Scy * ipf_nat_icmperrorlookup() will return NULL for `defective' packets. 379267614Sdarrenr */ 3793255332Scy if ((fin->fin_v != 4) || !(nat = ipf_nat_icmperrorlookup(fin, dir))) { 3794255332Scy NBUMPSIDED(fin->fin_out, ns_icmp_notfound); 379553642Sguido return NULL; 3796255332Scy } 379792685Sdarrenr 3798145522Sdarrenr tcp = NULL; 3799145522Sdarrenr csump = NULL; 380092685Sdarrenr flags = 0; 3801130886Sdarrenr sumd2 = 0; 380253642Sguido *nflags = IPN_ICMPERR; 3803145522Sdarrenr icmp = fin->fin_dp; 380453642Sguido oip = (ip_t *)&icmp->icmp_ip; 3805145522Sdarrenr dp = (((char *)oip) + (IP_HL(oip) << 2)); 3806145522Sdarrenr if (oip->ip_p == IPPROTO_TCP) { 3807145522Sdarrenr tcp = (tcphdr_t *)dp; 3808145522Sdarrenr csump = (u_short *)&tcp->th_sum; 380953642Sguido flags = IPN_TCP; 3810145522Sdarrenr } else if (oip->ip_p == IPPROTO_UDP) { 3811145522Sdarrenr udphdr_t *udp; 3812145522Sdarrenr 3813145522Sdarrenr udp = (udphdr_t *)dp; 3814145522Sdarrenr tcp = (tcphdr_t *)dp; 3815145522Sdarrenr csump = (u_short *)&udp->uh_sum; 381653642Sguido flags = IPN_UDP; 3817145522Sdarrenr } else if (oip->ip_p == IPPROTO_ICMP) 3818145522Sdarrenr flags = IPN_ICMPQUERY; 3819145522Sdarrenr dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip); 382095418Sdarrenr 382195418Sdarrenr /* 382253642Sguido * Need to adjust ICMP header to include the real IP#'s and 382353642Sguido * port #'s. Only apply a checksum change relative to the 3824255332Scy * IP address change as it will be modified again in ipf_nat_checkout 382553642Sguido * for both address and port. Two checksum changes are 382653642Sguido * necessary for the two header address changes. Be careful 382753642Sguido * to only modify the checksum once for the port # and twice 382853642Sguido * for the IP#. 382953642Sguido */ 383060852Sdarrenr 383167614Sdarrenr /* 383267614Sdarrenr * Step 1 383367614Sdarrenr * Fix the IP addresses in the offending IP packet. You also need 3834170268Sdarrenr * to adjust the IP header checksum of that offending IP packet. 383567614Sdarrenr * 3836145522Sdarrenr * Normally, you would expect that the ICMP checksum of the 3837130886Sdarrenr * ICMP error message needs to be adjusted as well for the 3838130886Sdarrenr * IP address change in oip. 3839145522Sdarrenr * However, this is a NOP, because the ICMP checksum is 3840130886Sdarrenr * calculated over the complete ICMP packet, which includes the 3841145522Sdarrenr * changed oip IP addresses and oip->ip_sum. However, these 3842130886Sdarrenr * two changes cancel each other out (if the delta for 3843145522Sdarrenr * the IP address is x, then the delta for ip_sum is minus x), 3844130886Sdarrenr * so no change in the icmp_cksum is necessary. 3845130886Sdarrenr * 3846170268Sdarrenr * Inbound ICMP 3847170268Sdarrenr * ------------ 3848170268Sdarrenr * MAP rule, SRC=a,DST=b -> SRC=c,DST=b 3849170268Sdarrenr * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b) 3850255332Scy * - OIP_SRC(c)=nat_newsrcip, OIP_DST(b)=nat_newdstip 3851255332Scy *=> OIP_SRC(c)=nat_oldsrcip, OIP_DST(b)=nat_olddstip 3852170268Sdarrenr * 3853170268Sdarrenr * RDR rule, SRC=a,DST=b -> SRC=a,DST=c 3854170268Sdarrenr * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a) 3855255332Scy * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip 3856255332Scy *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip 3857170268Sdarrenr * 3858255332Scy * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d 3859255332Scy * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d) 3860255332Scy * - OIP_SRC(c)=nat_newsrcip, OIP_DST(d)=nat_newdstip 3861255332Scy *=> OIP_SRC(c)=nat_oldsrcip, OIP_DST(d)=nat_olddstip 3862255332Scy * 3863255332Scy * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d 3864255332Scy * - response to outgoing packet (d,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) 3865255332Scy * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip 3866255332Scy *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip 3867255332Scy * 3868170268Sdarrenr * Outbound ICMP 3869170268Sdarrenr * ------------- 3870170268Sdarrenr * MAP rule, SRC=a,DST=b -> SRC=c,DST=b 3871170268Sdarrenr * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) 3872255332Scy * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip 3873255332Scy *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip 3874170268Sdarrenr * 3875170268Sdarrenr * RDR rule, SRC=a,DST=b -> SRC=a,DST=c 3876170268Sdarrenr * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c) 3877255332Scy * - OIP_SRC(a)=nat_newsrcip, OIP_DST(c)=nat_newdstip 3878255332Scy *=> OIP_SRC(a)=nat_oldsrcip, OIP_DST(c)=nat_olddstip 3879170268Sdarrenr * 3880255332Scy * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d 3881255332Scy * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d) 3882255332Scy * - OIP_SRC(c)=nat_olddstip, OIP_DST(d)=nat_oldsrcip 3883255332Scy *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip 3884255332Scy * 3885255332Scy * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d 3886255332Scy * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a) 3887255332Scy * - OIP_SRC(b)=nat_newsrcip, OIP_DST(a)=nat_newdstip 3888255332Scy *=> OIP_SRC(a)=nat_oldsrcip, OIP_DST(c)=nat_olddstip 3889130886Sdarrenr */ 3890255332Scy 3891255332Scy if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) || 3892255332Scy ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) { 3893255332Scy a1.s_addr = ntohl(nat->nat_osrcaddr); 3894255332Scy a4.s_addr = ntohl(oip->ip_src.s_addr); 3895255332Scy a3.s_addr = ntohl(nat->nat_odstaddr); 3896255332Scy a2.s_addr = ntohl(oip->ip_dst.s_addr); 3897170268Sdarrenr oip->ip_src.s_addr = htonl(a1.s_addr); 3898255332Scy oip->ip_dst.s_addr = htonl(a3.s_addr); 3899255332Scy odst = 1; 3900170268Sdarrenr } else { 3901255332Scy a1.s_addr = ntohl(nat->nat_ndstaddr); 3902170268Sdarrenr a2.s_addr = ntohl(oip->ip_dst.s_addr); 3903255332Scy a3.s_addr = ntohl(nat->nat_nsrcaddr); 3904255332Scy a4.s_addr = ntohl(oip->ip_src.s_addr); 3905255332Scy oip->ip_dst.s_addr = htonl(a3.s_addr); 3906255332Scy oip->ip_src.s_addr = htonl(a1.s_addr); 3907255332Scy odst = 0; 3908170268Sdarrenr } 3909255332Scy sum1 = 0; 3910255332Scy sum2 = 0; 3911255332Scy sumd = 0; 3912255332Scy CALC_SUMD(a2.s_addr, a3.s_addr, sum1); 3913255332Scy CALC_SUMD(a4.s_addr, a1.s_addr, sum2); 3914255332Scy sumd = sum2 + sum1; 3915255332Scy if (sumd != 0) 3916255332Scy ipf_fix_datacksum(&oip->ip_sum, sumd); 3917130886Sdarrenr 3918170268Sdarrenr sumd2 = sumd; 3919170268Sdarrenr sum1 = 0; 3920170268Sdarrenr sum2 = 0; 3921170268Sdarrenr 3922130886Sdarrenr /* 3923170268Sdarrenr * Fix UDP pseudo header checksum to compensate for the 3924170268Sdarrenr * IP address change. 3925130886Sdarrenr */ 3926130886Sdarrenr if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) { 3927255332Scy u_32_t sum3, sum4, sumt; 3928255332Scy 392964580Sdarrenr /* 393067614Sdarrenr * Step 2 : 393167614Sdarrenr * For offending TCP/UDP IP packets, translate the ports as 393267614Sdarrenr * well, based on the NAT specification. Of course such 3933170268Sdarrenr * a change may be reflected in the ICMP checksum as well. 393467614Sdarrenr * 393567614Sdarrenr * Since the port fields are part of the TCP/UDP checksum 393667614Sdarrenr * of the offending IP packet, you need to adjust that checksum 3937255332Scy * as well... except that the change in the port numbers should 3938170268Sdarrenr * be offset by the checksum change. However, the TCP/UDP 3939170268Sdarrenr * checksum will also need to change if there has been an 3940170268Sdarrenr * IP address change. 394167614Sdarrenr */ 3942170268Sdarrenr if (odst == 1) { 3943255332Scy sum1 = ntohs(nat->nat_osport); 3944255332Scy sum4 = ntohs(tcp->th_sport); 3945255332Scy sum3 = ntohs(nat->nat_odport); 3946255332Scy sum2 = ntohs(tcp->th_dport); 3947145522Sdarrenr 3948170268Sdarrenr tcp->th_sport = htons(sum1); 3949255332Scy tcp->th_dport = htons(sum3); 3950170268Sdarrenr } else { 3951255332Scy sum1 = ntohs(nat->nat_ndport); 3952145522Sdarrenr sum2 = ntohs(tcp->th_dport); 3953255332Scy sum3 = ntohs(nat->nat_nsport); 3954255332Scy sum4 = ntohs(tcp->th_sport); 3955170268Sdarrenr 3956255332Scy tcp->th_dport = htons(sum3); 3957255332Scy tcp->th_sport = htons(sum1); 3958145522Sdarrenr } 3959255332Scy CALC_SUMD(sum4, sum1, sumt); 3960255332Scy sumd += sumt; 3961255332Scy CALC_SUMD(sum2, sum3, sumt); 3962255332Scy sumd += sumt; 396367614Sdarrenr 3964170268Sdarrenr if (sumd != 0 || sumd2 != 0) { 3965145522Sdarrenr /* 3966170268Sdarrenr * At this point, sumd is the delta to apply to the 3967170268Sdarrenr * TCP/UDP header, given the changes in both the IP 3968170268Sdarrenr * address and the ports and sumd2 is the delta to 3969170268Sdarrenr * apply to the ICMP header, given the IP address 3970170268Sdarrenr * change delta that may need to be applied to the 3971170268Sdarrenr * TCP/UDP checksum instead. 3972145522Sdarrenr * 3973170268Sdarrenr * If we will both the IP and TCP/UDP checksums 3974170268Sdarrenr * then the ICMP checksum changes by the address 3975170268Sdarrenr * delta applied to the TCP/UDP checksum. If we 3976170268Sdarrenr * do not change the TCP/UDP checksum them we 3977170268Sdarrenr * apply the delta in ports to the ICMP checksum. 3978145522Sdarrenr */ 3979161356Sguido if (oip->ip_p == IPPROTO_UDP) { 3980161356Sguido if ((dlen >= 8) && (*csump != 0)) { 3981255332Scy ipf_fix_datacksum(csump, sumd); 3982161356Sguido } else { 3983255332Scy CALC_SUMD(sum1, sum4, sumd2); 3984255332Scy CALC_SUMD(sum3, sum2, sumt); 3985255332Scy sumd2 += sumt; 3986161356Sguido } 3987170268Sdarrenr } else if (oip->ip_p == IPPROTO_TCP) { 3988145522Sdarrenr if (dlen >= 18) { 3989255332Scy ipf_fix_datacksum(csump, sumd); 3990145522Sdarrenr } else { 3991255332Scy CALC_SUMD(sum1, sum4, sumd2); 3992255332Scy CALC_SUMD(sum3, sum2, sumt); 3993255332Scy sumd2 += sumt; 399467614Sdarrenr } 399553642Sguido } 3996170268Sdarrenr if (sumd2 != 0) { 3997170268Sdarrenr sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 3998170268Sdarrenr sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 3999170268Sdarrenr sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); 4000255332Scy ipf_fix_incksum(0, &icmp->icmp_cksum, sumd2, 0); 4001130886Sdarrenr } 400253642Sguido } 4003145522Sdarrenr } else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) { 4004145522Sdarrenr icmphdr_t *orgicmp; 4005145522Sdarrenr 4006145522Sdarrenr /* 4007145522Sdarrenr * XXX - what if this is bogus hl and we go off the end ? 4008255332Scy * In this case, ipf_nat_icmperrorlookup() will have 4009255332Scy * returned NULL. 4010145522Sdarrenr */ 4011145522Sdarrenr orgicmp = (icmphdr_t *)dp; 4012145522Sdarrenr 4013170268Sdarrenr if (odst == 1) { 4014255332Scy if (orgicmp->icmp_id != nat->nat_osport) { 4015145522Sdarrenr 4016145522Sdarrenr /* 4017145522Sdarrenr * Fix ICMP checksum (of the offening ICMP 4018145522Sdarrenr * query packet) to compensate the change 4019145522Sdarrenr * in the ICMP id of the offending ICMP 4020145522Sdarrenr * packet. 4021145522Sdarrenr * 4022145522Sdarrenr * Since you modify orgicmp->icmp_id with 4023145522Sdarrenr * a delta (say x) and you compensate that 4024145522Sdarrenr * in origicmp->icmp_cksum with a delta 4025145522Sdarrenr * minus x, you don't have to adjust the 4026145522Sdarrenr * overall icmp->icmp_cksum 4027145522Sdarrenr */ 4028145522Sdarrenr sum1 = ntohs(orgicmp->icmp_id); 4029255332Scy sum2 = ntohs(nat->nat_oicmpid); 4030145522Sdarrenr CALC_SUMD(sum1, sum2, sumd); 4031255332Scy orgicmp->icmp_id = nat->nat_oicmpid; 4032255332Scy ipf_fix_datacksum(&orgicmp->icmp_cksum, sumd); 4033145522Sdarrenr } 4034145522Sdarrenr } /* nat_dir == NAT_INBOUND is impossible for icmp queries */ 403553642Sguido } 403653642Sguido return nat; 403753642Sguido} 403853642Sguido 403953642Sguido 404053642Sguido/* 4041255332Scy * MAP-IN MAP-OUT RDR-IN RDR-OUT 4042255332Scy * osrc X == src == src X 4043255332Scy * odst X == dst == dst X 4044255332Scy * nsrc == dst X X == dst 4045255332Scy * ndst == src X X == src 4046255332Scy * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND 4047255332Scy */ 4048255332Scy/* 4049145522Sdarrenr * NB: these lookups don't lock access to the list, it assumed that it has 4050145522Sdarrenr * already been done! 405153642Sguido */ 4052145522Sdarrenr/* ------------------------------------------------------------------------ */ 4053255332Scy/* Function: ipf_nat_inlookup */ 4054145522Sdarrenr/* Returns: nat_t* - NULL == no match, */ 4055145522Sdarrenr/* else pointer to matching NAT entry */ 4056145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4057145522Sdarrenr/* flags(I) - NAT flags for this packet */ 4058145522Sdarrenr/* p(I) - protocol for this packet */ 4059145522Sdarrenr/* src(I) - source IP address */ 4060145522Sdarrenr/* mapdst(I) - destination IP address */ 4061145522Sdarrenr/* */ 4062145522Sdarrenr/* Lookup a nat entry based on the mapped destination ip address/port and */ 4063145522Sdarrenr/* real source address/port. We use this lookup when receiving a packet, */ 4064145522Sdarrenr/* we're looking for a table entry, based on the destination address. */ 4065145522Sdarrenr/* */ 4066145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 4067145522Sdarrenr/* */ 4068255332Scy/* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ 4069145522Sdarrenr/* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 4070145522Sdarrenr/* */ 4071145522Sdarrenr/* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 4072145522Sdarrenr/* the packet is of said protocol */ 4073145522Sdarrenr/* ------------------------------------------------------------------------ */ 4074255332Scynat_t * 4075255332Scyipf_nat_inlookup(fin, flags, p, src, mapdst) 4076255332Scy fr_info_t *fin; 4077255332Scy u_int flags, p; 4078255332Scy struct in_addr src , mapdst; 407953642Sguido{ 4080255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 4081255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 4082145522Sdarrenr u_short sport, dport; 4083145522Sdarrenr grehdr_t *gre; 408492685Sdarrenr ipnat_t *ipn; 4085145522Sdarrenr u_int sflags; 4086145522Sdarrenr nat_t *nat; 4087145522Sdarrenr int nflags; 4088145522Sdarrenr u_32_t dst; 408992685Sdarrenr void *ifp; 4090255332Scy u_int hv, rhv; 409153642Sguido 4092161356Sguido ifp = fin->fin_ifp; 4093145522Sdarrenr gre = NULL; 409467614Sdarrenr dst = mapdst.s_addr; 4095145522Sdarrenr sflags = flags & NAT_TCPUDPICMP; 4096145522Sdarrenr 4097145522Sdarrenr switch (p) 4098145522Sdarrenr { 4099145522Sdarrenr case IPPROTO_TCP : 4100145522Sdarrenr case IPPROTO_UDP : 410192685Sdarrenr sport = htons(fin->fin_data[0]); 410292685Sdarrenr dport = htons(fin->fin_data[1]); 4103145522Sdarrenr break; 4104145522Sdarrenr case IPPROTO_ICMP : 4105323199Scy sport = 0; 4106323199Scy dport = fin->fin_data[1]; 4107145522Sdarrenr break; 4108145522Sdarrenr default : 4109255332Scy sport = 0; 4110255332Scy dport = 0; 4111145522Sdarrenr break; 411292685Sdarrenr } 411353642Sguido 4114145522Sdarrenr 4115145522Sdarrenr if ((flags & SI_WILDP) != 0) 4116145522Sdarrenr goto find_in_wild_ports; 4117145522Sdarrenr 4118255332Scy rhv = NAT_HASH_FN(dst, dport, 0xffffffff); 4119255332Scy rhv = NAT_HASH_FN(src.s_addr, rhv + sport, 0xffffffff); 4120255332Scy hv = rhv % softn->ipf_nat_table_sz; 4121255332Scy nat = softn->ipf_nat_table[1][hv]; 4122255332Scy /* TRACE dst, dport, src, sport, hv, nat */ 4123255332Scy 412453642Sguido for (; nat; nat = nat->nat_hnext[1]) { 4125161356Sguido if (nat->nat_ifps[0] != NULL) { 4126161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 4127161356Sguido continue; 4128255332Scy } 4129161356Sguido 4130255332Scy if (nat->nat_pr[0] != p) 4131255332Scy continue; 4132145522Sdarrenr 4133255332Scy switch (nat->nat_dir) 4134255332Scy { 4135255332Scy case NAT_INBOUND : 4136255332Scy case NAT_DIVERTIN : 4137255332Scy if (nat->nat_v[0] != 4) 4138255332Scy continue; 4139255332Scy if (nat->nat_osrcaddr != src.s_addr || 4140255332Scy nat->nat_odstaddr != dst) 4141255332Scy continue; 4142255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 4143255332Scy if (nat->nat_osport != sport) 4144145522Sdarrenr continue; 4145255332Scy if (nat->nat_odport != dport) 4146255332Scy continue; 4147255332Scy 4148255332Scy } else if (p == IPPROTO_ICMP) { 4149255332Scy if (nat->nat_osport != dport) { 4150255332Scy continue; 4151145522Sdarrenr } 4152255332Scy } 4153255332Scy break; 4154255332Scy case NAT_DIVERTOUT : 4155255332Scy if (nat->nat_dlocal) 4156255332Scy continue; 4157255332Scy case NAT_OUTBOUND : 4158255332Scy if (nat->nat_v[1] != 4) 4159255332Scy continue; 4160255332Scy if (nat->nat_dlocal) 4161255332Scy continue; 4162255332Scy if (nat->nat_dlocal) 4163255332Scy continue; 4164255332Scy if (nat->nat_ndstaddr != src.s_addr || 4165255332Scy nat->nat_nsrcaddr != dst) 4166255332Scy continue; 4167255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 4168255332Scy if (nat->nat_ndport != sport) 416992685Sdarrenr continue; 4170255332Scy if (nat->nat_nsport != dport) 417192685Sdarrenr continue; 4172255332Scy 4173255332Scy } else if (p == IPPROTO_ICMP) { 4174255332Scy if (nat->nat_osport != dport) { 4175255332Scy continue; 4176255332Scy } 417792685Sdarrenr } 4178255332Scy break; 4179255332Scy } 418092685Sdarrenr 4181255332Scy 4182255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 418392685Sdarrenr ipn = nat->nat_ptr; 418492685Sdarrenr if ((ipn != NULL) && (nat->nat_aps != NULL)) 4185255332Scy if (ipf_proxy_match(fin, nat) != 0) 418692685Sdarrenr continue; 418792685Sdarrenr } 4188255332Scy if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { 4189255332Scy nat->nat_ifps[0] = ifp; 4190255332Scy nat->nat_mtu[0] = GETIFMTU_4(ifp); 4191255332Scy } 4192255332Scy return nat; 419353642Sguido } 4194145522Sdarrenr 4195145522Sdarrenr /* 4196145522Sdarrenr * So if we didn't find it but there are wildcard members in the hash 4197145522Sdarrenr * table, go back and look for them. We do this search and update here 4198145522Sdarrenr * because it is modifying the NAT table and we want to do this only 4199145522Sdarrenr * for the first packet that matches. The exception, of course, is 4200145522Sdarrenr * for "dummy" (FI_IGNORE) lookups. 4201145522Sdarrenr */ 4202145522Sdarrenrfind_in_wild_ports: 4203255332Scy if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { 4204255332Scy NBUMPSIDEX(0, ns_lookup_miss, ns_lookup_miss_0); 420567614Sdarrenr return NULL; 4206255332Scy } 4207255332Scy if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { 4208255332Scy NBUMPSIDEX(0, ns_lookup_nowild, ns_lookup_nowild_0); 4209145522Sdarrenr return NULL; 4210255332Scy } 4211145522Sdarrenr 4212255332Scy RWLOCK_EXIT(&softc->ipf_nat); 4213145522Sdarrenr 421480482Sdarrenr hv = NAT_HASH_FN(dst, 0, 0xffffffff); 4215255332Scy hv = NAT_HASH_FN(src.s_addr, hv, softn->ipf_nat_table_sz); 4216255332Scy WRITE_ENTER(&softc->ipf_nat); 4217145522Sdarrenr 4218255332Scy nat = softn->ipf_nat_table[1][hv]; 4219255332Scy /* TRACE dst, src, hv, nat */ 422067614Sdarrenr for (; nat; nat = nat->nat_hnext[1]) { 4221161356Sguido if (nat->nat_ifps[0] != NULL) { 4222161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) 4223161356Sguido continue; 4224255332Scy } 4225145522Sdarrenr 4226255332Scy if (nat->nat_pr[0] != fin->fin_p) 422767614Sdarrenr continue; 4228145522Sdarrenr 4229255332Scy switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)) 4230255332Scy { 4231255332Scy case NAT_INBOUND : 4232255332Scy if (nat->nat_v[0] != 4) 4233255332Scy continue; 4234255332Scy if (nat->nat_osrcaddr != src.s_addr || 4235255332Scy nat->nat_odstaddr != dst) 4236255332Scy continue; 4237255332Scy break; 4238255332Scy case NAT_OUTBOUND : 4239255332Scy if (nat->nat_v[1] != 4) 4240255332Scy continue; 4241255332Scy if (nat->nat_ndstaddr != src.s_addr || 4242255332Scy nat->nat_nsrcaddr != dst) 4243255332Scy continue; 4244255332Scy break; 4245255332Scy } 4246255332Scy 4247145522Sdarrenr nflags = nat->nat_flags; 4248145522Sdarrenr if (!(nflags & (NAT_TCPUDP|SI_WILDP))) 4249145522Sdarrenr continue; 4250145522Sdarrenr 4251255332Scy if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags, 4252255332Scy NAT_INBOUND) == 1) { 4253145522Sdarrenr if ((fin->fin_flx & FI_IGNORE) != 0) 4254145522Sdarrenr break; 4255145522Sdarrenr if ((nflags & SI_CLONE) != 0) { 4256255332Scy nat = ipf_nat_clone(fin, nat); 4257145522Sdarrenr if (nat == NULL) 4258145522Sdarrenr break; 4259145522Sdarrenr } else { 4260255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 4261255332Scy softn->ipf_nat_stats.ns_wilds--; 4262255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 4263145522Sdarrenr } 4264255332Scy 4265255332Scy if (nat->nat_dir == NAT_INBOUND) { 4266255332Scy if (nat->nat_osport == 0) { 4267255332Scy nat->nat_osport = sport; 4268255332Scy nat->nat_nsport = sport; 4269255332Scy } 4270255332Scy if (nat->nat_odport == 0) { 4271255332Scy nat->nat_odport = dport; 4272255332Scy nat->nat_ndport = dport; 4273255332Scy } 4274255332Scy } else if (nat->nat_dir == NAT_OUTBOUND) { 4275255332Scy if (nat->nat_osport == 0) { 4276255332Scy nat->nat_osport = dport; 4277255332Scy nat->nat_nsport = dport; 4278255332Scy } 4279255332Scy if (nat->nat_odport == 0) { 4280255332Scy nat->nat_odport = sport; 4281255332Scy nat->nat_ndport = sport; 4282255332Scy } 4283255332Scy } 4284255332Scy if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { 4285255332Scy nat->nat_ifps[0] = ifp; 4286255332Scy nat->nat_mtu[0] = GETIFMTU_4(ifp); 4287255332Scy } 4288145522Sdarrenr nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 4289255332Scy ipf_nat_tabmove(softn, nat); 429067614Sdarrenr break; 429167614Sdarrenr } 429267614Sdarrenr } 4293145522Sdarrenr 4294255332Scy MUTEX_DOWNGRADE(&softc->ipf_nat); 4295145522Sdarrenr 4296255332Scy if (nat == NULL) { 4297255332Scy NBUMPSIDE(0, ns_lookup_miss); 4298255332Scy } 429967614Sdarrenr return nat; 430053642Sguido} 430153642Sguido 430253642Sguido 4303145522Sdarrenr/* ------------------------------------------------------------------------ */ 4304255332Scy/* Function: ipf_nat_tabmove */ 4305145522Sdarrenr/* Returns: Nil */ 4306255332Scy/* Parameters: softn(I) - pointer to NAT context structure */ 4307255332Scy/* nat(I) - pointer to NAT structure */ 4308145522Sdarrenr/* Write Lock: ipf_nat */ 4309145522Sdarrenr/* */ 4310145522Sdarrenr/* This function is only called for TCP/UDP NAT table entries where the */ 4311145522Sdarrenr/* original was placed in the table without hashing on the ports and we now */ 4312145522Sdarrenr/* want to include hashing on port numbers. */ 4313145522Sdarrenr/* ------------------------------------------------------------------------ */ 4314255332Scystatic void 4315255332Scyipf_nat_tabmove(softn, nat) 4316255332Scy ipf_nat_softc_t *softn; 4317255332Scy nat_t *nat; 431867614Sdarrenr{ 4319255332Scy u_int hv0, hv1, rhv0, rhv1; 4320255332Scy natstat_t *nsp; 432167614Sdarrenr nat_t **natp; 432267614Sdarrenr 4323145522Sdarrenr if (nat->nat_flags & SI_CLONE) 4324145522Sdarrenr return; 432572006Sdarrenr 4326255332Scy nsp = &softn->ipf_nat_stats; 432767614Sdarrenr /* 432867614Sdarrenr * Remove the NAT entry from the old location 432967614Sdarrenr */ 433067614Sdarrenr if (nat->nat_hnext[0]) 433167614Sdarrenr nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; 433267614Sdarrenr *nat->nat_phnext[0] = nat->nat_hnext[0]; 4333255332Scy nsp->ns_side[0].ns_bucketlen[nat->nat_hv[0] % 4334255332Scy softn->ipf_nat_table_sz]--; 433567614Sdarrenr 433667614Sdarrenr if (nat->nat_hnext[1]) 433767853Sdarrenr nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; 433867614Sdarrenr *nat->nat_phnext[1] = nat->nat_hnext[1]; 4339255332Scy nsp->ns_side[1].ns_bucketlen[nat->nat_hv[1] % 4340255332Scy softn->ipf_nat_table_sz]--; 434167614Sdarrenr 434267853Sdarrenr /* 434367853Sdarrenr * Add into the NAT table in the new position 434467853Sdarrenr */ 4345255332Scy rhv0 = NAT_HASH_FN(nat->nat_osrcaddr, nat->nat_osport, 0xffffffff); 4346255332Scy rhv0 = NAT_HASH_FN(nat->nat_odstaddr, rhv0 + nat->nat_odport, 4347255332Scy 0xffffffff); 4348255332Scy rhv1 = NAT_HASH_FN(nat->nat_nsrcaddr, nat->nat_nsport, 0xffffffff); 4349255332Scy rhv1 = NAT_HASH_FN(nat->nat_ndstaddr, rhv1 + nat->nat_ndport, 4350255332Scy 0xffffffff); 4351255332Scy 4352255332Scy hv0 = rhv0 % softn->ipf_nat_table_sz; 4353255332Scy hv1 = rhv1 % softn->ipf_nat_table_sz; 4354255332Scy 4355255332Scy if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) { 4356255332Scy u_int swap; 4357255332Scy 4358255332Scy swap = hv0; 4359255332Scy hv0 = hv1; 4360255332Scy hv1 = swap; 4361255332Scy } 4362255332Scy 4363255332Scy /* TRACE nat_osrcaddr, nat_osport, nat_odstaddr, nat_odport, hv0 */ 4364255332Scy /* TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, nat_ndport, hv1 */ 4365255332Scy 4366255332Scy nat->nat_hv[0] = rhv0; 4367255332Scy natp = &softn->ipf_nat_table[0][hv0]; 436867614Sdarrenr if (*natp) 436967614Sdarrenr (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 437067614Sdarrenr nat->nat_phnext[0] = natp; 437167614Sdarrenr nat->nat_hnext[0] = *natp; 437267614Sdarrenr *natp = nat; 4373255332Scy nsp->ns_side[0].ns_bucketlen[hv0]++; 437467614Sdarrenr 4375255332Scy nat->nat_hv[1] = rhv1; 4376255332Scy natp = &softn->ipf_nat_table[1][hv1]; 437767614Sdarrenr if (*natp) 437867614Sdarrenr (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 437967614Sdarrenr nat->nat_phnext[1] = natp; 438067614Sdarrenr nat->nat_hnext[1] = *natp; 438167614Sdarrenr *natp = nat; 4382255332Scy nsp->ns_side[1].ns_bucketlen[hv1]++; 438367614Sdarrenr} 438467614Sdarrenr 438567614Sdarrenr 4386145522Sdarrenr/* ------------------------------------------------------------------------ */ 4387255332Scy/* Function: ipf_nat_outlookup */ 4388145522Sdarrenr/* Returns: nat_t* - NULL == no match, */ 4389145522Sdarrenr/* else pointer to matching NAT entry */ 4390145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4391145522Sdarrenr/* flags(I) - NAT flags for this packet */ 4392145522Sdarrenr/* p(I) - protocol for this packet */ 4393145522Sdarrenr/* src(I) - source IP address */ 4394145522Sdarrenr/* dst(I) - destination IP address */ 4395255332Scy/* rw(I) - 1 == write lock on held, 0 == read lock. */ 4396145522Sdarrenr/* */ 4397145522Sdarrenr/* Lookup a nat entry based on the source 'real' ip address/port and */ 4398145522Sdarrenr/* destination address/port. We use this lookup when sending a packet out, */ 4399145522Sdarrenr/* we're looking for a table entry, based on the source address. */ 4400145522Sdarrenr/* */ 4401145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ 4402145522Sdarrenr/* */ 4403255332Scy/* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ 4404145522Sdarrenr/* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ 4405145522Sdarrenr/* */ 4406145522Sdarrenr/* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ 4407145522Sdarrenr/* the packet is of said protocol */ 4408145522Sdarrenr/* ------------------------------------------------------------------------ */ 4409255332Scynat_t * 4410255332Scyipf_nat_outlookup(fin, flags, p, src, dst) 4411255332Scy fr_info_t *fin; 4412255332Scy u_int flags, p; 4413255332Scy struct in_addr src , dst; 441453642Sguido{ 4415255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 4416255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 4417145522Sdarrenr u_short sport, dport; 4418145522Sdarrenr u_int sflags; 441992685Sdarrenr ipnat_t *ipn; 4420145522Sdarrenr nat_t *nat; 442192685Sdarrenr void *ifp; 442253642Sguido u_int hv; 442353642Sguido 442492685Sdarrenr ifp = fin->fin_ifp; 4425145522Sdarrenr sflags = flags & IPN_TCPUDPICMP; 4426145522Sdarrenr 4427145522Sdarrenr switch (p) 4428145522Sdarrenr { 4429145522Sdarrenr case IPPROTO_TCP : 4430145522Sdarrenr case IPPROTO_UDP : 4431145522Sdarrenr sport = htons(fin->fin_data[0]); 4432145522Sdarrenr dport = htons(fin->fin_data[1]); 4433145522Sdarrenr break; 4434145522Sdarrenr case IPPROTO_ICMP : 4435323199Scy sport = 0; 4436323199Scy dport = fin->fin_data[1]; 4437145522Sdarrenr break; 4438145522Sdarrenr default : 4439323199Scy sport = 0; 4440323199Scy dport = 0; 4441145522Sdarrenr break; 444292685Sdarrenr } 444353642Sguido 4444145522Sdarrenr if ((flags & SI_WILDP) != 0) 4445145522Sdarrenr goto find_out_wild_ports; 4446145522Sdarrenr 4447255332Scy hv = NAT_HASH_FN(src.s_addr, sport, 0xffffffff); 4448255332Scy hv = NAT_HASH_FN(dst.s_addr, hv + dport, softn->ipf_nat_table_sz); 4449255332Scy nat = softn->ipf_nat_table[0][hv]; 4450255332Scy 4451255332Scy /* TRACE src, sport, dst, dport, hv, nat */ 4452255332Scy 445353642Sguido for (; nat; nat = nat->nat_hnext[0]) { 4454161356Sguido if (nat->nat_ifps[1] != NULL) { 4455161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 4456161356Sguido continue; 4457255332Scy } 4458161356Sguido 4459255332Scy if (nat->nat_pr[1] != p) 4460255332Scy continue; 446153642Sguido 4462255332Scy switch (nat->nat_dir) 4463255332Scy { 4464255332Scy case NAT_INBOUND : 4465255332Scy case NAT_DIVERTIN : 4466255332Scy if (nat->nat_v[1] != 4) 4467255332Scy continue; 4468255332Scy if (nat->nat_ndstaddr != src.s_addr || 4469255332Scy nat->nat_nsrcaddr != dst.s_addr) 4470255332Scy continue; 4471255332Scy 4472255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 4473255332Scy if (nat->nat_ndport != sport) 4474145522Sdarrenr continue; 4475255332Scy if (nat->nat_nsport != dport) 447692685Sdarrenr continue; 4477255332Scy 4478255332Scy } else if (p == IPPROTO_ICMP) { 4479255332Scy if (nat->nat_osport != dport) { 448092685Sdarrenr continue; 4481255332Scy } 448292685Sdarrenr } 4483255332Scy break; 4484255332Scy case NAT_OUTBOUND : 4485255332Scy case NAT_DIVERTOUT : 4486255332Scy if (nat->nat_v[0] != 4) 4487255332Scy continue; 4488255332Scy if (nat->nat_osrcaddr != src.s_addr || 4489255332Scy nat->nat_odstaddr != dst.s_addr) 4490255332Scy continue; 449192685Sdarrenr 4492255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 4493255332Scy if (nat->nat_odport != dport) 449492685Sdarrenr continue; 4495255332Scy if (nat->nat_osport != sport) 4496255332Scy continue; 4497255332Scy 4498255332Scy } else if (p == IPPROTO_ICMP) { 4499255332Scy if (nat->nat_osport != dport) { 4500255332Scy continue; 4501255332Scy } 4502255332Scy } 4503255332Scy break; 450492685Sdarrenr } 4505255332Scy 4506255332Scy ipn = nat->nat_ptr; 4507255332Scy if ((ipn != NULL) && (nat->nat_aps != NULL)) 4508255332Scy if (ipf_proxy_match(fin, nat) != 0) 4509255332Scy continue; 4510255332Scy 4511255332Scy if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { 4512255332Scy nat->nat_ifps[1] = ifp; 4513255332Scy nat->nat_mtu[1] = GETIFMTU_4(ifp); 4514255332Scy } 4515255332Scy return nat; 451653642Sguido } 4517145522Sdarrenr 4518145522Sdarrenr /* 4519145522Sdarrenr * So if we didn't find it but there are wildcard members in the hash 4520145522Sdarrenr * table, go back and look for them. We do this search and update here 4521145522Sdarrenr * because it is modifying the NAT table and we want to do this only 4522145522Sdarrenr * for the first packet that matches. The exception, of course, is 4523145522Sdarrenr * for "dummy" (FI_IGNORE) lookups. 4524145522Sdarrenr */ 4525145522Sdarrenrfind_out_wild_ports: 4526255332Scy if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { 4527255332Scy NBUMPSIDEX(1, ns_lookup_miss, ns_lookup_miss_1); 452867614Sdarrenr return NULL; 4529255332Scy } 4530255332Scy if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { 4531255332Scy NBUMPSIDEX(1, ns_lookup_nowild, ns_lookup_nowild_1); 4532145522Sdarrenr return NULL; 4533255332Scy } 453492685Sdarrenr 4535255332Scy RWLOCK_EXIT(&softc->ipf_nat); 4536145522Sdarrenr 4537255332Scy hv = NAT_HASH_FN(src.s_addr, 0, 0xffffffff); 4538255332Scy hv = NAT_HASH_FN(dst.s_addr, hv, softn->ipf_nat_table_sz); 4539145522Sdarrenr 4540255332Scy WRITE_ENTER(&softc->ipf_nat); 4541145522Sdarrenr 4542255332Scy nat = softn->ipf_nat_table[0][hv]; 454367614Sdarrenr for (; nat; nat = nat->nat_hnext[0]) { 4544161356Sguido if (nat->nat_ifps[1] != NULL) { 4545161356Sguido if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) 4546161356Sguido continue; 4547255332Scy } 4548145522Sdarrenr 4549255332Scy if (nat->nat_pr[1] != fin->fin_p) 455067614Sdarrenr continue; 4551145522Sdarrenr 4552255332Scy switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)) 4553255332Scy { 4554255332Scy case NAT_INBOUND : 4555255332Scy if (nat->nat_v[1] != 4) 4556255332Scy continue; 4557255332Scy if (nat->nat_ndstaddr != src.s_addr || 4558255332Scy nat->nat_nsrcaddr != dst.s_addr) 4559255332Scy continue; 4560255332Scy break; 4561255332Scy case NAT_OUTBOUND : 4562255332Scy if (nat->nat_v[0] != 4) 4563255332Scy continue; 4564255332Scy if (nat->nat_osrcaddr != src.s_addr || 4565255332Scy nat->nat_odstaddr != dst.s_addr) 4566255332Scy continue; 4567255332Scy break; 4568255332Scy } 4569255332Scy 4570255332Scy if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP))) 4571145522Sdarrenr continue; 4572145522Sdarrenr 4573255332Scy if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags, 4574255332Scy NAT_OUTBOUND) == 1) { 4575145522Sdarrenr if ((fin->fin_flx & FI_IGNORE) != 0) 4576145522Sdarrenr break; 4577255332Scy if ((nat->nat_flags & SI_CLONE) != 0) { 4578255332Scy nat = ipf_nat_clone(fin, nat); 4579145522Sdarrenr if (nat == NULL) 4580145522Sdarrenr break; 4581145522Sdarrenr } else { 4582255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 4583255332Scy softn->ipf_nat_stats.ns_wilds--; 4584255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 4585145522Sdarrenr } 4586255332Scy 4587255332Scy if (nat->nat_dir == NAT_OUTBOUND) { 4588255332Scy if (nat->nat_osport == 0) { 4589255332Scy nat->nat_osport = sport; 4590255332Scy nat->nat_nsport = sport; 4591255332Scy } 4592255332Scy if (nat->nat_odport == 0) { 4593255332Scy nat->nat_odport = dport; 4594255332Scy nat->nat_ndport = dport; 4595255332Scy } 4596255332Scy } else if (nat->nat_dir == NAT_INBOUND) { 4597255332Scy if (nat->nat_osport == 0) { 4598255332Scy nat->nat_osport = dport; 4599255332Scy nat->nat_nsport = dport; 4600255332Scy } 4601255332Scy if (nat->nat_odport == 0) { 4602255332Scy nat->nat_odport = sport; 4603255332Scy nat->nat_ndport = sport; 4604255332Scy } 4605255332Scy } 4606255332Scy if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { 4607255332Scy nat->nat_ifps[1] = ifp; 4608255332Scy nat->nat_mtu[1] = GETIFMTU_4(ifp); 4609255332Scy } 4610145522Sdarrenr nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); 4611255332Scy ipf_nat_tabmove(softn, nat); 461267614Sdarrenr break; 461367614Sdarrenr } 461467614Sdarrenr } 4615145522Sdarrenr 4616255332Scy MUTEX_DOWNGRADE(&softc->ipf_nat); 4617145522Sdarrenr 4618255332Scy if (nat == NULL) { 4619255332Scy NBUMPSIDE(1, ns_lookup_miss); 4620255332Scy } 462167614Sdarrenr return nat; 462253642Sguido} 462353642Sguido 462453642Sguido 4625145522Sdarrenr/* ------------------------------------------------------------------------ */ 4626255332Scy/* Function: ipf_nat_lookupredir */ 4627145522Sdarrenr/* Returns: nat_t* - NULL == no match, */ 4628145522Sdarrenr/* else pointer to matching NAT entry */ 4629145522Sdarrenr/* Parameters: np(I) - pointer to description of packet to find NAT table */ 4630145522Sdarrenr/* entry for. */ 4631145522Sdarrenr/* */ 4632145522Sdarrenr/* Lookup the NAT tables to search for a matching redirect */ 4633161356Sguido/* The contents of natlookup_t should imitate those found in a packet that */ 4634161356Sguido/* would be translated - ie a packet coming in for RDR or going out for MAP.*/ 4635161356Sguido/* We can do the lookup in one of two ways, imitating an inbound or */ 4636161356Sguido/* outbound packet. By default we assume outbound, unless IPN_IN is set. */ 4637161356Sguido/* For IN, the fields are set as follows: */ 4638161356Sguido/* nl_real* = source information */ 4639161356Sguido/* nl_out* = destination information (translated) */ 4640161356Sguido/* For an out packet, the fields are set like this: */ 4641161356Sguido/* nl_in* = source information (untranslated) */ 4642161356Sguido/* nl_out* = destination information (translated) */ 4643145522Sdarrenr/* ------------------------------------------------------------------------ */ 4644255332Scynat_t * 4645255332Scyipf_nat_lookupredir(np) 4646255332Scy natlookup_t *np; 464753642Sguido{ 4648145522Sdarrenr fr_info_t fi; 464953642Sguido nat_t *nat; 465053642Sguido 465192685Sdarrenr bzero((char *)&fi, sizeof(fi)); 4652145522Sdarrenr if (np->nl_flags & IPN_IN) { 4653145522Sdarrenr fi.fin_data[0] = ntohs(np->nl_realport); 4654145522Sdarrenr fi.fin_data[1] = ntohs(np->nl_outport); 4655145522Sdarrenr } else { 4656145522Sdarrenr fi.fin_data[0] = ntohs(np->nl_inport); 4657145522Sdarrenr fi.fin_data[1] = ntohs(np->nl_outport); 4658145522Sdarrenr } 4659145522Sdarrenr if (np->nl_flags & IPN_TCP) 4660145522Sdarrenr fi.fin_p = IPPROTO_TCP; 4661145522Sdarrenr else if (np->nl_flags & IPN_UDP) 4662145522Sdarrenr fi.fin_p = IPPROTO_UDP; 4663145522Sdarrenr else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) 4664145522Sdarrenr fi.fin_p = IPPROTO_ICMP; 466592685Sdarrenr 466653642Sguido /* 4667145522Sdarrenr * We can do two sorts of lookups: 4668145522Sdarrenr * - IPN_IN: we have the `real' and `out' address, look for `in'. 4669145522Sdarrenr * - default: we have the `in' and `out' address, look for `real'. 467053642Sguido */ 4671145522Sdarrenr if (np->nl_flags & IPN_IN) { 4672255332Scy if ((nat = ipf_nat_inlookup(&fi, np->nl_flags, fi.fin_p, 4673255332Scy np->nl_realip, np->nl_outip))) { 4674255332Scy np->nl_inip = nat->nat_odstip; 4675255332Scy np->nl_inport = nat->nat_odport; 4676145522Sdarrenr } 4677145522Sdarrenr } else { 4678145522Sdarrenr /* 4679145522Sdarrenr * If nl_inip is non null, this is a lookup based on the real 4680145522Sdarrenr * ip address. Else, we use the fake. 4681145522Sdarrenr */ 4682255332Scy if ((nat = ipf_nat_outlookup(&fi, np->nl_flags, fi.fin_p, 4683145522Sdarrenr np->nl_inip, np->nl_outip))) { 4684145522Sdarrenr 4685145522Sdarrenr if ((np->nl_flags & IPN_FINDFORWARD) != 0) { 4686145522Sdarrenr fr_info_t fin; 4687145522Sdarrenr bzero((char *)&fin, sizeof(fin)); 4688255332Scy fin.fin_p = nat->nat_pr[0]; 4689255332Scy fin.fin_data[0] = ntohs(nat->nat_ndport); 4690255332Scy fin.fin_data[1] = ntohs(nat->nat_nsport); 4691255332Scy if (ipf_nat_inlookup(&fin, np->nl_flags, 4692255332Scy fin.fin_p, nat->nat_ndstip, 4693255332Scy nat->nat_nsrcip) != NULL) { 4694145522Sdarrenr np->nl_flags &= ~IPN_FINDFORWARD; 4695145522Sdarrenr } 4696145522Sdarrenr } 4697145522Sdarrenr 4698315079Scy np->nl_realip = nat->nat_odstip; 4699315079Scy np->nl_realport = nat->nat_odport; 4700145522Sdarrenr } 4701145522Sdarrenr } 4702145522Sdarrenr 470353642Sguido return nat; 470453642Sguido} 470553642Sguido 470653642Sguido 4707145522Sdarrenr/* ------------------------------------------------------------------------ */ 4708255332Scy/* Function: ipf_nat_match */ 4709145522Sdarrenr/* Returns: int - 0 == no match, 1 == match */ 4710145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4711145522Sdarrenr/* np(I) - pointer to NAT rule */ 4712145522Sdarrenr/* */ 4713145522Sdarrenr/* Pull the matching of a packet against a NAT rule out of that complex */ 4714255332Scy/* loop inside ipf_nat_checkin() and lay it out properly in its own function. */ 4715145522Sdarrenr/* ------------------------------------------------------------------------ */ 4716255332Scystatic int 4717255332Scyipf_nat_match(fin, np) 4718255332Scy fr_info_t *fin; 4719255332Scy ipnat_t *np; 472060852Sdarrenr{ 4721255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 472260852Sdarrenr frtuc_t *ft; 4723255332Scy int match; 472460852Sdarrenr 4725255332Scy match = 0; 4726255332Scy switch (np->in_osrcatype) 4727255332Scy { 4728255332Scy case FRI_NORMAL : 4729255332Scy match = ((fin->fin_saddr & np->in_osrcmsk) != np->in_osrcaddr); 4730255332Scy break; 4731255332Scy case FRI_LOOKUP : 4732255332Scy match = (*np->in_osrcfunc)(softc, np->in_osrcptr, 4733255332Scy 4, &fin->fin_saddr, fin->fin_plen); 4734255332Scy break; 4735255332Scy } 4736255332Scy match ^= ((np->in_flags & IPN_NOTSRC) != 0); 4737255332Scy if (match) 473860852Sdarrenr return 0; 473960852Sdarrenr 4740255332Scy match = 0; 4741255332Scy switch (np->in_odstatype) 4742255332Scy { 4743255332Scy case FRI_NORMAL : 4744255332Scy match = ((fin->fin_daddr & np->in_odstmsk) != np->in_odstaddr); 4745255332Scy break; 4746255332Scy case FRI_LOOKUP : 4747255332Scy match = (*np->in_odstfunc)(softc, np->in_odstptr, 4748255332Scy 4, &fin->fin_daddr, fin->fin_plen); 4749255332Scy break; 4750255332Scy } 4751255332Scy 4752255332Scy match ^= ((np->in_flags & IPN_NOTDST) != 0); 4753255332Scy if (match) 475460852Sdarrenr return 0; 4755145522Sdarrenr 475660852Sdarrenr ft = &np->in_tuc; 4757145522Sdarrenr if (!(fin->fin_flx & FI_TCPUDP) || 4758145522Sdarrenr (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { 475960852Sdarrenr if (ft->ftu_scmp || ft->ftu_dcmp) 476060852Sdarrenr return 0; 476160852Sdarrenr return 1; 476260852Sdarrenr } 476360852Sdarrenr 4764255332Scy return ipf_tcpudpchk(&fin->fin_fi, ft); 476560852Sdarrenr} 476660852Sdarrenr 476760852Sdarrenr 4768145522Sdarrenr/* ------------------------------------------------------------------------ */ 4769255332Scy/* Function: ipf_nat_update */ 4770145522Sdarrenr/* Returns: Nil */ 4771255332Scy/* Parameters: fin(I) - pointer to packet information */ 4772255332Scy/* nat(I) - pointer to NAT structure */ 4773145522Sdarrenr/* */ 4774145522Sdarrenr/* Updates the lifetime of a NAT table entry for non-TCP packets. Must be */ 4775255332Scy/* called with fin_rev updated - i.e. after calling ipf_nat_proto(). */ 4776255332Scy/* */ 4777255332Scy/* This *MUST* be called after ipf_nat_proto() as it expects fin_rev to */ 4778255332Scy/* already be set. */ 4779145522Sdarrenr/* ------------------------------------------------------------------------ */ 4780255332Scyvoid 4781255332Scyipf_nat_update(fin, nat) 4782255332Scy fr_info_t *fin; 4783255332Scy nat_t *nat; 478453642Sguido{ 4785255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 4786255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 4787145522Sdarrenr ipftq_t *ifq, *ifq2; 4788145522Sdarrenr ipftqent_t *tqe; 4789255332Scy ipnat_t *np = nat->nat_ptr; 4790145522Sdarrenr 4791145522Sdarrenr tqe = &nat->nat_tqe; 4792145522Sdarrenr ifq = tqe->tqe_ifq; 4793145522Sdarrenr 4794145522Sdarrenr /* 4795145522Sdarrenr * We allow over-riding of NAT timeouts from NAT rules, even for 4796145522Sdarrenr * TCP, however, if it is TCP and there is no rule timeout set, 4797145522Sdarrenr * then do not update the timeout here. 4798145522Sdarrenr */ 4799255332Scy if (np != NULL) { 4800255332Scy np->in_bytes[fin->fin_rev] += fin->fin_plen; 4801145522Sdarrenr ifq2 = np->in_tqehead[fin->fin_rev]; 4802255332Scy } else { 4803145522Sdarrenr ifq2 = NULL; 4804255332Scy } 4805145522Sdarrenr 4806255332Scy if (nat->nat_pr[0] == IPPROTO_TCP && ifq2 == NULL) { 4807255332Scy (void) ipf_tcp_age(&nat->nat_tqe, fin, softn->ipf_nat_tcptq, 4808255332Scy 0, 2); 4809145522Sdarrenr } else { 4810145522Sdarrenr if (ifq2 == NULL) { 4811255332Scy if (nat->nat_pr[0] == IPPROTO_UDP) 4812255332Scy ifq2 = fin->fin_rev ? &softn->ipf_nat_udpacktq : 4813255332Scy &softn->ipf_nat_udptq; 4814255332Scy else if (nat->nat_pr[0] == IPPROTO_ICMP || 4815255332Scy nat->nat_pr[0] == IPPROTO_ICMPV6) 4816255332Scy ifq2 = fin->fin_rev ? &softn->ipf_nat_icmpacktq: 4817255332Scy &softn->ipf_nat_icmptq; 4818145522Sdarrenr else 4819255332Scy ifq2 = &softn->ipf_nat_iptq; 4820145522Sdarrenr } 4821145522Sdarrenr 4822255332Scy ipf_movequeue(softc->ipf_ticks, tqe, ifq, ifq2); 4823145522Sdarrenr } 4824145522Sdarrenr} 4825145522Sdarrenr 4826145522Sdarrenr 4827145522Sdarrenr/* ------------------------------------------------------------------------ */ 4828255332Scy/* Function: ipf_nat_checkout */ 4829145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 4830145522Sdarrenr/* 0 == no packet translation occurred, */ 4831145522Sdarrenr/* 1 == packet was successfully translated. */ 4832145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 4833145522Sdarrenr/* passp(I) - pointer to filtering result flags */ 4834145522Sdarrenr/* */ 4835145522Sdarrenr/* Check to see if an outcoming packet should be changed. ICMP packets are */ 4836145522Sdarrenr/* first checked to see if they match an existing entry (if an error), */ 4837145522Sdarrenr/* otherwise a search of the current NAT table is made. If neither results */ 4838145522Sdarrenr/* in a match then a search for a matching NAT rule is made. Create a new */ 4839145522Sdarrenr/* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 4840145522Sdarrenr/* packet header(s) as required. */ 4841145522Sdarrenr/* ------------------------------------------------------------------------ */ 4842255332Scyint 4843255332Scyipf_nat_checkout(fin, passp) 4844255332Scy fr_info_t *fin; 4845255332Scy u_32_t *passp; 4846145522Sdarrenr{ 4847255332Scy ipnat_t *np = NULL, *npnext; 4848145522Sdarrenr struct ifnet *ifp, *sifp; 4849255332Scy ipf_main_softc_t *softc; 4850255332Scy ipf_nat_softc_t *softn; 4851145522Sdarrenr icmphdr_t *icmp = NULL; 485253642Sguido tcphdr_t *tcp = NULL; 4853145522Sdarrenr int rval, natfailed; 4854145522Sdarrenr u_int nflags = 0; 4855145522Sdarrenr u_32_t ipa, iph; 4856145522Sdarrenr int natadd = 1; 485753642Sguido frentry_t *fr; 485853642Sguido nat_t *nat; 485953642Sguido 4860255332Scy if (fin->fin_v == 6) { 4861255332Scy#ifdef USE_INET6 4862255332Scy return ipf_nat6_checkout(fin, passp); 4863255332Scy#else 486453642Sguido return 0; 4865255332Scy#endif 4866255332Scy } 486753642Sguido 4868255332Scy softc = fin->fin_main_soft; 4869255332Scy softn = softc->ipf_nat_soft; 4870255332Scy 4871255332Scy if (softn->ipf_nat_lock != 0) 4872255332Scy return 0; 4873255332Scy if (softn->ipf_nat_stats.ns_rules == 0 && 4874255332Scy softn->ipf_nat_instances == NULL) 4875255332Scy return 0; 4876255332Scy 4877145522Sdarrenr natfailed = 0; 4878145522Sdarrenr fr = fin->fin_fr; 4879145522Sdarrenr sifp = fin->fin_ifp; 4880170268Sdarrenr if (fr != NULL) { 4881255332Scy ifp = fr->fr_tifs[fin->fin_rev].fd_ptr; 4882170268Sdarrenr if ((ifp != NULL) && (ifp != (void *)-1)) 4883170268Sdarrenr fin->fin_ifp = ifp; 4884170268Sdarrenr } 488592685Sdarrenr ifp = fin->fin_ifp; 488653642Sguido 4887145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 4888145522Sdarrenr switch (fin->fin_p) 4889145522Sdarrenr { 4890145522Sdarrenr case IPPROTO_TCP : 489153642Sguido nflags = IPN_TCP; 4892145522Sdarrenr break; 4893145522Sdarrenr case IPPROTO_UDP : 489453642Sguido nflags = IPN_UDP; 4895145522Sdarrenr break; 4896145522Sdarrenr case IPPROTO_ICMP : 4897145522Sdarrenr icmp = fin->fin_dp; 4898145522Sdarrenr 4899145522Sdarrenr /* 4900145522Sdarrenr * This is an incoming packet, so the destination is 4901145522Sdarrenr * the icmp_id and the source port equals 0 4902145522Sdarrenr */ 4903255332Scy if ((fin->fin_flx & FI_ICMPQUERY) != 0) 4904145522Sdarrenr nflags = IPN_ICMPQUERY; 4905145522Sdarrenr break; 4906145522Sdarrenr default : 4907145522Sdarrenr break; 490853642Sguido } 4909255332Scy 4910145522Sdarrenr if ((nflags & IPN_TCPUDP)) 4911145522Sdarrenr tcp = fin->fin_dp; 491253642Sguido } 491353642Sguido 491492685Sdarrenr ipa = fin->fin_saddr; 491553642Sguido 4916255332Scy READ_ENTER(&softc->ipf_nat); 491760852Sdarrenr 4918255332Scy if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && 4919255332Scy (nat = ipf_nat_icmperror(fin, &nflags, NAT_OUTBOUND))) 4920145522Sdarrenr /*EMPTY*/; 4921255332Scy else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) 492253642Sguido natadd = 0; 4923255332Scy else if ((nat = ipf_nat_outlookup(fin, nflags|NAT_SEARCH, 4924255332Scy (u_int)fin->fin_p, fin->fin_src, 4925255332Scy fin->fin_dst))) { 492653642Sguido nflags = nat->nat_flags; 4927255332Scy } else if (fin->fin_off == 0) { 4928255332Scy u_32_t hv, msk, nmsk = 0; 492992685Sdarrenr 493053642Sguido /* 493153642Sguido * If there is no current entry in the nat table for this IP#, 493253642Sguido * create one for it (if there is a matching rule). 493353642Sguido */ 493453642Sguidomaskloop: 4935255332Scy msk = softn->ipf_nat_map_active_masks[nmsk]; 4936255332Scy iph = ipa & msk; 4937255332Scy hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_maprules_sz); 4938255332Scyretry_roundrobin: 4939255332Scy for (np = softn->ipf_nat_map_rules[hv]; np; np = npnext) { 4940255332Scy npnext = np->in_mnext; 4941161356Sguido if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) 494260852Sdarrenr continue; 4943255332Scy if (np->in_v[0] != 4) 494460852Sdarrenr continue; 4945255332Scy if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p)) 4946145522Sdarrenr continue; 4947255332Scy if ((np->in_flags & IPN_RF) && 4948255332Scy !(np->in_flags & nflags)) 4949145522Sdarrenr continue; 495060852Sdarrenr if (np->in_flags & IPN_FILTER) { 4951255332Scy switch (ipf_nat_match(fin, np)) 4952255332Scy { 4953255332Scy case 0 : 495460852Sdarrenr continue; 4955255332Scy case -1 : 4956337948Scy rval = -3; 4957255332Scy goto outmatchfail; 4958255332Scy case 1 : 4959255332Scy default : 4960255332Scy break; 4961255332Scy } 4962255332Scy } else if ((ipa & np->in_osrcmsk) != np->in_osrcaddr) 496360852Sdarrenr continue; 4964145522Sdarrenr 4965145522Sdarrenr if ((fr != NULL) && 4966255332Scy !ipf_matchtag(&np->in_tag, &fr->fr_nattag)) 496792685Sdarrenr continue; 4968145522Sdarrenr 4969255332Scy if (np->in_plabel != -1) { 4970145522Sdarrenr if (((np->in_flags & IPN_FILTER) == 0) && 4971255332Scy (np->in_odport != fin->fin_data[1])) 4972145522Sdarrenr continue; 4973255332Scy if (ipf_proxy_ok(fin, tcp, np) == 0) 4974145522Sdarrenr continue; 4975145522Sdarrenr } 4976145522Sdarrenr 4977255332Scy if (np->in_flags & IPN_NO) { 497892685Sdarrenr np->in_hits++; 497992685Sdarrenr break; 4980145522Sdarrenr } 4981255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 4982255332Scy /* 4983255332Scy * If we've matched a round-robin rule but it has 4984255332Scy * moved in the list since we got it, start over as 4985255332Scy * this is now no longer correct. 4986255332Scy */ 4987255332Scy if (npnext != np->in_mnext) { 4988255332Scy if ((np->in_flags & IPN_ROUNDR) != 0) { 4989255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 4990255332Scy goto retry_roundrobin; 4991255332Scy } 4992255332Scy npnext = np->in_mnext; 4993145522Sdarrenr } 4994255332Scy 4995255332Scy nat = ipf_nat_add(fin, np, NULL, nflags, NAT_OUTBOUND); 4996255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 4997255332Scy if (nat != NULL) { 4998255332Scy natfailed = 0; 4999255332Scy break; 5000255332Scy } 5001337948Scy natfailed = -2; 500253642Sguido } 5003255332Scy if ((np == NULL) && (nmsk < softn->ipf_nat_map_max)) { 5004255332Scy nmsk++; 5005255332Scy goto maskloop; 5006255332Scy } 500753642Sguido } 500853642Sguido 5009145522Sdarrenr if (nat != NULL) { 5010255332Scy rval = ipf_nat_out(fin, nat, natadd, nflags); 5011145522Sdarrenr if (rval == 1) { 5012145522Sdarrenr MUTEX_ENTER(&nat->nat_lock); 5013255332Scy ipf_nat_update(fin, nat); 5014255332Scy nat->nat_bytes[1] += fin->fin_plen; 5015255332Scy nat->nat_pkts[1]++; 5016255332Scy fin->fin_pktnum = nat->nat_pkts[1]; 5017145522Sdarrenr MUTEX_EXIT(&nat->nat_lock); 5018145522Sdarrenr } 5019145522Sdarrenr } else 5020145522Sdarrenr rval = natfailed; 5021255332Scyoutmatchfail: 5022255332Scy RWLOCK_EXIT(&softc->ipf_nat); 5023145522Sdarrenr 5024255332Scy switch (rval) 5025255332Scy { 5026337948Scy case -3 : 5027337948Scy /* ipf_nat_match() failure */ 5028337948Scy /* FALLTHROUGH */ 5029337948Scy case -2 : 5030337948Scy /* retry_roundrobin loop failure */ 5031337948Scy /* FALLTHROUGH */ 5032255332Scy case -1 : 5033337948Scy /* proxy failure detected by ipf_nat_out() */ 5034255332Scy if (passp != NULL) { 5035337948Scy DT2(frb_natv4out, fr_info_t *, fin, int, rval); 5036255332Scy NBUMPSIDED(1, ns_drop); 5037145522Sdarrenr *passp = FR_BLOCK; 5038255332Scy fin->fin_reason = FRB_NATV4; 5039255332Scy } 5040145522Sdarrenr fin->fin_flx |= FI_BADNAT; 5041255332Scy NBUMPSIDED(1, ns_badnat); 5042337948Scy rval = -1; /* We only return -1 on error. */ 5043255332Scy break; 5044255332Scy case 0 : 5045255332Scy NBUMPSIDE(1, ns_ignored); 5046255332Scy break; 5047255332Scy case 1 : 5048255332Scy NBUMPSIDE(1, ns_translated); 5049255332Scy break; 5050145522Sdarrenr } 5051145522Sdarrenr fin->fin_ifp = sifp; 5052145522Sdarrenr return rval; 5053145522Sdarrenr} 5054145522Sdarrenr 5055145522Sdarrenr/* ------------------------------------------------------------------------ */ 5056255332Scy/* Function: ipf_nat_out */ 5057145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 5058145522Sdarrenr/* 1 == packet was successfully translated. */ 5059145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 5060145522Sdarrenr/* nat(I) - pointer to NAT structure */ 5061145522Sdarrenr/* natadd(I) - flag indicating if it is safe to add frag cache */ 5062145522Sdarrenr/* nflags(I) - NAT flags set for this packet */ 5063145522Sdarrenr/* */ 5064145522Sdarrenr/* Translate a packet coming "out" on an interface. */ 5065145522Sdarrenr/* ------------------------------------------------------------------------ */ 5066255332Scyint 5067255332Scyipf_nat_out(fin, nat, natadd, nflags) 5068255332Scy fr_info_t *fin; 5069255332Scy nat_t *nat; 5070255332Scy int natadd; 5071255332Scy u_32_t nflags; 5072145522Sdarrenr{ 5073255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 5074255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 5075145522Sdarrenr icmphdr_t *icmp; 5076145522Sdarrenr tcphdr_t *tcp; 5077145522Sdarrenr ipnat_t *np; 5078255332Scy int skip; 5079145522Sdarrenr int i; 5080145522Sdarrenr 5081145522Sdarrenr tcp = NULL; 5082145522Sdarrenr icmp = NULL; 5083145522Sdarrenr np = nat->nat_ptr; 5084145522Sdarrenr 5085145522Sdarrenr if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL)) 5086255332Scy (void) ipf_frag_natnew(softc, fin, 0, nat); 5087145522Sdarrenr 508872006Sdarrenr /* 5089145522Sdarrenr * Fix up checksums, not by recalculating them, but 5090145522Sdarrenr * simply computing adjustments. 5091145522Sdarrenr * This is only done for STREAMS based IP implementations where the 5092145522Sdarrenr * checksum has already been calculated by IP. In all other cases, 5093145522Sdarrenr * IPFilter is called before the checksum needs calculating so there 5094145522Sdarrenr * is no call to modify whatever is in the header now. 509572006Sdarrenr */ 5096255332Scy if (nflags == IPN_ICMPERR) { 5097255332Scy u_32_t s1, s2, sumd, msumd; 509863523Sdarrenr 5099255332Scy s1 = LONG_SUM(ntohl(fin->fin_saddr)); 5100255332Scy if (nat->nat_dir == NAT_OUTBOUND) { 5101255332Scy s2 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); 5102255332Scy } else { 5103255332Scy s2 = LONG_SUM(ntohl(nat->nat_odstaddr)); 510463523Sdarrenr } 5105255332Scy CALC_SUMD(s1, s2, sumd); 5106255332Scy msumd = sumd; 5107255332Scy 5108255332Scy s1 = LONG_SUM(ntohl(fin->fin_daddr)); 5109255332Scy if (nat->nat_dir == NAT_OUTBOUND) { 5110255332Scy s2 = LONG_SUM(ntohl(nat->nat_ndstaddr)); 5111255332Scy } else { 5112255332Scy s2 = LONG_SUM(ntohl(nat->nat_osrcaddr)); 5113255332Scy } 5114255332Scy CALC_SUMD(s1, s2, sumd); 5115255332Scy msumd += sumd; 5116255332Scy 5117255332Scy ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, msumd, 0); 5118255332Scy } 5119369272Scy#if !defined(_KERNEL) || SOLARIS || \ 5120366531Scy defined(BRIDGE_IPF) || defined(__FreeBSD__) 5121255332Scy else { 5122255332Scy /* 5123255332Scy * Strictly speaking, this isn't necessary on BSD 5124255332Scy * kernels because they do checksum calculation after 5125255332Scy * this code has run BUT if ipfilter is being used 5126255332Scy * to do NAT as a bridge, that code doesn't exist. 5127255332Scy */ 5128255332Scy switch (nat->nat_dir) 5129255332Scy { 5130255332Scy case NAT_OUTBOUND : 5131255332Scy ipf_fix_outcksum(fin->fin_cksum & FI_CK_L4PART, 5132255332Scy &fin->fin_ip->ip_sum, 5133255332Scy nat->nat_ipsumd, 0); 5134255332Scy break; 5135255332Scy 5136255332Scy case NAT_INBOUND : 5137255332Scy ipf_fix_incksum(fin->fin_cksum & FI_CK_L4PART, 5138255332Scy &fin->fin_ip->ip_sum, 5139255332Scy nat->nat_ipsumd, 0); 5140255332Scy break; 5141255332Scy 5142255332Scy default : 5143255332Scy break; 514463523Sdarrenr } 5145255332Scy } 514653642Sguido#endif 5147255332Scy 5148255332Scy /* 5149255332Scy * Address assignment is after the checksum modification because 5150255332Scy * we are using the address in the packet for determining the 5151255332Scy * correct checksum offset (the ICMP error could be coming from 5152255332Scy * anyone...) 5153255332Scy */ 5154255332Scy switch (nat->nat_dir) 5155255332Scy { 5156255332Scy case NAT_OUTBOUND : 5157255332Scy fin->fin_ip->ip_src = nat->nat_nsrcip; 5158255332Scy fin->fin_saddr = nat->nat_nsrcaddr; 5159255332Scy fin->fin_ip->ip_dst = nat->nat_ndstip; 5160255332Scy fin->fin_daddr = nat->nat_ndstaddr; 5161255332Scy break; 5162255332Scy 5163255332Scy case NAT_INBOUND : 5164255332Scy fin->fin_ip->ip_src = nat->nat_odstip; 5165255332Scy fin->fin_saddr = nat->nat_ndstaddr; 5166255332Scy fin->fin_ip->ip_dst = nat->nat_osrcip; 5167255332Scy fin->fin_daddr = nat->nat_nsrcaddr; 5168255332Scy break; 5169255332Scy 5170255332Scy case NAT_DIVERTIN : 5171255332Scy { 5172255332Scy mb_t *m; 5173255332Scy 5174255332Scy skip = ipf_nat_decap(fin, nat); 5175255332Scy if (skip <= 0) { 5176255332Scy NBUMPSIDED(1, ns_decap_fail); 5177255332Scy return -1; 5178255332Scy } 5179255332Scy 5180255332Scy m = fin->fin_m; 5181255332Scy 5182369272Scy#if SOLARIS && defined(_KERNEL) 5183255332Scy m->b_rptr += skip; 5184255332Scy#else 5185255332Scy m->m_data += skip; 5186255332Scy m->m_len -= skip; 5187255332Scy 5188255332Scy# ifdef M_PKTHDR 5189255332Scy if (m->m_flags & M_PKTHDR) 5190255332Scy m->m_pkthdr.len -= skip; 5191255332Scy# endif 5192255332Scy#endif 5193255332Scy 5194255332Scy MUTEX_ENTER(&nat->nat_lock); 5195255332Scy ipf_nat_update(fin, nat); 5196255332Scy MUTEX_EXIT(&nat->nat_lock); 5197255332Scy fin->fin_flx |= FI_NATED; 5198255332Scy if (np != NULL && np->in_tag.ipt_num[0] != 0) 5199255332Scy fin->fin_nattag = &np->in_tag; 5200255332Scy return 1; 5201255332Scy /* NOTREACHED */ 5202255332Scy } 5203255332Scy 5204255332Scy case NAT_DIVERTOUT : 5205255332Scy { 5206255332Scy u_32_t s1, s2, sumd; 5207255332Scy udphdr_t *uh; 5208255332Scy ip_t *ip; 5209255332Scy mb_t *m; 5210255332Scy 5211255332Scy m = M_DUP(np->in_divmp); 5212255332Scy if (m == NULL) { 5213255332Scy NBUMPSIDED(1, ns_divert_dup); 5214255332Scy return -1; 5215255332Scy } 5216255332Scy 5217255332Scy ip = MTOD(m, ip_t *); 5218280971Sglebius ip_fillid(ip); 5219255332Scy s2 = ntohs(ip->ip_id); 5220255332Scy 5221255332Scy s1 = ip->ip_len; 5222255332Scy ip->ip_len = ntohs(ip->ip_len); 5223255332Scy ip->ip_len += fin->fin_plen; 5224255332Scy ip->ip_len = htons(ip->ip_len); 5225255332Scy s2 += ntohs(ip->ip_len); 5226255332Scy CALC_SUMD(s1, s2, sumd); 5227255332Scy 5228255332Scy uh = (udphdr_t *)(ip + 1); 5229255332Scy uh->uh_ulen += fin->fin_plen; 5230255332Scy uh->uh_ulen = htons(uh->uh_ulen); 5231369272Scy#if !defined(_KERNEL) || SOLARIS || \ 5232344833Scy defined(BRIDGE_IPF) || defined(__FreeBSD__) 5233255332Scy ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0); 5234255332Scy#endif 5235255332Scy 5236255332Scy PREP_MB_T(fin, m); 5237255332Scy 5238255332Scy fin->fin_src = ip->ip_src; 5239255332Scy fin->fin_dst = ip->ip_dst; 5240255332Scy fin->fin_ip = ip; 5241255332Scy fin->fin_plen += sizeof(ip_t) + 8; /* UDP + IPv4 hdr */ 5242255332Scy fin->fin_dlen += sizeof(ip_t) + 8; /* UDP + IPv4 hdr */ 5243255332Scy 5244255332Scy nflags &= ~IPN_TCPUDPICMP; 5245255332Scy 5246255332Scy break; 5247255332Scy } 5248255332Scy 5249255332Scy default : 5250255332Scy break; 5251145522Sdarrenr } 525253642Sguido 5253145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 5254255332Scy u_short *csump; 5255255332Scy 5256255332Scy if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) { 5257145522Sdarrenr tcp = fin->fin_dp; 525853642Sguido 5259255332Scy switch (nat->nat_dir) 5260255332Scy { 5261255332Scy case NAT_OUTBOUND : 5262255332Scy tcp->th_sport = nat->nat_nsport; 5263255332Scy fin->fin_data[0] = ntohs(nat->nat_nsport); 5264255332Scy tcp->th_dport = nat->nat_ndport; 5265255332Scy fin->fin_data[1] = ntohs(nat->nat_ndport); 5266255332Scy break; 5267255332Scy 5268255332Scy case NAT_INBOUND : 5269255332Scy tcp->th_sport = nat->nat_odport; 5270255332Scy fin->fin_data[0] = ntohs(nat->nat_odport); 5271255332Scy tcp->th_dport = nat->nat_osport; 5272255332Scy fin->fin_data[1] = ntohs(nat->nat_osport); 5273255332Scy break; 5274255332Scy } 5275145522Sdarrenr } 527653642Sguido 5277255332Scy if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) { 5278145522Sdarrenr icmp = fin->fin_dp; 5279255332Scy icmp->icmp_id = nat->nat_nicmpid; 5280145522Sdarrenr } 5281110916Sdarrenr 5282255332Scy csump = ipf_nat_proto(fin, nat, nflags); 5283255332Scy 5284255332Scy /* 5285255332Scy * The above comments do not hold for layer 4 (or higher) 5286255332Scy * checksums... 5287255332Scy */ 5288255332Scy if (csump != NULL) { 5289255332Scy if (nat->nat_dir == NAT_OUTBOUND) 5290255332Scy ipf_fix_outcksum(fin->fin_cksum, csump, 5291255332Scy nat->nat_sumd[0], 5292255332Scy nat->nat_sumd[1] + 5293255332Scy fin->fin_dlen); 5294255332Scy else 5295255332Scy ipf_fix_incksum(fin->fin_cksum, csump, 5296255332Scy nat->nat_sumd[0], 5297255332Scy nat->nat_sumd[1] + 5298255332Scy fin->fin_dlen); 5299255332Scy } 5300145522Sdarrenr } 5301110916Sdarrenr 5302255332Scy ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); 5303145522Sdarrenr /* ------------------------------------------------------------- */ 5304255332Scy /* A few quick notes: */ 5305255332Scy /* Following are test conditions prior to calling the */ 5306255332Scy /* ipf_proxy_check routine. */ 5307255332Scy /* */ 5308255332Scy /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 5309255332Scy /* with a redirect rule, we attempt to match the packet's */ 5310255332Scy /* source port against in_dport, otherwise we'd compare the */ 5311255332Scy /* packet's destination. */ 5312145522Sdarrenr /* ------------------------------------------------------------- */ 5313145522Sdarrenr if ((np != NULL) && (np->in_apr != NULL)) { 5314255332Scy i = ipf_proxy_check(fin, nat); 5315369541Sgit2svn if (i == -1) { 5316255332Scy NBUMPSIDED(1, ns_ipf_proxy_fail); 5317255332Scy } 5318255332Scy } else { 5319145522Sdarrenr i = 1; 5320255332Scy } 5321145522Sdarrenr fin->fin_flx |= FI_NATED; 5322145522Sdarrenr return i; 532353642Sguido} 532453642Sguido 532553642Sguido 5326145522Sdarrenr/* ------------------------------------------------------------------------ */ 5327255332Scy/* Function: ipf_nat_checkin */ 5328145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 5329145522Sdarrenr/* 0 == no packet translation occurred, */ 5330145522Sdarrenr/* 1 == packet was successfully translated. */ 5331145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 5332145522Sdarrenr/* passp(I) - pointer to filtering result flags */ 5333145522Sdarrenr/* */ 5334145522Sdarrenr/* Check to see if an incoming packet should be changed. ICMP packets are */ 5335145522Sdarrenr/* first checked to see if they match an existing entry (if an error), */ 5336145522Sdarrenr/* otherwise a search of the current NAT table is made. If neither results */ 5337145522Sdarrenr/* in a match then a search for a matching NAT rule is made. Create a new */ 5338145522Sdarrenr/* NAT entry if a we matched a NAT rule. Lastly, actually change the */ 5339145522Sdarrenr/* packet header(s) as required. */ 5340145522Sdarrenr/* ------------------------------------------------------------------------ */ 5341255332Scyint 5342255332Scyipf_nat_checkin(fin, passp) 5343255332Scy fr_info_t *fin; 5344255332Scy u_32_t *passp; 534553642Sguido{ 5346255332Scy ipf_main_softc_t *softc; 5347255332Scy ipf_nat_softc_t *softn; 5348145522Sdarrenr u_int nflags, natadd; 5349255332Scy ipnat_t *np, *npnext; 5350145522Sdarrenr int rval, natfailed; 5351145522Sdarrenr struct ifnet *ifp; 5352145522Sdarrenr struct in_addr in; 5353145522Sdarrenr icmphdr_t *icmp; 5354145522Sdarrenr tcphdr_t *tcp; 5355145522Sdarrenr u_short dport; 535653642Sguido nat_t *nat; 535753642Sguido u_32_t iph; 535853642Sguido 5359255332Scy softc = fin->fin_main_soft; 5360255332Scy softn = softc->ipf_nat_soft; 5361255332Scy 5362255332Scy if (softn->ipf_nat_lock != 0) 536353642Sguido return 0; 5364255332Scy if (softn->ipf_nat_stats.ns_rules == 0 && 5365255332Scy softn->ipf_nat_instances == NULL) 5366255332Scy return 0; 536753642Sguido 5368145522Sdarrenr tcp = NULL; 5369145522Sdarrenr icmp = NULL; 5370145522Sdarrenr dport = 0; 5371145522Sdarrenr natadd = 1; 5372145522Sdarrenr nflags = 0; 5373145522Sdarrenr natfailed = 0; 5374145522Sdarrenr ifp = fin->fin_ifp; 5375145522Sdarrenr 5376145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 5377145522Sdarrenr switch (fin->fin_p) 5378145522Sdarrenr { 5379145522Sdarrenr case IPPROTO_TCP : 538053642Sguido nflags = IPN_TCP; 5381145522Sdarrenr break; 5382145522Sdarrenr case IPPROTO_UDP : 538353642Sguido nflags = IPN_UDP; 5384145522Sdarrenr break; 5385145522Sdarrenr case IPPROTO_ICMP : 5386145522Sdarrenr icmp = fin->fin_dp; 5387145522Sdarrenr 5388145522Sdarrenr /* 5389145522Sdarrenr * This is an incoming packet, so the destination is 5390145522Sdarrenr * the icmp_id and the source port equals 0 5391145522Sdarrenr */ 5392255332Scy if ((fin->fin_flx & FI_ICMPQUERY) != 0) { 5393145522Sdarrenr nflags = IPN_ICMPQUERY; 5394255332Scy dport = icmp->icmp_id; 5395145522Sdarrenr } break; 5396145522Sdarrenr default : 5397145522Sdarrenr break; 5398145522Sdarrenr } 5399255332Scy 540053642Sguido if ((nflags & IPN_TCPUDP)) { 5401145522Sdarrenr tcp = fin->fin_dp; 5402255332Scy dport = fin->fin_data[1]; 540353642Sguido } 540453642Sguido } 540553642Sguido 540692685Sdarrenr in = fin->fin_dst; 540753642Sguido 5408255332Scy READ_ENTER(&softc->ipf_nat); 540953642Sguido 5410255332Scy if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && 5411255332Scy (nat = ipf_nat_icmperror(fin, &nflags, NAT_INBOUND))) 5412145522Sdarrenr /*EMPTY*/; 5413255332Scy else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) 541453642Sguido natadd = 0; 5415255332Scy else if ((nat = ipf_nat_inlookup(fin, nflags|NAT_SEARCH, 5416255332Scy (u_int)fin->fin_p, 5417255332Scy fin->fin_src, in))) { 541853642Sguido nflags = nat->nat_flags; 5419255332Scy } else if (fin->fin_off == 0) { 5420255332Scy u_32_t hv, msk, rmsk = 0; 5421145522Sdarrenr 542253642Sguido /* 542353642Sguido * If there is no current entry in the nat table for this IP#, 542453642Sguido * create one for it (if there is a matching rule). 542553642Sguido */ 542653642Sguidomaskloop: 5427255332Scy msk = softn->ipf_nat_rdr_active_masks[rmsk]; 5428255332Scy iph = in.s_addr & msk; 5429255332Scy hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_rdrrules_sz); 5430255332Scyretry_roundrobin: 5431255332Scy /* TRACE (iph,msk,rmsk,hv,softn->ipf_nat_rdrrules_sz) */ 5432255332Scy for (np = softn->ipf_nat_rdr_rules[hv]; np; np = npnext) { 5433255332Scy npnext = np->in_rnext; 5434145522Sdarrenr if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) 543560852Sdarrenr continue; 5436255332Scy if (np->in_v[0] != 4) 5437138947Sdarrenr continue; 5438255332Scy if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p)) 5439145522Sdarrenr continue; 5440145522Sdarrenr if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) 5441145522Sdarrenr continue; 544260852Sdarrenr if (np->in_flags & IPN_FILTER) { 5443255332Scy switch (ipf_nat_match(fin, np)) 5444255332Scy { 5445255332Scy case 0 : 544660852Sdarrenr continue; 5447255332Scy case -1 : 5448337948Scy rval = -3; 5449255332Scy goto inmatchfail; 5450255332Scy case 1 : 5451255332Scy default : 5452255332Scy break; 5453255332Scy } 5454145522Sdarrenr } else { 5455255332Scy if ((in.s_addr & np->in_odstmsk) != 5456255332Scy np->in_odstaddr) 5457145522Sdarrenr continue; 5458255332Scy if (np->in_odport && 5459255332Scy ((np->in_dtop < dport) || 5460255332Scy (dport < np->in_odport))) 5461145522Sdarrenr continue; 5462145522Sdarrenr } 5463145522Sdarrenr 5464255332Scy if (np->in_plabel != -1) { 5465255332Scy if (!ipf_proxy_ok(fin, tcp, np)) { 5466145522Sdarrenr continue; 546753642Sguido } 5468145522Sdarrenr } 5469145522Sdarrenr 5470255332Scy if (np->in_flags & IPN_NO) { 5471145522Sdarrenr np->in_hits++; 5472145522Sdarrenr break; 5473255332Scy } 547460852Sdarrenr 5475255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 5476255332Scy /* 5477255332Scy * If we've matched a round-robin rule but it has 5478255332Scy * moved in the list since we got it, start over as 5479255332Scy * this is now no longer correct. 5480255332Scy */ 5481255332Scy if (npnext != np->in_rnext) { 5482255332Scy if ((np->in_flags & IPN_ROUNDR) != 0) { 5483255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 5484255332Scy goto retry_roundrobin; 5485255332Scy } 5486255332Scy npnext = np->in_rnext; 5487145522Sdarrenr } 5488255332Scy 5489255332Scy nat = ipf_nat_add(fin, np, NULL, nflags, NAT_INBOUND); 5490255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 5491255332Scy if (nat != NULL) { 5492255332Scy natfailed = 0; 5493255332Scy break; 5494145522Sdarrenr } 5495337948Scy natfailed = -2; 549653642Sguido } 5497255332Scy if ((np == NULL) && (rmsk < softn->ipf_nat_rdr_max)) { 5498255332Scy rmsk++; 5499255332Scy goto maskloop; 5500255332Scy } 550153642Sguido } 5502255332Scy 5503145522Sdarrenr if (nat != NULL) { 5504255332Scy rval = ipf_nat_in(fin, nat, natadd, nflags); 5505145522Sdarrenr if (rval == 1) { 5506145522Sdarrenr MUTEX_ENTER(&nat->nat_lock); 5507255332Scy ipf_nat_update(fin, nat); 5508255332Scy nat->nat_bytes[0] += fin->fin_plen; 5509255332Scy nat->nat_pkts[0]++; 5510255332Scy fin->fin_pktnum = nat->nat_pkts[0]; 5511145522Sdarrenr MUTEX_EXIT(&nat->nat_lock); 5512145522Sdarrenr } 5513145522Sdarrenr } else 5514145522Sdarrenr rval = natfailed; 5515255332Scyinmatchfail: 5516255332Scy RWLOCK_EXIT(&softc->ipf_nat); 551772006Sdarrenr 5518255332Scy switch (rval) 5519255332Scy { 5520337948Scy case -3 : 5521337948Scy /* ipf_nat_match() failure */ 5522337948Scy /* FALLTHROUGH */ 5523337948Scy case -2 : 5524337948Scy /* retry_roundrobin loop failure */ 5525337948Scy /* FALLTHROUGH */ 5526255332Scy case -1 : 5527337948Scy /* proxy failure detected by ipf_nat_out() */ 5528255332Scy if (passp != NULL) { 5529337948Scy DT2(frb_natv4in, fr_info_t *, fin, int, rval); 5530255332Scy NBUMPSIDED(0, ns_drop); 5531145522Sdarrenr *passp = FR_BLOCK; 5532255332Scy fin->fin_reason = FRB_NATV4; 5533255332Scy } 5534145522Sdarrenr fin->fin_flx |= FI_BADNAT; 5535255332Scy NBUMPSIDED(0, ns_badnat); 5536337948Scy rval = -1; /* We only return -1 on error. */ 5537255332Scy break; 5538255332Scy case 0 : 5539255332Scy NBUMPSIDE(0, ns_ignored); 5540255332Scy break; 5541255332Scy case 1 : 5542255332Scy NBUMPSIDE(0, ns_translated); 5543255332Scy break; 5544145522Sdarrenr } 5545145522Sdarrenr return rval; 5546145522Sdarrenr} 5547145522Sdarrenr 5548145522Sdarrenr 5549145522Sdarrenr/* ------------------------------------------------------------------------ */ 5550255332Scy/* Function: ipf_nat_in */ 5551145522Sdarrenr/* Returns: int - -1 == packet failed NAT checks so block it, */ 5552145522Sdarrenr/* 1 == packet was successfully translated. */ 5553145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 5554145522Sdarrenr/* nat(I) - pointer to NAT structure */ 5555145522Sdarrenr/* natadd(I) - flag indicating if it is safe to add frag cache */ 5556145522Sdarrenr/* nflags(I) - NAT flags set for this packet */ 5557255332Scy/* Locks Held: ipf_nat(READ) */ 5558145522Sdarrenr/* */ 5559145522Sdarrenr/* Translate a packet coming "in" on an interface. */ 5560145522Sdarrenr/* ------------------------------------------------------------------------ */ 5561255332Scyint 5562255332Scyipf_nat_in(fin, nat, natadd, nflags) 5563255332Scy fr_info_t *fin; 5564255332Scy nat_t *nat; 5565255332Scy int natadd; 5566255332Scy u_32_t nflags; 5567145522Sdarrenr{ 5568255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 5569255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 5570255332Scy u_32_t sumd, ipsumd, sum1, sum2; 5571145522Sdarrenr icmphdr_t *icmp; 5572145522Sdarrenr tcphdr_t *tcp; 5573145522Sdarrenr ipnat_t *np; 5574255332Scy int skip; 5575145522Sdarrenr int i; 5576145522Sdarrenr 5577145522Sdarrenr tcp = NULL; 5578145522Sdarrenr np = nat->nat_ptr; 5579145522Sdarrenr fin->fin_fr = nat->nat_fr; 5580145522Sdarrenr 5581145522Sdarrenr if (np != NULL) { 5582145522Sdarrenr if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) 5583255332Scy (void) ipf_frag_natnew(softc, fin, 0, nat); 5584145522Sdarrenr 5585145522Sdarrenr /* ------------------------------------------------------------- */ 5586255332Scy /* A few quick notes: */ 5587255332Scy /* Following are test conditions prior to calling the */ 5588255332Scy /* ipf_proxy_check routine. */ 5589255332Scy /* */ 5590255332Scy /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ 5591255332Scy /* with a map rule, we attempt to match the packet's */ 5592255332Scy /* source port against in_dport, otherwise we'd compare the */ 5593255332Scy /* packet's destination. */ 5594145522Sdarrenr /* ------------------------------------------------------------- */ 5595145522Sdarrenr if (np->in_apr != NULL) { 5596255332Scy i = ipf_proxy_check(fin, nat); 559760852Sdarrenr if (i == -1) { 5598255332Scy NBUMPSIDED(0, ns_ipf_proxy_fail); 5599145522Sdarrenr return -1; 560060852Sdarrenr } 560160852Sdarrenr } 5602145522Sdarrenr } 560353642Sguido 5604255332Scy ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); 5605145522Sdarrenr 5606255332Scy ipsumd = nat->nat_ipsumd; 5607145522Sdarrenr /* 5608145522Sdarrenr * Fix up checksums, not by recalculating them, but 5609145522Sdarrenr * simply computing adjustments. 5610145522Sdarrenr * Why only do this for some platforms on inbound packets ? 5611145522Sdarrenr * Because for those that it is done, IP processing is yet to happen 5612145522Sdarrenr * and so the IPv4 header checksum has not yet been evaluated. 5613145522Sdarrenr * Perhaps it should always be done for the benefit of things like 5614145522Sdarrenr * fast forwarding (so that it doesn't need to be recomputed) but with 5615145522Sdarrenr * header checksum offloading, perhaps it is a moot point. 5616145522Sdarrenr */ 5617255332Scy 5618255332Scy switch (nat->nat_dir) 5619255332Scy { 5620255332Scy case NAT_INBOUND : 5621255332Scy if ((fin->fin_flx & FI_ICMPERR) == 0) { 5622255332Scy fin->fin_ip->ip_src = nat->nat_nsrcip; 5623255332Scy fin->fin_saddr = nat->nat_nsrcaddr; 5624255332Scy } else { 5625255332Scy sum1 = nat->nat_osrcaddr; 5626255332Scy sum2 = nat->nat_nsrcaddr; 5627255332Scy CALC_SUMD(sum1, sum2, sumd); 5628255332Scy ipsumd -= sumd; 5629255332Scy } 5630255332Scy fin->fin_ip->ip_dst = nat->nat_ndstip; 5631255332Scy fin->fin_daddr = nat->nat_ndstaddr; 5632369272Scy#if !defined(_KERNEL) || SOLARIS 5633255332Scy ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, ipsumd, 0); 5634145522Sdarrenr#endif 5635255332Scy break; 5636145522Sdarrenr 5637255332Scy case NAT_OUTBOUND : 5638255332Scy if ((fin->fin_flx & FI_ICMPERR) == 0) { 5639255332Scy fin->fin_ip->ip_src = nat->nat_odstip; 5640255332Scy fin->fin_saddr = nat->nat_odstaddr; 5641255332Scy } else { 5642255332Scy sum1 = nat->nat_odstaddr; 5643255332Scy sum2 = nat->nat_ndstaddr; 5644255332Scy CALC_SUMD(sum1, sum2, sumd); 5645255332Scy ipsumd -= sumd; 5646255332Scy } 5647255332Scy fin->fin_ip->ip_dst = nat->nat_osrcip; 5648255332Scy fin->fin_daddr = nat->nat_osrcaddr; 5649369272Scy#if !defined(_KERNEL) || SOLARIS 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 *); 5667280971Sglebius ip_fillid(ip); 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 5681369272Scy#if !defined(_KERNEL) || SOLARIS 5682255332Scy ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0); 5683255332Scy#endif 5684255332Scy PREP_MB_T(fin, m); 5685255332Scy 5686255332Scy fin->fin_ip = ip; 5687255332Scy fin->fin_plen += sizeof(ip_t) + 8; /* UDP + new IPv4 hdr */ 5688255332Scy fin->fin_dlen += sizeof(ip_t) + 8; /* UDP + old IPv4 hdr */ 5689255332Scy 5690255332Scy nflags &= ~IPN_TCPUDPICMP; 5691255332Scy 5692255332Scy break; 5693255332Scy } 5694255332Scy 5695255332Scy case NAT_DIVERTOUT : 5696255332Scy { 5697255332Scy mb_t *m; 5698255332Scy 5699255332Scy skip = ipf_nat_decap(fin, nat); 5700255332Scy if (skip <= 0) { 5701255332Scy NBUMPSIDED(0, ns_decap_fail); 5702255332Scy return -1; 5703255332Scy } 5704255332Scy 5705255332Scy m = fin->fin_m; 5706255332Scy 5707369272Scy#if SOLARIS && defined(_KERNEL) 5708255332Scy m->b_rptr += skip; 5709255332Scy#else 5710255332Scy m->m_data += skip; 5711255332Scy m->m_len -= skip; 5712255332Scy 5713255332Scy# ifdef M_PKTHDR 5714255332Scy if (m->m_flags & M_PKTHDR) 5715255332Scy m->m_pkthdr.len -= skip; 5716255332Scy# endif 5717255332Scy#endif 5718255332Scy 5719255332Scy ipf_nat_update(fin, nat); 5720255332Scy nflags &= ~IPN_TCPUDPICMP; 5721255332Scy fin->fin_flx |= FI_NATED; 5722255332Scy if (np != NULL && np->in_tag.ipt_num[0] != 0) 5723255332Scy fin->fin_nattag = &np->in_tag; 5724255332Scy return 1; 5725255332Scy /* NOTREACHED */ 5726255332Scy } 5727255332Scy } 5728255332Scy if (nflags & IPN_TCPUDP) 5729255332Scy tcp = fin->fin_dp; 5730255332Scy 5731145522Sdarrenr if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { 5732255332Scy u_short *csump; 5733255332Scy 5734255332Scy if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) { 5735255332Scy switch (nat->nat_dir) 5736255332Scy { 5737255332Scy case NAT_INBOUND : 5738255332Scy tcp->th_sport = nat->nat_nsport; 5739255332Scy fin->fin_data[0] = ntohs(nat->nat_nsport); 5740255332Scy tcp->th_dport = nat->nat_ndport; 5741255332Scy fin->fin_data[1] = ntohs(nat->nat_ndport); 5742255332Scy break; 5743255332Scy 5744255332Scy case NAT_OUTBOUND : 5745255332Scy tcp->th_sport = nat->nat_odport; 5746255332Scy fin->fin_data[0] = ntohs(nat->nat_odport); 5747255332Scy tcp->th_dport = nat->nat_osport; 5748255332Scy fin->fin_data[1] = ntohs(nat->nat_osport); 5749255332Scy break; 5750255332Scy } 575192685Sdarrenr } 575253642Sguido 5753145522Sdarrenr 5754255332Scy if ((nat->nat_odport != 0) && (nflags & IPN_ICMPQUERY)) { 5755145522Sdarrenr icmp = fin->fin_dp; 5756145522Sdarrenr 5757255332Scy icmp->icmp_id = nat->nat_nicmpid; 5758145522Sdarrenr } 5759145522Sdarrenr 5760255332Scy csump = ipf_nat_proto(fin, nat, nflags); 5761255332Scy 5762255332Scy /* 5763255332Scy * The above comments do not hold for layer 4 (or higher) 5764255332Scy * checksums... 5765255332Scy */ 5766255332Scy if (csump != NULL) { 5767255332Scy if (nat->nat_dir == NAT_OUTBOUND) 5768255332Scy ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0); 5769255332Scy else 5770255332Scy ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0); 5771255332Scy } 5772145522Sdarrenr } 5773145522Sdarrenr 5774145522Sdarrenr fin->fin_flx |= FI_NATED; 5775145522Sdarrenr if (np != NULL && np->in_tag.ipt_num[0] != 0) 5776145522Sdarrenr fin->fin_nattag = &np->in_tag; 5777145522Sdarrenr return 1; 5778145522Sdarrenr} 5779130886Sdarrenr 5780130886Sdarrenr 5781145522Sdarrenr/* ------------------------------------------------------------------------ */ 5782255332Scy/* Function: ipf_nat_proto */ 5783145522Sdarrenr/* Returns: u_short* - pointer to transport header checksum to update, */ 5784145522Sdarrenr/* NULL if the transport protocol is not recognised */ 5785145522Sdarrenr/* as needing a checksum update. */ 5786145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 5787145522Sdarrenr/* nat(I) - pointer to NAT structure */ 5788145522Sdarrenr/* nflags(I) - NAT flags set for this packet */ 5789145522Sdarrenr/* */ 5790145522Sdarrenr/* Return the pointer to the checksum field for each protocol so understood.*/ 5791145522Sdarrenr/* If support for making other changes to a protocol header is required, */ 5792145522Sdarrenr/* that is not strictly 'address' translation, such as clamping the MSS in */ 5793145522Sdarrenr/* TCP down to a specific value, then do it from here. */ 5794145522Sdarrenr/* ------------------------------------------------------------------------ */ 5795255332Scyu_short * 5796255332Scyipf_nat_proto(fin, nat, nflags) 5797255332Scy fr_info_t *fin; 5798255332Scy nat_t *nat; 5799255332Scy u_int nflags; 5800145522Sdarrenr{ 5801145522Sdarrenr icmphdr_t *icmp; 5802145522Sdarrenr u_short *csump; 5803145522Sdarrenr tcphdr_t *tcp; 5804145522Sdarrenr udphdr_t *udp; 580553642Sguido 5806145522Sdarrenr csump = NULL; 5807145522Sdarrenr if (fin->fin_out == 0) { 5808255332Scy fin->fin_rev = (nat->nat_dir & NAT_OUTBOUND); 5809145522Sdarrenr } else { 5810255332Scy fin->fin_rev = ((nat->nat_dir & NAT_OUTBOUND) == 0); 5811145522Sdarrenr } 581253642Sguido 5813145522Sdarrenr switch (fin->fin_p) 5814145522Sdarrenr { 5815145522Sdarrenr case IPPROTO_TCP : 5816145522Sdarrenr tcp = fin->fin_dp; 5817110916Sdarrenr 5818255332Scy if ((nflags & IPN_TCP) != 0) 5819255332Scy csump = &tcp->th_sum; 582053642Sguido 5821145522Sdarrenr /* 5822145522Sdarrenr * Do a MSS CLAMPING on a SYN packet, 5823145522Sdarrenr * only deal IPv4 for now. 5824145522Sdarrenr */ 5825145522Sdarrenr if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0) 5826255332Scy ipf_nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump); 582760852Sdarrenr 5828145522Sdarrenr break; 5829145522Sdarrenr 5830145522Sdarrenr case IPPROTO_UDP : 5831145522Sdarrenr udp = fin->fin_dp; 5832145522Sdarrenr 5833255332Scy if ((nflags & IPN_UDP) != 0) { 5834255332Scy if (udp->uh_sum != 0) 5835255332Scy csump = &udp->uh_sum; 5836255332Scy } 5837145522Sdarrenr break; 5838145522Sdarrenr 5839145522Sdarrenr case IPPROTO_ICMP : 5840145522Sdarrenr icmp = fin->fin_dp; 5841145522Sdarrenr 5842145522Sdarrenr if ((nflags & IPN_ICMPQUERY) != 0) { 5843145522Sdarrenr if (icmp->icmp_cksum != 0) 5844145522Sdarrenr csump = &icmp->icmp_cksum; 584553642Sguido } 5846145522Sdarrenr break; 584753642Sguido 5848255332Scy#ifdef USE_INET6 5849255332Scy case IPPROTO_ICMPV6 : 5850255332Scy { 5851255332Scy struct icmp6_hdr *icmp6 = (struct icmp6_hdr *)fin->fin_dp; 585253642Sguido 5853255332Scy icmp6 = fin->fin_dp; 5854145522Sdarrenr 5855255332Scy if ((nflags & IPN_ICMPQUERY) != 0) { 5856255332Scy if (icmp6->icmp6_cksum != 0) 5857255332Scy csump = &icmp6->icmp6_cksum; 5858255332Scy } 5859255332Scy break; 5860255332Scy } 5861255332Scy#endif 5862145522Sdarrenr } 5863255332Scy return csump; 586453642Sguido} 586553642Sguido 586653642Sguido 5867145522Sdarrenr/* ------------------------------------------------------------------------ */ 5868255332Scy/* Function: ipf_nat_expire */ 5869145522Sdarrenr/* Returns: Nil */ 5870255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 5871145522Sdarrenr/* */ 5872145522Sdarrenr/* Check all of the timeout queues for entries at the top which need to be */ 5873145522Sdarrenr/* expired. */ 5874145522Sdarrenr/* ------------------------------------------------------------------------ */ 5875255332Scyvoid 5876255332Scyipf_nat_expire(softc) 5877255332Scy ipf_main_softc_t *softc; 587853642Sguido{ 5879255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 5880145522Sdarrenr ipftq_t *ifq, *ifqnext; 5881145522Sdarrenr ipftqent_t *tqe, *tqn; 5882161356Sguido int i; 5883153876Sguido SPL_INT(s); 588453642Sguido 588553642Sguido SPL_NET(s); 5886255332Scy WRITE_ENTER(&softc->ipf_nat); 5887255332Scy for (ifq = softn->ipf_nat_tcptq, i = 0; ifq != NULL; 5888255332Scy ifq = ifq->ifq_next) { 5889145522Sdarrenr for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { 5890255332Scy if (tqe->tqe_die > softc->ipf_ticks) 5891145522Sdarrenr break; 5892145522Sdarrenr tqn = tqe->tqe_next; 5893255332Scy ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE); 589453642Sguido } 589553642Sguido } 5896145522Sdarrenr 5897255332Scy for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifq->ifq_next) { 5898145522Sdarrenr for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { 5899255332Scy if (tqe->tqe_die > softc->ipf_ticks) 5900145522Sdarrenr break; 5901145522Sdarrenr tqn = tqe->tqe_next; 5902255332Scy ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE); 5903145522Sdarrenr } 5904145522Sdarrenr } 5905145522Sdarrenr 5906255332Scy for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) { 5907145522Sdarrenr ifqnext = ifq->ifq_next; 5908145522Sdarrenr 5909145522Sdarrenr if (((ifq->ifq_flags & IFQF_DELETE) != 0) && 5910145522Sdarrenr (ifq->ifq_ref == 0)) { 5911255332Scy ipf_freetimeoutqueue(softc, ifq); 5912145522Sdarrenr } 5913145522Sdarrenr } 5914145522Sdarrenr 5915255332Scy if (softn->ipf_nat_doflush != 0) { 5916255332Scy ipf_nat_extraflush(softc, softn, 2); 5917255332Scy softn->ipf_nat_doflush = 0; 5918170268Sdarrenr } 5919170268Sdarrenr 5920255332Scy RWLOCK_EXIT(&softc->ipf_nat); 592153642Sguido SPL_X(s); 592253642Sguido} 592353642Sguido 592453642Sguido 5925145522Sdarrenr/* ------------------------------------------------------------------------ */ 5926255332Scy/* Function: ipf_nat_sync */ 5927145522Sdarrenr/* Returns: Nil */ 5928255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 5929255332Scy/* ifp(I) - pointer to network interface */ 5930145522Sdarrenr/* */ 5931145522Sdarrenr/* Walk through all of the currently active NAT sessions, looking for those */ 5932145522Sdarrenr/* which need to have their translated address updated. */ 5933145522Sdarrenr/* ------------------------------------------------------------------------ */ 5934255332Scyvoid 5935255332Scyipf_nat_sync(softc, ifp) 5936255332Scy ipf_main_softc_t *softc; 5937255332Scy void *ifp; 593853642Sguido{ 5939255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 5940145522Sdarrenr u_32_t sum1, sum2, sumd; 5941255332Scy i6addr_t in; 5942145522Sdarrenr ipnat_t *n; 5943145522Sdarrenr nat_t *nat; 594453642Sguido void *ifp2; 5945255332Scy int idx; 5946153876Sguido SPL_INT(s); 594753642Sguido 5948255332Scy if (softc->ipf_running <= 0) 5949145522Sdarrenr return; 5950145522Sdarrenr 595153642Sguido /* 595253642Sguido * Change IP addresses for NAT sessions for any protocol except TCP 5953145522Sdarrenr * since it will break the TCP connection anyway. The only rules 5954145522Sdarrenr * which will get changed are those which are "map ... -> 0/32", 5955145522Sdarrenr * where the rule specifies the address is taken from the interface. 595653642Sguido */ 595753642Sguido SPL_NET(s); 5958255332Scy WRITE_ENTER(&softc->ipf_nat); 5959145522Sdarrenr 5960255332Scy if (softc->ipf_running <= 0) { 5961255332Scy RWLOCK_EXIT(&softc->ipf_nat); 5962145522Sdarrenr return; 5963145522Sdarrenr } 5964145522Sdarrenr 5965255332Scy for (nat = softn->ipf_nat_instances; nat; nat = nat->nat_next) { 5966145522Sdarrenr if ((nat->nat_flags & IPN_TCP) != 0) 5967145522Sdarrenr continue; 5968255332Scy 5969145522Sdarrenr n = nat->nat_ptr; 5970255332Scy if (n != NULL) { 5971255332Scy if (n->in_v[1] == 4) { 5972255332Scy if (n->in_redir & NAT_MAP) { 5973255332Scy if ((n->in_nsrcaddr != 0) || 5974255332Scy (n->in_nsrcmsk != 0xffffffff)) 5975255332Scy continue; 5976255332Scy } else if (n->in_redir & NAT_REDIRECT) { 5977255332Scy if ((n->in_ndstaddr != 0) || 5978255332Scy (n->in_ndstmsk != 0xffffffff)) 5979255332Scy continue; 5980255332Scy } 5981255332Scy } 5982255332Scy#ifdef USE_INET6 5983255332Scy if (n->in_v[1] == 4) { 5984255332Scy if (n->in_redir & NAT_MAP) { 5985255332Scy if (!IP6_ISZERO(&n->in_nsrcaddr) || 5986255332Scy !IP6_ISONES(&n->in_nsrcmsk)) 5987255332Scy continue; 5988255332Scy } else if (n->in_redir & NAT_REDIRECT) { 5989255332Scy if (!IP6_ISZERO(&n->in_ndstaddr) || 5990255332Scy !IP6_ISONES(&n->in_ndstmsk)) 5991255332Scy continue; 5992255332Scy } 5993255332Scy } 5994255332Scy#endif 5995255332Scy } 5996255332Scy 5997145522Sdarrenr if (((ifp == NULL) || (ifp == nat->nat_ifps[0]) || 5998145522Sdarrenr (ifp == nat->nat_ifps[1]))) { 5999255332Scy nat->nat_ifps[0] = GETIFP(nat->nat_ifnames[0], 6000255332Scy nat->nat_v[0]); 6001255332Scy if ((nat->nat_ifps[0] != NULL) && 6002255332Scy (nat->nat_ifps[0] != (void *)-1)) { 6003255332Scy nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); 6004255332Scy } 6005145522Sdarrenr if (nat->nat_ifnames[1][0] != '\0') { 6006145522Sdarrenr nat->nat_ifps[1] = GETIFP(nat->nat_ifnames[1], 6007255332Scy nat->nat_v[1]); 6008255332Scy } else { 6009145522Sdarrenr nat->nat_ifps[1] = nat->nat_ifps[0]; 6010255332Scy } 6011255332Scy if ((nat->nat_ifps[1] != NULL) && 6012255332Scy (nat->nat_ifps[1] != (void *)-1)) { 6013255332Scy nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); 6014255332Scy } 6015145522Sdarrenr ifp2 = nat->nat_ifps[0]; 6016145522Sdarrenr if (ifp2 == NULL) 6017145522Sdarrenr continue; 6018145522Sdarrenr 601953642Sguido /* 602053642Sguido * Change the map-to address to be the same as the 602153642Sguido * new one. 602253642Sguido */ 6023255332Scy sum1 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6); 6024255332Scy if (ipf_ifpaddr(softc, nat->nat_v[0], FRI_NORMAL, ifp2, 6025255332Scy &in, NULL) != -1) { 6026255332Scy if (nat->nat_v[0] == 4) 6027255332Scy nat->nat_nsrcip = in.in4; 6028255332Scy } 6029255332Scy sum2 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6); 603053642Sguido 603153642Sguido if (sum1 == sum2) 603253642Sguido continue; 603353642Sguido /* 603453642Sguido * Readjust the checksum adjustment to take into 603553642Sguido * account the new IP#. 603653642Sguido */ 603753642Sguido CALC_SUMD(sum1, sum2, sumd); 603855929Sguido /* XXX - dont change for TCP when solaris does 603955929Sguido * hardware checksumming. 604055929Sguido */ 604155929Sguido sumd += nat->nat_sumd[0]; 604255929Sguido nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 604355929Sguido nat->nat_sumd[1] = nat->nat_sumd[0]; 604453642Sguido } 6045145522Sdarrenr } 604653642Sguido 6047255332Scy for (n = softn->ipf_nat_list; (n != NULL); n = n->in_next) { 6048255332Scy char *base = n->in_names; 6049255332Scy 6050145522Sdarrenr if ((ifp == NULL) || (n->in_ifps[0] == ifp)) 6051255332Scy n->in_ifps[0] = ipf_resolvenic(softc, 6052255332Scy base + n->in_ifnames[0], 6053255332Scy n->in_v[0]); 6054145522Sdarrenr if ((ifp == NULL) || (n->in_ifps[1] == ifp)) 6055255332Scy n->in_ifps[1] = ipf_resolvenic(softc, 6056255332Scy base + n->in_ifnames[1], 6057255332Scy n->in_v[1]); 6058255332Scy 6059255332Scy if (n->in_redir & NAT_REDIRECT) 6060255332Scy idx = 1; 6061255332Scy else 6062255332Scy idx = 0; 6063255332Scy 6064255332Scy if (((ifp == NULL) || (n->in_ifps[idx] == ifp)) && 6065255332Scy (n->in_ifps[idx] != NULL && 6066255332Scy n->in_ifps[idx] != (void *)-1)) { 6067255332Scy 6068255332Scy ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, 6069255332Scy 0, n->in_ifps[idx]); 6070255332Scy ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, 6071255332Scy 0, n->in_ifps[idx]); 6072255332Scy ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, 6073255332Scy 0, n->in_ifps[idx]); 6074255332Scy ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, 6075255332Scy 0, n->in_ifps[idx]); 6076255332Scy } 6077145522Sdarrenr } 6078255332Scy RWLOCK_EXIT(&softc->ipf_nat); 607953642Sguido SPL_X(s); 608053642Sguido} 608153642Sguido 608253642Sguido 6083145522Sdarrenr/* ------------------------------------------------------------------------ */ 6084255332Scy/* Function: ipf_nat_icmpquerytype */ 6085145522Sdarrenr/* Returns: int - 1 == success, 0 == failure */ 6086145522Sdarrenr/* Parameters: icmptype(I) - ICMP type number */ 6087145522Sdarrenr/* */ 6088145522Sdarrenr/* Tests to see if the ICMP type number passed is a query/response type or */ 6089145522Sdarrenr/* not. */ 6090145522Sdarrenr/* ------------------------------------------------------------------------ */ 6091255332Scystatic int 6092255332Scyipf_nat_icmpquerytype(icmptype) 6093255332Scy int icmptype; 6094145522Sdarrenr{ 6095145522Sdarrenr 6096145522Sdarrenr /* 6097145522Sdarrenr * For the ICMP query NAT code, it is essential that both the query 6098145522Sdarrenr * and the reply match on the NAT rule. Because the NAT structure 6099145522Sdarrenr * does not keep track of the icmptype, and a single NAT structure 6100145522Sdarrenr * is used for all icmp types with the same src, dest and id, we 6101145522Sdarrenr * simply define the replies as queries as well. The funny thing is, 6102145522Sdarrenr * altough it seems silly to call a reply a query, this is exactly 6103145522Sdarrenr * as it is defined in the IPv4 specification 6104145522Sdarrenr */ 6105145522Sdarrenr switch (icmptype) 6106145522Sdarrenr { 6107145522Sdarrenr case ICMP_ECHOREPLY: 6108145522Sdarrenr case ICMP_ECHO: 6109324513Scy /* route advertisement/solicitation is currently unsupported: */ 6110324513Scy /* it would require rewriting the ICMP data section */ 6111145522Sdarrenr case ICMP_TSTAMP: 6112145522Sdarrenr case ICMP_TSTAMPREPLY: 6113145522Sdarrenr case ICMP_IREQ: 6114145522Sdarrenr case ICMP_IREQREPLY: 6115145522Sdarrenr case ICMP_MASKREQ: 6116145522Sdarrenr case ICMP_MASKREPLY: 6117145522Sdarrenr return 1; 6118145522Sdarrenr default: 6119145522Sdarrenr return 0; 6120145522Sdarrenr } 6121145522Sdarrenr} 6122145522Sdarrenr 6123145522Sdarrenr 6124145522Sdarrenr/* ------------------------------------------------------------------------ */ 6125145522Sdarrenr/* Function: nat_log */ 6126145522Sdarrenr/* Returns: Nil */ 6127255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6128255332Scy/* softn(I) - pointer to NAT context structure */ 6129255332Scy/* nat(I) - pointer to NAT structure */ 6130255332Scy/* action(I) - action related to NAT structure being performed */ 6131145522Sdarrenr/* */ 6132145522Sdarrenr/* Creates a NAT log entry. */ 6133145522Sdarrenr/* ------------------------------------------------------------------------ */ 6134255332Scyvoid 6135255332Scyipf_nat_log(softc, softn, nat, action) 6136255332Scy ipf_main_softc_t *softc; 6137255332Scy ipf_nat_softc_t *softn; 6138255332Scy struct nat *nat; 6139255332Scy u_int action; 614053642Sguido{ 6141145522Sdarrenr#ifdef IPFILTER_LOG 6142139005Smlaier# ifndef LARGE_NAT 614353642Sguido struct ipnat *np; 6144138979Sdarrenr int rulen; 6145138979Sdarrenr# endif 614653642Sguido struct natlog natl; 614753642Sguido void *items[1]; 614853642Sguido size_t sizes[1]; 6149138979Sdarrenr int types[1]; 615053642Sguido 6151255332Scy bcopy((char *)&nat->nat_osrc6, (char *)&natl.nl_osrcip, 6152255332Scy sizeof(natl.nl_osrcip)); 6153255332Scy bcopy((char *)&nat->nat_nsrc6, (char *)&natl.nl_nsrcip, 6154255332Scy sizeof(natl.nl_nsrcip)); 6155255332Scy bcopy((char *)&nat->nat_odst6, (char *)&natl.nl_odstip, 6156255332Scy sizeof(natl.nl_odstip)); 6157255332Scy bcopy((char *)&nat->nat_ndst6, (char *)&natl.nl_ndstip, 6158255332Scy sizeof(natl.nl_ndstip)); 6159255332Scy 6160145522Sdarrenr natl.nl_bytes[0] = nat->nat_bytes[0]; 6161145522Sdarrenr natl.nl_bytes[1] = nat->nat_bytes[1]; 6162145522Sdarrenr natl.nl_pkts[0] = nat->nat_pkts[0]; 6163145522Sdarrenr natl.nl_pkts[1] = nat->nat_pkts[1]; 6164255332Scy natl.nl_odstport = nat->nat_odport; 6165255332Scy natl.nl_osrcport = nat->nat_osport; 6166255332Scy natl.nl_nsrcport = nat->nat_nsport; 6167255332Scy natl.nl_ndstport = nat->nat_ndport; 6168255332Scy natl.nl_p[0] = nat->nat_pr[0]; 6169255332Scy natl.nl_p[1] = nat->nat_pr[1]; 6170255332Scy natl.nl_v[0] = nat->nat_v[0]; 6171255332Scy natl.nl_v[1] = nat->nat_v[1]; 6172255332Scy natl.nl_type = nat->nat_redir; 6173255332Scy natl.nl_action = action; 617453642Sguido natl.nl_rule = -1; 6175255332Scy 6176255332Scy bcopy(nat->nat_ifnames[0], natl.nl_ifnames[0], 6177255332Scy sizeof(nat->nat_ifnames[0])); 6178255332Scy bcopy(nat->nat_ifnames[1], natl.nl_ifnames[1], 6179255332Scy sizeof(nat->nat_ifnames[1])); 6180255332Scy 6181145522Sdarrenr# ifndef LARGE_NAT 618253642Sguido if (nat->nat_ptr != NULL) { 6183255332Scy for (rulen = 0, np = softn->ipf_nat_list; np != NULL; 6184255332Scy np = np->in_next, rulen++) 618553642Sguido if (np == nat->nat_ptr) { 618653642Sguido natl.nl_rule = rulen; 618753642Sguido break; 618853642Sguido } 618953642Sguido } 6190145522Sdarrenr# endif 619153642Sguido items[0] = &natl; 619253642Sguido sizes[0] = sizeof(natl); 619353642Sguido types[0] = 0; 619453642Sguido 6195255332Scy (void) ipf_log_items(softc, IPL_LOGNAT, NULL, items, sizes, types, 1); 6196145522Sdarrenr#endif 619753642Sguido} 619892685Sdarrenr 619992685Sdarrenr 6200255332Scy 6201255332Scy 6202145522Sdarrenr/* ------------------------------------------------------------------------ */ 6203255332Scy/* Function: ipf_nat_rule_deref */ 6204170268Sdarrenr/* Returns: Nil */ 6205255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6206255332Scy/* inp(I) - pointer to pointer to NAT rule */ 6207170268Sdarrenr/* Write Locks: ipf_nat */ 6208170268Sdarrenr/* */ 6209255332Scy/* Dropping the refernce count for a rule means that whatever held the */ 6210255332Scy/* pointer to this rule (*inp) is no longer interested in it and when the */ 6211255332Scy/* reference count drops to zero, any resources allocated for the rule can */ 6212255332Scy/* be released and the rule itself free'd. */ 6213170268Sdarrenr/* ------------------------------------------------------------------------ */ 6214255332Scyvoid 6215255332Scyipf_nat_rule_deref(softc, inp) 6216255332Scy ipf_main_softc_t *softc; 6217255332Scy ipnat_t **inp; 6218170268Sdarrenr{ 6219255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 6220255332Scy ipnat_t *n; 6221170268Sdarrenr 6222255332Scy n = *inp; 6223170268Sdarrenr *inp = NULL; 6224255332Scy n->in_use--; 6225255332Scy if (n->in_use > 0) 6226255332Scy return; 6227255332Scy 6228255332Scy if (n->in_apr != NULL) 6229255332Scy ipf_proxy_deref(n->in_apr); 6230255332Scy 6231255332Scy ipf_nat_rule_fini(softc, n); 6232255332Scy 6233255332Scy if (n->in_redir & NAT_REDIRECT) { 6234255332Scy if ((n->in_flags & IPN_PROXYRULE) == 0) { 6235255332Scy ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_rdr); 6236255332Scy } 6237255332Scy } 6238255332Scy if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { 6239255332Scy if ((n->in_flags & IPN_PROXYRULE) == 0) { 6240255332Scy ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_map); 6241255332Scy } 6242255332Scy } 6243255332Scy 6244255332Scy if (n->in_tqehead[0] != NULL) { 6245255332Scy if (ipf_deletetimeoutqueue(n->in_tqehead[0]) == 0) { 6246255332Scy ipf_freetimeoutqueue(softc, n->in_tqehead[1]); 6247255332Scy } 6248255332Scy } 6249255332Scy 6250255332Scy if (n->in_tqehead[1] != NULL) { 6251255332Scy if (ipf_deletetimeoutqueue(n->in_tqehead[1]) == 0) { 6252255332Scy ipf_freetimeoutqueue(softc, n->in_tqehead[1]); 6253255332Scy } 6254255332Scy } 6255255332Scy 6256255332Scy if ((n->in_flags & IPN_PROXYRULE) == 0) { 6257255332Scy ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules); 6258255332Scy } 6259255332Scy 6260255332Scy MUTEX_DESTROY(&n->in_lock); 6261255332Scy 6262255332Scy KFREES(n, n->in_size); 6263255332Scy 6264255332Scy#if SOLARIS && !defined(INSTANCES) 6265255332Scy if (softn->ipf_nat_stats.ns_rules == 0) 6266255332Scy pfil_delayed_copy = 1; 6267170268Sdarrenr#endif 6268170268Sdarrenr} 6269170268Sdarrenr 6270170268Sdarrenr 6271170268Sdarrenr/* ------------------------------------------------------------------------ */ 6272255332Scy/* Function: ipf_nat_deref */ 6273145522Sdarrenr/* Returns: Nil */ 6274255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6275255332Scy/* natp(I) - pointer to pointer to NAT table entry */ 6276145522Sdarrenr/* */ 6277145522Sdarrenr/* Decrement the reference counter for this NAT table entry and free it if */ 6278145522Sdarrenr/* there are no more things using it. */ 6279172776Sdarrenr/* */ 6280172776Sdarrenr/* IF nat_ref == 1 when this function is called, then we have an orphan nat */ 6281172776Sdarrenr/* structure *because* it only gets called on paths _after_ nat_ref has been*/ 6282172776Sdarrenr/* incremented. If nat_ref == 1 then we shouldn't decrement it here */ 6283172776Sdarrenr/* because nat_delete() will do that and send nat_ref to -1. */ 6284172776Sdarrenr/* */ 6285172776Sdarrenr/* Holding the lock on nat_lock is required to serialise nat_delete() being */ 6286172776Sdarrenr/* called from a NAT flush ioctl with a deref happening because of a packet.*/ 6287145522Sdarrenr/* ------------------------------------------------------------------------ */ 6288255332Scyvoid 6289255332Scyipf_nat_deref(softc, natp) 6290255332Scy ipf_main_softc_t *softc; 6291255332Scy nat_t **natp; 6292145522Sdarrenr{ 6293145522Sdarrenr nat_t *nat; 6294145522Sdarrenr 6295145522Sdarrenr nat = *natp; 6296145522Sdarrenr *natp = NULL; 6297172776Sdarrenr 6298172776Sdarrenr MUTEX_ENTER(&nat->nat_lock); 6299172776Sdarrenr if (nat->nat_ref > 1) { 6300172776Sdarrenr nat->nat_ref--; 6301255332Scy ASSERT(nat->nat_ref >= 0); 6302172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 6303172776Sdarrenr return; 6304172776Sdarrenr } 6305172776Sdarrenr MUTEX_EXIT(&nat->nat_lock); 6306172776Sdarrenr 6307255332Scy WRITE_ENTER(&softc->ipf_nat); 6308255332Scy ipf_nat_delete(softc, nat, NL_EXPIRE); 6309255332Scy RWLOCK_EXIT(&softc->ipf_nat); 6310145522Sdarrenr} 6311145522Sdarrenr 6312145522Sdarrenr 6313145522Sdarrenr/* ------------------------------------------------------------------------ */ 6314255332Scy/* Function: ipf_nat_clone */ 6315145522Sdarrenr/* Returns: ipstate_t* - NULL == cloning failed, */ 6316145522Sdarrenr/* else pointer to new state structure */ 6317145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 6318145522Sdarrenr/* is(I) - pointer to master state structure */ 6319145522Sdarrenr/* Write Lock: ipf_nat */ 6320145522Sdarrenr/* */ 6321145522Sdarrenr/* Create a "duplcate" state table entry from the master. */ 6322145522Sdarrenr/* ------------------------------------------------------------------------ */ 6323255332Scynat_t * 6324255332Scyipf_nat_clone(fin, nat) 6325255332Scy fr_info_t *fin; 6326255332Scy nat_t *nat; 6327145522Sdarrenr{ 6328255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 6329255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 6330145522Sdarrenr frentry_t *fr; 6331145522Sdarrenr nat_t *clone; 6332145522Sdarrenr ipnat_t *np; 6333145522Sdarrenr 6334145522Sdarrenr KMALLOC(clone, nat_t *); 6335255332Scy if (clone == NULL) { 6336255332Scy NBUMPSIDED(fin->fin_out, ns_clone_nomem); 6337145522Sdarrenr return NULL; 6338255332Scy } 6339145522Sdarrenr bcopy((char *)nat, (char *)clone, sizeof(*clone)); 6340145522Sdarrenr 6341145522Sdarrenr MUTEX_NUKE(&clone->nat_lock); 6342145522Sdarrenr 6343255332Scy clone->nat_rev = fin->fin_rev; 6344153876Sguido clone->nat_aps = NULL; 6345153876Sguido /* 6346255332Scy * Initialize all these so that ipf_nat_delete() doesn't cause a crash. 6347153876Sguido */ 6348153876Sguido clone->nat_tqe.tqe_pnext = NULL; 6349153876Sguido clone->nat_tqe.tqe_next = NULL; 6350153876Sguido clone->nat_tqe.tqe_ifq = NULL; 6351153876Sguido clone->nat_tqe.tqe_parent = clone; 6352153876Sguido 6353145522Sdarrenr clone->nat_flags &= ~SI_CLONE; 6354145522Sdarrenr clone->nat_flags |= SI_CLONED; 6355145522Sdarrenr 6356153876Sguido if (clone->nat_hm) 6357153876Sguido clone->nat_hm->hm_ref++; 6358145522Sdarrenr 6359255332Scy if (ipf_nat_insert(softc, softn, clone) == -1) { 6360145522Sdarrenr KFREE(clone); 6361255332Scy NBUMPSIDED(fin->fin_out, ns_insert_fail); 6362145522Sdarrenr return NULL; 6363145522Sdarrenr } 6364255332Scy 6365145522Sdarrenr np = clone->nat_ptr; 6366145522Sdarrenr if (np != NULL) { 6367255332Scy if (softn->ipf_nat_logging) 6368255332Scy ipf_nat_log(softc, softn, clone, NL_CLONE); 6369145522Sdarrenr np->in_use++; 6370145522Sdarrenr } 6371145522Sdarrenr fr = clone->nat_fr; 6372145522Sdarrenr if (fr != NULL) { 6373145522Sdarrenr MUTEX_ENTER(&fr->fr_lock); 6374145522Sdarrenr fr->fr_ref++; 6375145522Sdarrenr MUTEX_EXIT(&fr->fr_lock); 6376145522Sdarrenr } 6377145522Sdarrenr 6378255332Scy 6379145522Sdarrenr /* 6380145522Sdarrenr * Because the clone is created outside the normal loop of things and 6381145522Sdarrenr * TCP has special needs in terms of state, initialise the timeout 6382145522Sdarrenr * state of the new NAT from here. 6383145522Sdarrenr */ 6384255332Scy if (clone->nat_pr[0] == IPPROTO_TCP) { 6385255332Scy (void) ipf_tcp_age(&clone->nat_tqe, fin, softn->ipf_nat_tcptq, 6386255332Scy clone->nat_flags, 2); 6387145522Sdarrenr } 6388255332Scy clone->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, clone); 6389255332Scy if (softn->ipf_nat_logging) 6390255332Scy ipf_nat_log(softc, softn, clone, NL_CLONE); 6391145522Sdarrenr return clone; 6392145522Sdarrenr} 6393145522Sdarrenr 6394145522Sdarrenr 6395145522Sdarrenr/* ------------------------------------------------------------------------ */ 6396255332Scy/* Function: ipf_nat_wildok */ 6397145522Sdarrenr/* Returns: int - 1 == packet's ports match wildcards */ 6398145522Sdarrenr/* 0 == packet's ports don't match wildcards */ 6399145522Sdarrenr/* Parameters: nat(I) - NAT entry */ 6400145522Sdarrenr/* sport(I) - source port */ 6401145522Sdarrenr/* dport(I) - destination port */ 6402145522Sdarrenr/* flags(I) - wildcard flags */ 6403145522Sdarrenr/* dir(I) - packet direction */ 6404145522Sdarrenr/* */ 6405145522Sdarrenr/* Use NAT entry and packet direction to determine which combination of */ 6406145522Sdarrenr/* wildcard flags should be used. */ 6407145522Sdarrenr/* ------------------------------------------------------------------------ */ 6408255332Scyint 6409255332Scyipf_nat_wildok(nat, sport, dport, flags, dir) 6410255332Scy nat_t *nat; 6411255332Scy int sport, dport, flags, dir; 6412145522Sdarrenr{ 6413145522Sdarrenr /* 6414145522Sdarrenr * When called by dir is set to 6415145522Sdarrenr * nat_inlookup NAT_INBOUND (0) 6416145522Sdarrenr * nat_outlookup NAT_OUTBOUND (1) 6417145522Sdarrenr * 6418145522Sdarrenr * We simply combine the packet's direction in dir with the original 6419145522Sdarrenr * "intended" direction of that NAT entry in nat->nat_dir to decide 6420145522Sdarrenr * which combination of wildcard flags to allow. 6421145522Sdarrenr */ 6422255332Scy switch ((dir << 1) | (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND))) 6423145522Sdarrenr { 6424145522Sdarrenr case 3: /* outbound packet / outbound entry */ 6425255332Scy if (((nat->nat_osport == sport) || 6426145522Sdarrenr (flags & SI_W_SPORT)) && 6427255332Scy ((nat->nat_odport == dport) || 6428145522Sdarrenr (flags & SI_W_DPORT))) 6429145522Sdarrenr return 1; 6430145522Sdarrenr break; 6431145522Sdarrenr case 2: /* outbound packet / inbound entry */ 6432255332Scy if (((nat->nat_osport == dport) || 6433255332Scy (flags & SI_W_SPORT)) && 6434255332Scy ((nat->nat_odport == sport) || 6435255332Scy (flags & SI_W_DPORT))) 6436145522Sdarrenr return 1; 6437145522Sdarrenr break; 6438145522Sdarrenr case 1: /* inbound packet / outbound entry */ 6439255332Scy if (((nat->nat_osport == dport) || 6440255332Scy (flags & SI_W_SPORT)) && 6441255332Scy ((nat->nat_odport == sport) || 6442255332Scy (flags & SI_W_DPORT))) 6443145522Sdarrenr return 1; 6444145522Sdarrenr break; 6445145522Sdarrenr case 0: /* inbound packet / inbound entry */ 6446255332Scy if (((nat->nat_osport == sport) || 6447145522Sdarrenr (flags & SI_W_SPORT)) && 6448255332Scy ((nat->nat_odport == dport) || 6449145522Sdarrenr (flags & SI_W_DPORT))) 6450145522Sdarrenr return 1; 6451145522Sdarrenr break; 6452145522Sdarrenr default: 6453145522Sdarrenr break; 6454145522Sdarrenr } 6455145522Sdarrenr 6456145522Sdarrenr return(0); 6457145522Sdarrenr} 6458145522Sdarrenr 6459145522Sdarrenr 6460145522Sdarrenr/* ------------------------------------------------------------------------ */ 6461145522Sdarrenr/* Function: nat_mssclamp */ 6462145522Sdarrenr/* Returns: Nil */ 6463145522Sdarrenr/* Parameters: tcp(I) - pointer to TCP header */ 6464145522Sdarrenr/* maxmss(I) - value to clamp the TCP MSS to */ 6465145522Sdarrenr/* fin(I) - pointer to packet information */ 6466145522Sdarrenr/* csump(I) - pointer to TCP checksum */ 6467145522Sdarrenr/* */ 6468145522Sdarrenr/* Check for MSS option and clamp it if necessary. If found and changed, */ 6469145522Sdarrenr/* then the TCP header checksum will be updated to reflect the change in */ 6470145522Sdarrenr/* the MSS. */ 6471145522Sdarrenr/* ------------------------------------------------------------------------ */ 6472255332Scystatic void 6473255332Scyipf_nat_mssclamp(tcp, maxmss, fin, csump) 6474255332Scy tcphdr_t *tcp; 6475255332Scy u_32_t maxmss; 6476255332Scy fr_info_t *fin; 6477255332Scy u_short *csump; 6478110916Sdarrenr{ 6479110916Sdarrenr u_char *cp, *ep, opt; 6480110916Sdarrenr int hlen, advance; 6481110916Sdarrenr u_32_t mss, sumd; 6482110916Sdarrenr 6483145522Sdarrenr hlen = TCP_OFF(tcp) << 2; 6484110916Sdarrenr if (hlen > sizeof(*tcp)) { 6485110916Sdarrenr cp = (u_char *)tcp + sizeof(*tcp); 6486110916Sdarrenr ep = (u_char *)tcp + hlen; 6487110916Sdarrenr 6488110916Sdarrenr while (cp < ep) { 6489110916Sdarrenr opt = cp[0]; 6490110916Sdarrenr if (opt == TCPOPT_EOL) 6491110916Sdarrenr break; 6492110916Sdarrenr else if (opt == TCPOPT_NOP) { 6493110916Sdarrenr cp++; 6494110916Sdarrenr continue; 6495110916Sdarrenr } 6496145522Sdarrenr 6497145522Sdarrenr if (cp + 1 >= ep) 6498110916Sdarrenr break; 6499110916Sdarrenr advance = cp[1]; 6500145522Sdarrenr if ((cp + advance > ep) || (advance <= 0)) 6501110916Sdarrenr break; 6502145522Sdarrenr switch (opt) 6503145522Sdarrenr { 6504110916Sdarrenr case TCPOPT_MAXSEG: 6505110916Sdarrenr if (advance != 4) 6506110916Sdarrenr break; 6507145522Sdarrenr mss = cp[2] * 256 + cp[3]; 6508110916Sdarrenr if (mss > maxmss) { 6509145522Sdarrenr cp[2] = maxmss / 256; 6510145522Sdarrenr cp[3] = maxmss & 0xff; 6511110916Sdarrenr CALC_SUMD(mss, maxmss, sumd); 6512255332Scy ipf_fix_outcksum(0, csump, sumd, 0); 6513110916Sdarrenr } 6514110916Sdarrenr break; 6515110916Sdarrenr default: 6516110916Sdarrenr /* ignore unknown options */ 6517110916Sdarrenr break; 6518110916Sdarrenr } 6519145522Sdarrenr 6520145522Sdarrenr cp += advance; 6521145522Sdarrenr } 6522145522Sdarrenr } 6523145522Sdarrenr} 6524145522Sdarrenr 6525145522Sdarrenr 6526145522Sdarrenr/* ------------------------------------------------------------------------ */ 6527255332Scy/* Function: ipf_nat_setqueue */ 6528145522Sdarrenr/* Returns: Nil */ 6529255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6530255332Scy/* softn(I) - pointer to NAT context structure */ 6531255332Scy/* nat(I)- pointer to NAT structure */ 6532145522Sdarrenr/* Locks: ipf_nat (read or write) */ 6533145522Sdarrenr/* */ 6534145522Sdarrenr/* Put the NAT entry on its default queue entry, using rev as a helped in */ 6535145522Sdarrenr/* determining which queue it should be placed on. */ 6536145522Sdarrenr/* ------------------------------------------------------------------------ */ 6537255332Scyvoid 6538255332Scyipf_nat_setqueue(softc, softn, nat) 6539255332Scy ipf_main_softc_t *softc; 6540255332Scy ipf_nat_softc_t *softn; 6541255332Scy nat_t *nat; 6542145522Sdarrenr{ 6543145522Sdarrenr ipftq_t *oifq, *nifq; 6544255332Scy int rev = nat->nat_rev; 6545145522Sdarrenr 6546145522Sdarrenr if (nat->nat_ptr != NULL) 6547145522Sdarrenr nifq = nat->nat_ptr->in_tqehead[rev]; 6548145522Sdarrenr else 6549145522Sdarrenr nifq = NULL; 6550145522Sdarrenr 6551145522Sdarrenr if (nifq == NULL) { 6552255332Scy switch (nat->nat_pr[0]) 6553145522Sdarrenr { 6554145522Sdarrenr case IPPROTO_UDP : 6555255332Scy nifq = &softn->ipf_nat_udptq; 6556145522Sdarrenr break; 6557145522Sdarrenr case IPPROTO_ICMP : 6558255332Scy nifq = &softn->ipf_nat_icmptq; 6559145522Sdarrenr break; 6560145522Sdarrenr case IPPROTO_TCP : 6561255332Scy nifq = softn->ipf_nat_tcptq + 6562255332Scy nat->nat_tqe.tqe_state[rev]; 6563145522Sdarrenr break; 6564145522Sdarrenr default : 6565255332Scy nifq = &softn->ipf_nat_iptq; 6566145522Sdarrenr break; 6567145522Sdarrenr } 6568145522Sdarrenr } 6569145522Sdarrenr 6570145522Sdarrenr oifq = nat->nat_tqe.tqe_ifq; 6571145522Sdarrenr /* 6572145522Sdarrenr * If it's currently on a timeout queue, move it from one queue to 6573145522Sdarrenr * another, else put it on the end of the newly determined queue. 6574145522Sdarrenr */ 6575145522Sdarrenr if (oifq != NULL) 6576255332Scy ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, nifq); 6577145522Sdarrenr else 6578255332Scy ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, nifq, nat); 6579145522Sdarrenr return; 6580145522Sdarrenr} 6581170268Sdarrenr 6582170268Sdarrenr 6583170268Sdarrenr/* ------------------------------------------------------------------------ */ 6584170268Sdarrenr/* Function: nat_getnext */ 6585170268Sdarrenr/* Returns: int - 0 == ok, else error */ 6586255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6587255332Scy/* t(I) - pointer to ipftoken structure */ 6588170268Sdarrenr/* itp(I) - pointer to ipfgeniter_t structure */ 6589170268Sdarrenr/* */ 6590170268Sdarrenr/* Fetch the next nat/ipnat structure pointer from the linked list and */ 6591170268Sdarrenr/* copy it out to the storage space pointed to by itp_data. The next item */ 6592170268Sdarrenr/* in the list to look at is put back in the ipftoken struture. */ 6593170268Sdarrenr/* ------------------------------------------------------------------------ */ 6594255332Scystatic int 6595255332Scyipf_nat_getnext(softc, t, itp, objp) 6596255332Scy ipf_main_softc_t *softc; 6597255332Scy ipftoken_t *t; 6598255332Scy ipfgeniter_t *itp; 6599255332Scy ipfobj_t *objp; 6600170268Sdarrenr{ 6601255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 6602170268Sdarrenr hostmap_t *hm, *nexthm = NULL, zerohm; 6603170268Sdarrenr ipnat_t *ipn, *nextipnat = NULL, zeroipn; 6604170268Sdarrenr nat_t *nat, *nextnat = NULL, zeronat; 6605255332Scy int error = 0; 6606255332Scy void *nnext; 6607170268Sdarrenr 6608255332Scy if (itp->igi_nitems != 1) { 6609255332Scy IPFERROR(60075); 6610172776Sdarrenr return ENOSPC; 6611255332Scy } 6612170268Sdarrenr 6613255332Scy READ_ENTER(&softc->ipf_nat); 6614170268Sdarrenr 6615170268Sdarrenr switch (itp->igi_type) 6616170268Sdarrenr { 6617170268Sdarrenr case IPFGENITER_HOSTMAP : 6618170268Sdarrenr hm = t->ipt_data; 6619170268Sdarrenr if (hm == NULL) { 6620255332Scy nexthm = softn->ipf_hm_maplist; 6621170268Sdarrenr } else { 6622170268Sdarrenr nexthm = hm->hm_next; 6623170268Sdarrenr } 6624255332Scy if (nexthm != NULL) { 6625255332Scy ATOMIC_INC32(nexthm->hm_ref); 6626255332Scy t->ipt_data = nexthm; 6627255332Scy } else { 6628255332Scy bzero(&zerohm, sizeof(zerohm)); 6629255332Scy nexthm = &zerohm; 6630255332Scy t->ipt_data = NULL; 6631255332Scy } 6632255332Scy nnext = nexthm->hm_next; 6633170268Sdarrenr break; 6634170268Sdarrenr 6635170268Sdarrenr case IPFGENITER_IPNAT : 6636170268Sdarrenr ipn = t->ipt_data; 6637170268Sdarrenr if (ipn == NULL) { 6638255332Scy nextipnat = softn->ipf_nat_list; 6639170268Sdarrenr } else { 6640170268Sdarrenr nextipnat = ipn->in_next; 6641170268Sdarrenr } 6642255332Scy if (nextipnat != NULL) { 6643255332Scy ATOMIC_INC32(nextipnat->in_use); 6644255332Scy t->ipt_data = nextipnat; 6645255332Scy } else { 6646255332Scy bzero(&zeroipn, sizeof(zeroipn)); 6647255332Scy nextipnat = &zeroipn; 6648255332Scy t->ipt_data = NULL; 6649255332Scy } 6650255332Scy nnext = nextipnat->in_next; 6651170268Sdarrenr break; 6652170268Sdarrenr 6653170268Sdarrenr case IPFGENITER_NAT : 6654170268Sdarrenr nat = t->ipt_data; 6655170268Sdarrenr if (nat == NULL) { 6656255332Scy nextnat = softn->ipf_nat_instances; 6657170268Sdarrenr } else { 6658170268Sdarrenr nextnat = nat->nat_next; 6659170268Sdarrenr } 6660255332Scy if (nextnat != NULL) { 6661255332Scy MUTEX_ENTER(&nextnat->nat_lock); 6662255332Scy nextnat->nat_ref++; 6663255332Scy MUTEX_EXIT(&nextnat->nat_lock); 6664255332Scy t->ipt_data = nextnat; 6665255332Scy } else { 6666255332Scy bzero(&zeronat, sizeof(zeronat)); 6667255332Scy nextnat = &zeronat; 6668255332Scy t->ipt_data = NULL; 6669255332Scy } 6670255332Scy nnext = nextnat->nat_next; 6671170268Sdarrenr break; 6672255332Scy 6673170268Sdarrenr default : 6674255332Scy RWLOCK_EXIT(&softc->ipf_nat); 6675255332Scy IPFERROR(60055); 6676170268Sdarrenr return EINVAL; 6677170268Sdarrenr } 6678170268Sdarrenr 6679255332Scy RWLOCK_EXIT(&softc->ipf_nat); 6680170268Sdarrenr 6681255332Scy objp->ipfo_ptr = itp->igi_data; 6682170268Sdarrenr 6683172776Sdarrenr switch (itp->igi_type) 6684172776Sdarrenr { 6685172776Sdarrenr case IPFGENITER_HOSTMAP : 6686255332Scy error = COPYOUT(nexthm, objp->ipfo_ptr, sizeof(*nexthm)); 6687255332Scy if (error != 0) { 6688255332Scy IPFERROR(60049); 6689255332Scy error = EFAULT; 6690255332Scy } 6691172776Sdarrenr if (hm != NULL) { 6692255332Scy WRITE_ENTER(&softc->ipf_nat); 6693255332Scy ipf_nat_hostmapdel(softc, &hm); 6694255332Scy RWLOCK_EXIT(&softc->ipf_nat); 6695172776Sdarrenr } 6696172776Sdarrenr break; 6697255332Scy 6698172776Sdarrenr case IPFGENITER_IPNAT : 6699255332Scy objp->ipfo_size = nextipnat->in_size; 6700255332Scy objp->ipfo_type = IPFOBJ_IPNAT; 6701255332Scy error = ipf_outobjk(softc, objp, nextipnat); 6702172776Sdarrenr if (ipn != NULL) { 6703255332Scy WRITE_ENTER(&softc->ipf_nat); 6704255332Scy ipf_nat_rule_deref(softc, &ipn); 6705255332Scy RWLOCK_EXIT(&softc->ipf_nat); 6706172776Sdarrenr } 6707172776Sdarrenr break; 6708172776Sdarrenr 6709170268Sdarrenr case IPFGENITER_NAT : 6710255332Scy objp->ipfo_size = sizeof(nat_t); 6711255332Scy objp->ipfo_type = IPFOBJ_NAT; 6712255332Scy error = ipf_outobjk(softc, objp, nextnat); 6713255332Scy if (nat != NULL) 6714255332Scy ipf_nat_deref(softc, &nat); 6715170268Sdarrenr 6716170268Sdarrenr break; 6717170268Sdarrenr } 6718170268Sdarrenr 6719255332Scy if (nnext == NULL) 6720255332Scy ipf_token_mark_complete(t); 6721255332Scy 6722170268Sdarrenr return error; 6723170268Sdarrenr} 6724170268Sdarrenr 6725170268Sdarrenr 6726170268Sdarrenr/* ------------------------------------------------------------------------ */ 6727170268Sdarrenr/* Function: nat_extraflush */ 6728170268Sdarrenr/* Returns: int - 0 == success, -1 == failure */ 6729255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6730255332Scy/* softn(I) - pointer to NAT context structure */ 6731255332Scy/* which(I) - how to flush the active NAT table */ 6732170268Sdarrenr/* Write Locks: ipf_nat */ 6733170268Sdarrenr/* */ 6734170268Sdarrenr/* Flush nat tables. Three actions currently defined: */ 6735170268Sdarrenr/* which == 0 : flush all nat table entries */ 6736170268Sdarrenr/* which == 1 : flush TCP connections which have started to close but are */ 6737170268Sdarrenr/* stuck for some reason. */ 6738170268Sdarrenr/* which == 2 : flush TCP connections which have been idle for a long time, */ 6739170268Sdarrenr/* starting at > 4 days idle and working back in successive half-*/ 6740170268Sdarrenr/* days to at most 12 hours old. If this fails to free enough */ 6741170268Sdarrenr/* slots then work backwards in half hour slots to 30 minutes. */ 6742170268Sdarrenr/* If that too fails, then work backwards in 30 second intervals */ 6743170268Sdarrenr/* for the last 30 minutes to at worst 30 seconds idle. */ 6744170268Sdarrenr/* ------------------------------------------------------------------------ */ 6745255332Scystatic int 6746255332Scyipf_nat_extraflush(softc, softn, which) 6747255332Scy ipf_main_softc_t *softc; 6748255332Scy ipf_nat_softc_t *softn; 6749255332Scy int which; 6750170268Sdarrenr{ 6751170268Sdarrenr nat_t *nat, **natp; 6752170268Sdarrenr ipftqent_t *tqn; 6753255332Scy ipftq_t *ifq; 6754170268Sdarrenr int removed; 6755170268Sdarrenr SPL_INT(s); 6756170268Sdarrenr 6757170268Sdarrenr removed = 0; 6758170268Sdarrenr 6759170268Sdarrenr SPL_NET(s); 6760170268Sdarrenr switch (which) 6761170268Sdarrenr { 6762170268Sdarrenr case 0 : 6763255332Scy softn->ipf_nat_stats.ns_flush_all++; 6764170268Sdarrenr /* 6765170268Sdarrenr * Style 0 flush removes everything... 6766170268Sdarrenr */ 6767255332Scy for (natp = &softn->ipf_nat_instances; 6768255332Scy ((nat = *natp) != NULL); ) { 6769255332Scy ipf_nat_delete(softc, nat, NL_FLUSH); 6770170268Sdarrenr removed++; 6771170268Sdarrenr } 6772170268Sdarrenr break; 6773170268Sdarrenr 6774170268Sdarrenr case 1 : 6775255332Scy softn->ipf_nat_stats.ns_flush_closing++; 6776170268Sdarrenr /* 6777170268Sdarrenr * Since we're only interested in things that are closing, 6778170268Sdarrenr * we can start with the appropriate timeout queue. 6779170268Sdarrenr */ 6780255332Scy for (ifq = softn->ipf_nat_tcptq + IPF_TCPS_CLOSE_WAIT; 6781255332Scy ifq != NULL; ifq = ifq->ifq_next) { 6782170268Sdarrenr 6783170268Sdarrenr for (tqn = ifq->ifq_head; tqn != NULL; ) { 6784170268Sdarrenr nat = tqn->tqe_parent; 6785170268Sdarrenr tqn = tqn->tqe_next; 6786255332Scy if (nat->nat_pr[0] != IPPROTO_TCP || 6787255332Scy nat->nat_pr[1] != IPPROTO_TCP) 6788170268Sdarrenr break; 6789255332Scy ipf_nat_delete(softc, nat, NL_EXPIRE); 6790170268Sdarrenr removed++; 6791170268Sdarrenr } 6792170268Sdarrenr } 6793170268Sdarrenr 6794170268Sdarrenr /* 6795170268Sdarrenr * Also need to look through the user defined queues. 6796170268Sdarrenr */ 6797255332Scy for (ifq = softn->ipf_nat_utqe; ifq != NULL; 6798255332Scy ifq = ifq->ifq_next) { 6799170268Sdarrenr for (tqn = ifq->ifq_head; tqn != NULL; ) { 6800170268Sdarrenr nat = tqn->tqe_parent; 6801170268Sdarrenr tqn = tqn->tqe_next; 6802255332Scy if (nat->nat_pr[0] != IPPROTO_TCP || 6803255332Scy nat->nat_pr[1] != IPPROTO_TCP) 6804170268Sdarrenr continue; 6805170268Sdarrenr 6806170268Sdarrenr if ((nat->nat_tcpstate[0] > 6807170268Sdarrenr IPF_TCPS_ESTABLISHED) && 6808170268Sdarrenr (nat->nat_tcpstate[1] > 6809170268Sdarrenr IPF_TCPS_ESTABLISHED)) { 6810255332Scy ipf_nat_delete(softc, nat, NL_EXPIRE); 6811170268Sdarrenr removed++; 6812170268Sdarrenr } 6813170268Sdarrenr } 6814170268Sdarrenr } 6815170268Sdarrenr break; 6816170268Sdarrenr 6817170268Sdarrenr /* 6818170268Sdarrenr * Args 5-11 correspond to flushing those particular states 6819170268Sdarrenr * for TCP connections. 6820170268Sdarrenr */ 6821170268Sdarrenr case IPF_TCPS_CLOSE_WAIT : 6822170268Sdarrenr case IPF_TCPS_FIN_WAIT_1 : 6823170268Sdarrenr case IPF_TCPS_CLOSING : 6824170268Sdarrenr case IPF_TCPS_LAST_ACK : 6825170268Sdarrenr case IPF_TCPS_FIN_WAIT_2 : 6826170268Sdarrenr case IPF_TCPS_TIME_WAIT : 6827170268Sdarrenr case IPF_TCPS_CLOSED : 6828255332Scy softn->ipf_nat_stats.ns_flush_state++; 6829255332Scy tqn = softn->ipf_nat_tcptq[which].ifq_head; 6830170268Sdarrenr while (tqn != NULL) { 6831170268Sdarrenr nat = tqn->tqe_parent; 6832170268Sdarrenr tqn = tqn->tqe_next; 6833255332Scy ipf_nat_delete(softc, nat, NL_FLUSH); 6834170268Sdarrenr removed++; 6835170268Sdarrenr } 6836170268Sdarrenr break; 6837255332Scy 6838170268Sdarrenr default : 6839170268Sdarrenr if (which < 30) 6840170268Sdarrenr break; 6841255332Scy 6842255332Scy softn->ipf_nat_stats.ns_flush_timeout++; 6843170268Sdarrenr /* 6844170268Sdarrenr * Take a large arbitrary number to mean the number of seconds 6845170268Sdarrenr * for which which consider to be the maximum value we'll allow 6846170268Sdarrenr * the expiration to be. 6847170268Sdarrenr */ 6848170268Sdarrenr which = IPF_TTLVAL(which); 6849255332Scy for (natp = &softn->ipf_nat_instances; 6850255332Scy ((nat = *natp) != NULL); ) { 6851255332Scy if (softc->ipf_ticks - nat->nat_touched > which) { 6852255332Scy ipf_nat_delete(softc, nat, NL_FLUSH); 6853170268Sdarrenr removed++; 6854170268Sdarrenr } else 6855170268Sdarrenr natp = &nat->nat_next; 6856170268Sdarrenr } 6857170268Sdarrenr break; 6858170268Sdarrenr } 6859170268Sdarrenr 6860170268Sdarrenr if (which != 2) { 6861170268Sdarrenr SPL_X(s); 6862170268Sdarrenr return removed; 6863170268Sdarrenr } 6864170268Sdarrenr 6865255332Scy softn->ipf_nat_stats.ns_flush_queue++; 6866255332Scy 6867170268Sdarrenr /* 6868255332Scy * Asked to remove inactive entries because the table is full, try 6869255332Scy * again, 3 times, if first attempt failed with a different criteria 6870255332Scy * each time. The order tried in must be in decreasing age. 6871255332Scy * Another alternative is to implement random drop and drop N entries 6872255332Scy * at random until N have been freed up. 6873170268Sdarrenr */ 6874255332Scy if (softc->ipf_ticks - softn->ipf_nat_last_force_flush > 6875255332Scy IPF_TTLVAL(5)) { 6876255332Scy softn->ipf_nat_last_force_flush = softc->ipf_ticks; 6877255332Scy 6878255332Scy removed = ipf_queueflush(softc, ipf_nat_flush_entry, 6879255332Scy softn->ipf_nat_tcptq, 6880255332Scy softn->ipf_nat_utqe, 6881255332Scy &softn->ipf_nat_stats.ns_active, 6882255332Scy softn->ipf_nat_table_sz, 6883255332Scy softn->ipf_nat_table_wm_low); 6884170268Sdarrenr } 6885170268Sdarrenr 6886170268Sdarrenr SPL_X(s); 6887170268Sdarrenr return removed; 6888170268Sdarrenr} 6889170268Sdarrenr 6890170268Sdarrenr 6891170268Sdarrenr/* ------------------------------------------------------------------------ */ 6892255332Scy/* Function: ipf_nat_flush_entry */ 6893170268Sdarrenr/* Returns: 0 - always succeeds */ 6894255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6895255332Scy/* entry(I) - pointer to NAT entry */ 6896170268Sdarrenr/* Write Locks: ipf_nat */ 6897170268Sdarrenr/* */ 6898170268Sdarrenr/* This function is a stepping stone between ipf_queueflush() and */ 6899170268Sdarrenr/* nat_dlete(). It is used so we can provide a uniform interface via the */ 6900170268Sdarrenr/* ipf_queueflush() function. Since the nat_delete() function returns void */ 6901170268Sdarrenr/* we translate that to mean it always succeeds in deleting something. */ 6902170268Sdarrenr/* ------------------------------------------------------------------------ */ 6903255332Scystatic int 6904255332Scyipf_nat_flush_entry(softc, entry) 6905255332Scy ipf_main_softc_t *softc; 6906255332Scy void *entry; 6907170268Sdarrenr{ 6908255332Scy ipf_nat_delete(softc, entry, NL_FLUSH); 6909170268Sdarrenr return 0; 6910170268Sdarrenr} 6911172776Sdarrenr 6912172776Sdarrenr 6913172776Sdarrenr/* ------------------------------------------------------------------------ */ 6914255332Scy/* Function: ipf_nat_iterator */ 6915255332Scy/* Returns: int - 0 == ok, else error */ 6916255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6917255332Scy/* token(I) - pointer to ipftoken structure */ 6918255332Scy/* itp(I) - pointer to ipfgeniter_t structure */ 6919255332Scy/* obj(I) - pointer to data description structure */ 6920255332Scy/* */ 6921255332Scy/* This function acts as a handler for the SIOCGENITER ioctls that use a */ 6922255332Scy/* generic structure to iterate through a list. There are three different */ 6923255332Scy/* linked lists of NAT related information to go through: NAT rules, active */ 6924255332Scy/* NAT mappings and the NAT fragment cache. */ 6925255332Scy/* ------------------------------------------------------------------------ */ 6926255332Scystatic int 6927255332Scyipf_nat_iterator(softc, token, itp, obj) 6928255332Scy ipf_main_softc_t *softc; 6929255332Scy ipftoken_t *token; 6930255332Scy ipfgeniter_t *itp; 6931255332Scy ipfobj_t *obj; 6932255332Scy{ 6933255332Scy int error; 6934255332Scy 6935255332Scy if (itp->igi_data == NULL) { 6936255332Scy IPFERROR(60052); 6937255332Scy return EFAULT; 6938255332Scy } 6939255332Scy 6940255332Scy switch (itp->igi_type) 6941255332Scy { 6942255332Scy case IPFGENITER_HOSTMAP : 6943255332Scy case IPFGENITER_IPNAT : 6944255332Scy case IPFGENITER_NAT : 6945255332Scy error = ipf_nat_getnext(softc, token, itp, obj); 6946255332Scy break; 6947255332Scy 6948255332Scy case IPFGENITER_NATFRAG : 6949255332Scy error = ipf_frag_nat_next(softc, token, itp); 6950255332Scy break; 6951255332Scy default : 6952255332Scy IPFERROR(60053); 6953255332Scy error = EINVAL; 6954255332Scy break; 6955255332Scy } 6956255332Scy 6957255332Scy return error; 6958255332Scy} 6959255332Scy 6960255332Scy 6961255332Scy/* ------------------------------------------------------------------------ */ 6962255332Scy/* Function: ipf_nat_setpending */ 6963255332Scy/* Returns: Nil */ 6964255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 6965255332Scy/* nat(I) - pointer to NAT structure */ 6966255332Scy/* Locks: ipf_nat (read or write) */ 6967255332Scy/* */ 6968255332Scy/* Put the NAT entry on to the pending queue - this queue has a very short */ 6969255332Scy/* lifetime where items are put that can't be deleted straight away because */ 6970255332Scy/* of locking issues but we want to delete them ASAP, anyway. In calling */ 6971255332Scy/* this function, it is assumed that the owner (if there is one, as shown */ 6972255332Scy/* by nat_me) is no longer interested in it. */ 6973255332Scy/* ------------------------------------------------------------------------ */ 6974255332Scyvoid 6975255332Scyipf_nat_setpending(softc, nat) 6976255332Scy ipf_main_softc_t *softc; 6977255332Scy nat_t *nat; 6978255332Scy{ 6979255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 6980255332Scy ipftq_t *oifq; 6981255332Scy 6982255332Scy oifq = nat->nat_tqe.tqe_ifq; 6983255332Scy if (oifq != NULL) 6984255332Scy ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, 6985255332Scy &softn->ipf_nat_pending); 6986255332Scy else 6987255332Scy ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, 6988255332Scy &softn->ipf_nat_pending, nat); 6989255332Scy 6990255332Scy if (nat->nat_me != NULL) { 6991255332Scy *nat->nat_me = NULL; 6992255332Scy nat->nat_me = NULL; 6993255332Scy nat->nat_ref--; 6994255332Scy ASSERT(nat->nat_ref >= 0); 6995255332Scy } 6996255332Scy} 6997255332Scy 6998255332Scy 6999255332Scy/* ------------------------------------------------------------------------ */ 7000255332Scy/* Function: nat_newrewrite */ 7001255332Scy/* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ 7002255332Scy/* allow rule to be moved if IPN_ROUNDR is set. */ 7003255332Scy/* Parameters: fin(I) - pointer to packet information */ 7004255332Scy/* nat(I) - pointer to NAT entry */ 7005255332Scy/* ni(I) - pointer to structure with misc. information needed */ 7006255332Scy/* to create new NAT entry. */ 7007255332Scy/* Write Lock: ipf_nat */ 7008255332Scy/* */ 7009255332Scy/* This function is responsible for setting up an active NAT session where */ 7010255332Scy/* we are changing both the source and destination parameters at the same */ 7011255332Scy/* time. The loop in here works differently to elsewhere - each iteration */ 7012255332Scy/* is responsible for changing a single parameter that can be incremented. */ 7013255332Scy/* So one pass may increase the source IP#, next source port, next dest. IP#*/ 7014255332Scy/* and the last destination port for a total of 4 iterations to try each. */ 7015255332Scy/* This is done to try and exhaustively use the translation space available.*/ 7016255332Scy/* ------------------------------------------------------------------------ */ 7017255332Scystatic int 7018255332Scyipf_nat_newrewrite(fin, nat, nai) 7019255332Scy fr_info_t *fin; 7020255332Scy nat_t *nat; 7021255332Scy natinfo_t *nai; 7022255332Scy{ 7023255332Scy int src_search = 1; 7024255332Scy int dst_search = 1; 7025255332Scy fr_info_t frnat; 7026255332Scy u_32_t flags; 7027255332Scy u_short swap; 7028255332Scy ipnat_t *np; 7029255332Scy nat_t *natl; 7030255332Scy int l = 0; 7031255332Scy int changed; 7032255332Scy 7033255332Scy natl = NULL; 7034255332Scy changed = -1; 7035255332Scy np = nai->nai_np; 7036255332Scy flags = nat->nat_flags; 7037255332Scy bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); 7038255332Scy 7039255332Scy nat->nat_hm = NULL; 7040255332Scy 7041255332Scy do { 7042255332Scy changed = -1; 7043255332Scy /* TRACE (l, src_search, dst_search, np) */ 7044338170Scy DT4(ipf_nat_rewrite_1, int, l, int, src_search, int, dst_search, ipnat_t *, np); 7045255332Scy 7046255332Scy if ((src_search == 0) && (np->in_spnext == 0) && 7047255332Scy (dst_search == 0) && (np->in_dpnext == 0)) { 7048255332Scy if (l > 0) 7049255332Scy return -1; 7050255332Scy } 7051255332Scy 7052255332Scy /* 7053255332Scy * Find a new source address 7054255332Scy */ 7055255332Scy if (ipf_nat_nextaddr(fin, &np->in_nsrc, &frnat.fin_saddr, 7056255332Scy &frnat.fin_saddr) == -1) { 7057255332Scy return -1; 7058255332Scy } 7059255332Scy 7060255332Scy if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0xffffffff)) { 7061255332Scy src_search = 0; 7062255332Scy if (np->in_stepnext == 0) 7063255332Scy np->in_stepnext = 1; 7064255332Scy 7065255332Scy } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) { 7066255332Scy src_search = 0; 7067255332Scy if (np->in_stepnext == 0) 7068255332Scy np->in_stepnext = 1; 7069255332Scy 7070255332Scy } else if (np->in_nsrcmsk == 0xffffffff) { 7071255332Scy src_search = 0; 7072255332Scy if (np->in_stepnext == 0) 7073255332Scy np->in_stepnext = 1; 7074255332Scy 7075255332Scy } else if (np->in_nsrcmsk != 0xffffffff) { 7076255332Scy if (np->in_stepnext == 0 && changed == -1) { 7077255332Scy np->in_snip++; 7078255332Scy np->in_stepnext++; 7079255332Scy changed = 0; 7080255332Scy } 7081255332Scy } 7082255332Scy 7083255332Scy if ((flags & IPN_TCPUDPICMP) != 0) { 7084255332Scy if (np->in_spnext != 0) 7085255332Scy frnat.fin_data[0] = np->in_spnext; 7086255332Scy 7087255332Scy /* 7088255332Scy * Standard port translation. Select next port. 7089255332Scy */ 7090255332Scy if ((flags & IPN_FIXEDSPORT) != 0) { 7091255332Scy np->in_stepnext = 2; 7092255332Scy } else if ((np->in_stepnext == 1) && 7093255332Scy (changed == -1) && (natl != NULL)) { 7094255332Scy np->in_spnext++; 7095255332Scy np->in_stepnext++; 7096255332Scy changed = 1; 7097255332Scy if (np->in_spnext > np->in_spmax) 7098255332Scy np->in_spnext = np->in_spmin; 7099255332Scy } 7100255332Scy } else { 7101255332Scy np->in_stepnext = 2; 7102255332Scy } 7103255332Scy np->in_stepnext &= 0x3; 7104255332Scy 7105255332Scy /* 7106255332Scy * Find a new destination address 7107255332Scy */ 7108255332Scy /* TRACE (fin, np, l, frnat) */ 7109338170Scy DT4(ipf_nat_rewrite_2, frinfo_t *, fin, ipnat_t *, np, int, l, frinfo_t *, &frnat); 7110255332Scy 7111255332Scy if (ipf_nat_nextaddr(fin, &np->in_ndst, &frnat.fin_daddr, 7112255332Scy &frnat.fin_daddr) == -1) 7113255332Scy return -1; 7114255332Scy if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) { 7115255332Scy dst_search = 0; 7116255332Scy if (np->in_stepnext == 2) 7117255332Scy np->in_stepnext = 3; 7118255332Scy 7119255332Scy } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0)) { 7120255332Scy dst_search = 0; 7121255332Scy if (np->in_stepnext == 2) 7122255332Scy np->in_stepnext = 3; 7123255332Scy 7124255332Scy } else if (np->in_ndstmsk == 0xffffffff) { 7125255332Scy dst_search = 0; 7126255332Scy if (np->in_stepnext == 2) 7127255332Scy np->in_stepnext = 3; 7128255332Scy 7129255332Scy } else if (np->in_ndstmsk != 0xffffffff) { 7130255332Scy if ((np->in_stepnext == 2) && (changed == -1) && 7131255332Scy (natl != NULL)) { 7132255332Scy changed = 2; 7133255332Scy np->in_stepnext++; 7134255332Scy np->in_dnip++; 7135255332Scy } 7136255332Scy } 7137255332Scy 7138255332Scy if ((flags & IPN_TCPUDPICMP) != 0) { 7139255332Scy if (np->in_dpnext != 0) 7140255332Scy frnat.fin_data[1] = np->in_dpnext; 7141255332Scy 7142255332Scy /* 7143255332Scy * Standard port translation. Select next port. 7144255332Scy */ 7145255332Scy if ((flags & IPN_FIXEDDPORT) != 0) { 7146255332Scy np->in_stepnext = 0; 7147255332Scy } else if (np->in_stepnext == 3 && changed == -1) { 7148255332Scy np->in_dpnext++; 7149255332Scy np->in_stepnext++; 7150255332Scy changed = 3; 7151255332Scy if (np->in_dpnext > np->in_dpmax) 7152255332Scy np->in_dpnext = np->in_dpmin; 7153255332Scy } 7154255332Scy } else { 7155255332Scy if (np->in_stepnext == 3) 7156255332Scy np->in_stepnext = 0; 7157255332Scy } 7158255332Scy 7159255332Scy /* TRACE (frnat) */ 7160338170Scy DT1(ipf_nat_rewrite_3, frinfo_t *, &frnat); 7161255332Scy 7162255332Scy /* 7163255332Scy * Here we do a lookup of the connection as seen from 7164255332Scy * the outside. If an IP# pair already exists, try 7165255332Scy * again. So if you have A->B becomes C->B, you can 7166255332Scy * also have D->E become C->E but not D->B causing 7167255332Scy * another C->B. Also take protocol and ports into 7168255332Scy * account when determining whether a pre-existing 7169255332Scy * NAT setup will cause an external conflict where 7170255332Scy * this is appropriate. 7171255332Scy * 7172255332Scy * fin_data[] is swapped around because we are doing a 7173255332Scy * lookup of the packet is if it were moving in the opposite 7174255332Scy * direction of the one we are working with now. 7175255332Scy */ 7176255332Scy if (flags & IPN_TCPUDP) { 7177255332Scy swap = frnat.fin_data[0]; 7178255332Scy frnat.fin_data[0] = frnat.fin_data[1]; 7179255332Scy frnat.fin_data[1] = swap; 7180255332Scy } 7181255332Scy if (fin->fin_out == 1) { 7182255332Scy natl = ipf_nat_inlookup(&frnat, 7183255332Scy flags & ~(SI_WILDP|NAT_SEARCH), 7184255332Scy (u_int)frnat.fin_p, 7185255332Scy frnat.fin_dst, frnat.fin_src); 7186255332Scy 7187255332Scy } else { 7188255332Scy natl = ipf_nat_outlookup(&frnat, 7189255332Scy flags & ~(SI_WILDP|NAT_SEARCH), 7190255332Scy (u_int)frnat.fin_p, 7191255332Scy frnat.fin_dst, frnat.fin_src); 7192255332Scy } 7193255332Scy if (flags & IPN_TCPUDP) { 7194255332Scy swap = frnat.fin_data[0]; 7195255332Scy frnat.fin_data[0] = frnat.fin_data[1]; 7196255332Scy frnat.fin_data[1] = swap; 7197255332Scy } 7198255332Scy 7199255332Scy /* TRACE natl, in_stepnext, l */ 7200338170Scy DT3(ipf_nat_rewrite_2, nat_t *, natl, ipnat_t *, np , int, l); 7201255332Scy 7202255332Scy if ((natl != NULL) && (l > 8)) /* XXX 8 is arbitrary */ 7203255332Scy return -1; 7204255332Scy 7205255332Scy np->in_stepnext &= 0x3; 7206255332Scy 7207255332Scy l++; 7208255332Scy changed = -1; 7209255332Scy } while (natl != NULL); 7210255332Scy 7211255332Scy nat->nat_osrcip = fin->fin_src; 7212255332Scy nat->nat_odstip = fin->fin_dst; 7213255332Scy nat->nat_nsrcip = frnat.fin_src; 7214255332Scy nat->nat_ndstip = frnat.fin_dst; 7215255332Scy 7216255332Scy if ((flags & IPN_TCPUDP) != 0) { 7217255332Scy nat->nat_osport = htons(fin->fin_data[0]); 7218255332Scy nat->nat_odport = htons(fin->fin_data[1]); 7219255332Scy nat->nat_nsport = htons(frnat.fin_data[0]); 7220255332Scy nat->nat_ndport = htons(frnat.fin_data[1]); 7221255332Scy } else if ((flags & IPN_ICMPQUERY) != 0) { 7222255332Scy nat->nat_oicmpid = fin->fin_data[1]; 7223255332Scy nat->nat_nicmpid = frnat.fin_data[1]; 7224255332Scy } 7225255332Scy 7226255332Scy return 0; 7227255332Scy} 7228255332Scy 7229255332Scy 7230255332Scy/* ------------------------------------------------------------------------ */ 7231255332Scy/* Function: nat_newdivert */ 7232255332Scy/* Returns: int - -1 == error, 0 == success */ 7233255332Scy/* Parameters: fin(I) - pointer to packet information */ 7234255332Scy/* nat(I) - pointer to NAT entry */ 7235255332Scy/* ni(I) - pointer to structure with misc. information needed */ 7236255332Scy/* to create new NAT entry. */ 7237255332Scy/* Write Lock: ipf_nat */ 7238255332Scy/* */ 7239255332Scy/* Create a new NAT divert session as defined by the NAT rule. This is */ 7240255332Scy/* somewhat different to other NAT session creation routines because we */ 7241255332Scy/* do not iterate through either port numbers or IP addresses, searching */ 7242255332Scy/* for a unique mapping, however, a complimentary duplicate check is made. */ 7243255332Scy/* ------------------------------------------------------------------------ */ 7244255332Scystatic int 7245255332Scyipf_nat_newdivert(fin, nat, nai) 7246255332Scy fr_info_t *fin; 7247255332Scy nat_t *nat; 7248255332Scy natinfo_t *nai; 7249255332Scy{ 7250255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 7251255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 7252255332Scy fr_info_t frnat; 7253255332Scy ipnat_t *np; 7254255332Scy nat_t *natl; 7255255332Scy int p; 7256255332Scy 7257255332Scy np = nai->nai_np; 7258255332Scy bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); 7259255332Scy 7260255332Scy nat->nat_pr[0] = 0; 7261255332Scy nat->nat_osrcaddr = fin->fin_saddr; 7262255332Scy nat->nat_odstaddr = fin->fin_daddr; 7263255332Scy frnat.fin_saddr = htonl(np->in_snip); 7264255332Scy frnat.fin_daddr = htonl(np->in_dnip); 7265255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 7266255332Scy nat->nat_osport = htons(fin->fin_data[0]); 7267255332Scy nat->nat_odport = htons(fin->fin_data[1]); 7268255332Scy } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { 7269255332Scy nat->nat_oicmpid = fin->fin_data[1]; 7270255332Scy } 7271255332Scy 7272255332Scy if (np->in_redir & NAT_DIVERTUDP) { 7273255332Scy frnat.fin_data[0] = np->in_spnext; 7274255332Scy frnat.fin_data[1] = np->in_dpnext; 7275255332Scy frnat.fin_flx |= FI_TCPUDP; 7276255332Scy p = IPPROTO_UDP; 7277255332Scy } else { 7278255332Scy frnat.fin_flx &= ~FI_TCPUDP; 7279255332Scy p = IPPROTO_IPIP; 7280255332Scy } 7281255332Scy 7282255332Scy if (fin->fin_out == 1) { 7283255332Scy natl = ipf_nat_inlookup(&frnat, 0, p, 7284255332Scy frnat.fin_dst, frnat.fin_src); 7285255332Scy 7286255332Scy } else { 7287255332Scy natl = ipf_nat_outlookup(&frnat, 0, p, 7288255332Scy frnat.fin_dst, frnat.fin_src); 7289255332Scy } 7290255332Scy 7291255332Scy if (natl != NULL) { 7292255332Scy NBUMPSIDED(fin->fin_out, ns_divert_exist); 7293338170Scy DT3(ns_divert_exist, fr_info_t *, fin, nat_t *, nat, natinfo_t, nai); 7294255332Scy return -1; 7295255332Scy } 7296255332Scy 7297255332Scy nat->nat_nsrcaddr = frnat.fin_saddr; 7298255332Scy nat->nat_ndstaddr = frnat.fin_daddr; 7299255332Scy if ((nat->nat_flags & IPN_TCPUDP) != 0) { 7300255332Scy nat->nat_nsport = htons(frnat.fin_data[0]); 7301255332Scy nat->nat_ndport = htons(frnat.fin_data[1]); 7302255332Scy } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { 7303255332Scy nat->nat_nicmpid = frnat.fin_data[1]; 7304255332Scy } 7305255332Scy 7306255332Scy nat->nat_pr[fin->fin_out] = fin->fin_p; 7307255332Scy nat->nat_pr[1 - fin->fin_out] = p; 7308255332Scy 7309255332Scy if (np->in_redir & NAT_REDIRECT) 7310255332Scy nat->nat_dir = NAT_DIVERTIN; 7311255332Scy else 7312255332Scy nat->nat_dir = NAT_DIVERTOUT; 7313255332Scy 7314255332Scy return 0; 7315255332Scy} 7316255332Scy 7317255332Scy 7318255332Scy/* ------------------------------------------------------------------------ */ 7319255332Scy/* Function: nat_builddivertmp */ 7320255332Scy/* Returns: int - -1 == error, 0 == success */ 7321255332Scy/* Parameters: softn(I) - pointer to NAT context structure */ 7322255332Scy/* np(I) - pointer to a NAT rule */ 7323255332Scy/* */ 7324255332Scy/* For divert rules, a skeleton packet representing what will be prepended */ 7325255332Scy/* to the real packet is created. Even though we don't have the full */ 7326255332Scy/* packet here, a checksum is calculated that we update later when we */ 7327255332Scy/* fill in the final details. At present a 0 checksum for UDP is being set */ 7328255332Scy/* here because it is expected that divert will be used for localhost. */ 7329255332Scy/* ------------------------------------------------------------------------ */ 7330255332Scystatic int 7331255332Scyipf_nat_builddivertmp(softn, np) 7332255332Scy ipf_nat_softc_t *softn; 7333255332Scy ipnat_t *np; 7334255332Scy{ 7335255332Scy udphdr_t *uh; 7336255332Scy size_t len; 7337255332Scy ip_t *ip; 7338255332Scy 7339255332Scy if ((np->in_redir & NAT_DIVERTUDP) != 0) 7340255332Scy len = sizeof(ip_t) + sizeof(udphdr_t); 7341255332Scy else 7342255332Scy len = sizeof(ip_t); 7343255332Scy 7344255332Scy ALLOC_MB_T(np->in_divmp, len); 7345255332Scy if (np->in_divmp == NULL) { 7346255332Scy NBUMPD(ipf_nat_stats, ns_divert_build); 7347255332Scy return -1; 7348255332Scy } 7349255332Scy 7350255332Scy /* 7351255332Scy * First, the header to get the packet diverted to the new destination 7352255332Scy */ 7353255332Scy ip = MTOD(np->in_divmp, ip_t *); 7354255332Scy IP_V_A(ip, 4); 7355255332Scy IP_HL_A(ip, 5); 7356255332Scy ip->ip_tos = 0; 7357255332Scy if ((np->in_redir & NAT_DIVERTUDP) != 0) 7358255332Scy ip->ip_p = IPPROTO_UDP; 7359255332Scy else 7360255332Scy ip->ip_p = IPPROTO_IPIP; 7361255332Scy ip->ip_ttl = 255; 7362255332Scy ip->ip_off = 0; 7363255332Scy ip->ip_sum = 0; 7364255332Scy ip->ip_len = htons(len); 7365255332Scy ip->ip_id = 0; 7366255332Scy ip->ip_src.s_addr = htonl(np->in_snip); 7367255332Scy ip->ip_dst.s_addr = htonl(np->in_dnip); 7368255332Scy ip->ip_sum = ipf_cksum((u_short *)ip, sizeof(*ip)); 7369255332Scy 7370255332Scy if (np->in_redir & NAT_DIVERTUDP) { 7371255332Scy uh = (udphdr_t *)(ip + 1); 7372255332Scy uh->uh_sum = 0; 7373255332Scy uh->uh_ulen = 8; 7374255332Scy uh->uh_sport = htons(np->in_spnext); 7375255332Scy uh->uh_dport = htons(np->in_dpnext); 7376255332Scy } 7377255332Scy 7378255332Scy return 0; 7379255332Scy} 7380255332Scy 7381255332Scy 7382255332Scy#define MINDECAP (sizeof(ip_t) + sizeof(udphdr_t) + sizeof(ip_t)) 7383255332Scy 7384255332Scy/* ------------------------------------------------------------------------ */ 7385255332Scy/* Function: nat_decap */ 7386255332Scy/* Returns: int - -1 == error, 0 == success */ 7387255332Scy/* Parameters: fin(I) - pointer to packet information */ 7388255332Scy/* nat(I) - pointer to current NAT session */ 7389255332Scy/* */ 7390255332Scy/* This function is responsible for undoing a packet's encapsulation in the */ 7391255332Scy/* reverse of an encap/divert rule. After removing the outer encapsulation */ 7392255332Scy/* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/ 7393255332Scy/* match the "new" packet as it may still be used by IPFilter elsewhere. */ 7394255332Scy/* We use "dir" here as the basis for some of the expectations about the */ 7395255332Scy/* outer header. If we return an error, the goal is to leave the original */ 7396255332Scy/* packet information undisturbed - this falls short at the end where we'd */ 7397255332Scy/* need to back a backup copy of "fin" - expensive. */ 7398255332Scy/* ------------------------------------------------------------------------ */ 7399255332Scystatic int 7400255332Scyipf_nat_decap(fin, nat) 7401255332Scy fr_info_t *fin; 7402255332Scy nat_t *nat; 7403255332Scy{ 7404255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 7405255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 7406255332Scy char *hdr; 7407255332Scy int hlen; 7408255332Scy int skip; 7409255332Scy mb_t *m; 7410255332Scy 7411255332Scy if ((fin->fin_flx & FI_ICMPERR) != 0) { 7412255332Scy /* 7413255332Scy * ICMP packets don't get decapsulated, instead what we need 7414255332Scy * to do is change the ICMP reply from including (in the data 7415255332Scy * portion for errors) the encapsulated packet that we sent 7416255332Scy * out to something that resembles the original packet prior 7417255332Scy * to encapsulation. This isn't done here - all we're doing 7418255332Scy * here is changing the outer address to ensure that it gets 7419255332Scy * targetted back to the correct system. 7420255332Scy */ 7421255332Scy 7422255332Scy if (nat->nat_dir & NAT_OUTBOUND) { 7423255332Scy u_32_t sum1, sum2, sumd; 7424255332Scy 7425255332Scy sum1 = ntohl(fin->fin_daddr); 7426255332Scy sum2 = ntohl(nat->nat_osrcaddr); 7427255332Scy CALC_SUMD(sum1, sum2, sumd); 7428255332Scy fin->fin_ip->ip_dst = nat->nat_osrcip; 7429255332Scy fin->fin_daddr = nat->nat_osrcaddr; 7430369272Scy#if !defined(_KERNEL) || SOLARIS 7431255332Scy ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, sumd, 0); 7432255332Scy#endif 7433255332Scy } 7434255332Scy return 0; 7435255332Scy } 7436255332Scy 7437255332Scy m = fin->fin_m; 7438255332Scy skip = fin->fin_hlen; 7439255332Scy 7440255332Scy switch (nat->nat_dir) 7441255332Scy { 7442255332Scy case NAT_DIVERTIN : 7443255332Scy case NAT_DIVERTOUT : 7444255332Scy if (fin->fin_plen < MINDECAP) 7445255332Scy return -1; 7446255332Scy skip += sizeof(udphdr_t); 7447255332Scy break; 7448255332Scy 7449255332Scy case NAT_ENCAPIN : 7450255332Scy case NAT_ENCAPOUT : 7451255332Scy if (fin->fin_plen < (skip + sizeof(ip_t))) 7452255332Scy return -1; 7453255332Scy break; 7454255332Scy default : 7455255332Scy return -1; 7456255332Scy /* NOTREACHED */ 7457255332Scy } 7458255332Scy 7459255332Scy /* 7460255332Scy * The aim here is to keep the original packet details in "fin" for 7461255332Scy * as long as possible so that returning with an error is for the 7462255332Scy * original packet and there is little undoing work to do. 7463255332Scy */ 7464255332Scy if (M_LEN(m) < skip + sizeof(ip_t)) { 7465255332Scy if (ipf_pr_pullup(fin, skip + sizeof(ip_t)) == -1) 7466255332Scy return -1; 7467255332Scy } 7468255332Scy 7469255332Scy hdr = MTOD(fin->fin_m, char *); 7470255332Scy fin->fin_ip = (ip_t *)(hdr + skip); 7471255332Scy hlen = IP_HL(fin->fin_ip) << 2; 7472255332Scy 7473255332Scy if (ipf_pr_pullup(fin, skip + hlen) == -1) { 7474255332Scy NBUMPSIDED(fin->fin_out, ns_decap_pullup); 7475255332Scy return -1; 7476255332Scy } 7477255332Scy 7478255332Scy fin->fin_hlen = hlen; 7479255332Scy fin->fin_dlen -= skip; 7480255332Scy fin->fin_plen -= skip; 7481255332Scy fin->fin_ipoff += skip; 7482255332Scy 7483255332Scy if (ipf_makefrip(hlen, (ip_t *)hdr, fin) == -1) { 7484255332Scy NBUMPSIDED(fin->fin_out, ns_decap_bad); 7485255332Scy return -1; 7486255332Scy } 7487255332Scy 7488255332Scy return skip; 7489255332Scy} 7490255332Scy 7491255332Scy 7492255332Scy/* ------------------------------------------------------------------------ */ 7493255332Scy/* Function: nat_nextaddr */ 7494255332Scy/* Returns: int - -1 == bad input (no new address), */ 7495255332Scy/* 0 == success and dst has new address */ 7496255332Scy/* Parameters: fin(I) - pointer to packet information */ 7497255332Scy/* na(I) - how to generate new address */ 7498255332Scy/* old(I) - original address being replaced */ 7499255332Scy/* dst(O) - where to put the new address */ 7500255332Scy/* Write Lock: ipf_nat */ 7501255332Scy/* */ 7502255332Scy/* This function uses the contents of the "na" structure, in combination */ 7503255332Scy/* with "old" to produce a new address to store in "dst". Not all of the */ 7504255332Scy/* possible uses of "na" will result in a new address. */ 7505255332Scy/* ------------------------------------------------------------------------ */ 7506255332Scystatic int 7507255332Scyipf_nat_nextaddr(fin, na, old, dst) 7508255332Scy fr_info_t *fin; 7509255332Scy nat_addr_t *na; 7510255332Scy u_32_t *old, *dst; 7511255332Scy{ 7512255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 7513255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 7514255332Scy u_32_t amin, amax, new; 7515255332Scy i6addr_t newip; 7516255332Scy int error; 7517255332Scy 7518255332Scy new = 0; 7519255332Scy amin = na->na_addr[0].in4.s_addr; 7520255332Scy 7521255332Scy switch (na->na_atype) 7522255332Scy { 7523255332Scy case FRI_RANGE : 7524255332Scy amax = na->na_addr[1].in4.s_addr; 7525255332Scy break; 7526255332Scy 7527255332Scy case FRI_NETMASKED : 7528255332Scy case FRI_DYNAMIC : 7529255332Scy case FRI_NORMAL : 7530255332Scy /* 7531255332Scy * Compute the maximum address by adding the inverse of the 7532255332Scy * netmask to the minimum address. 7533255332Scy */ 7534255332Scy amax = ~na->na_addr[1].in4.s_addr; 7535255332Scy amax |= amin; 7536255332Scy break; 7537255332Scy 7538255332Scy case FRI_LOOKUP : 7539255332Scy break; 7540255332Scy 7541255332Scy case FRI_BROADCAST : 7542255332Scy case FRI_PEERADDR : 7543255332Scy case FRI_NETWORK : 7544255332Scy default : 7545338170Scy DT4(ns_na_atype, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new); 7546255332Scy return -1; 7547255332Scy } 7548255332Scy 7549255332Scy error = -1; 7550255332Scy 7551255332Scy if (na->na_atype == FRI_LOOKUP) { 7552255332Scy if (na->na_type == IPLT_DSTLIST) { 7553255332Scy error = ipf_dstlist_select_node(fin, na->na_ptr, dst, 7554255332Scy NULL); 7555255332Scy } else { 7556255332Scy NBUMPSIDE(fin->fin_out, ns_badnextaddr); 7557338170Scy DT4(ns_badnextaddr_1, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new); 7558255332Scy } 7559255332Scy 7560255332Scy } else if (na->na_atype == IPLT_NONE) { 7561255332Scy /* 7562255332Scy * 0/0 as the new address means leave it alone. 7563255332Scy */ 7564255332Scy if (na->na_addr[0].in4.s_addr == 0 && 7565255332Scy na->na_addr[1].in4.s_addr == 0) { 7566255332Scy new = *old; 7567255332Scy 7568255332Scy /* 7569255332Scy * 0/32 means get the interface's address 7570255332Scy */ 7571255332Scy } else if (na->na_addr[0].in4.s_addr == 0 && 7572255332Scy na->na_addr[1].in4.s_addr == 0xffffffff) { 7573255332Scy if (ipf_ifpaddr(softc, 4, na->na_atype, 7574255332Scy fin->fin_ifp, &newip, NULL) == -1) { 7575255332Scy NBUMPSIDED(fin->fin_out, ns_ifpaddrfail); 7576338170Scy DT4(ns_ifpaddrfail, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new); 7577255332Scy return -1; 7578255332Scy } 7579255332Scy new = newip.in4.s_addr; 7580255332Scy } else { 7581255332Scy new = htonl(na->na_nextip); 7582255332Scy } 7583255332Scy *dst = new; 7584255332Scy error = 0; 7585255332Scy 7586255332Scy } else { 7587255332Scy NBUMPSIDE(fin->fin_out, ns_badnextaddr); 7588338170Scy DT4(ns_badnextaddr_2, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new); 7589255332Scy } 7590255332Scy 7591255332Scy return error; 7592255332Scy} 7593255332Scy 7594255332Scy 7595255332Scy/* ------------------------------------------------------------------------ */ 7596255332Scy/* Function: nat_nextaddrinit */ 7597255332Scy/* Returns: int - 0 == success, else error number */ 7598255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7599255332Scy/* na(I) - NAT address information for generating new addr*/ 7600255332Scy/* initial(I) - flag indicating if it is the first call for */ 7601255332Scy/* this "na" structure. */ 7602255332Scy/* ifp(I) - network interface to derive address */ 7603255332Scy/* information from. */ 7604255332Scy/* */ 7605255332Scy/* This function is expected to be called in two scenarious: when a new NAT */ 7606255332Scy/* rule is loaded into the kernel and when the list of NAT rules is sync'd */ 7607255332Scy/* up with the valid network interfaces (possibly due to them changing.) */ 7608255332Scy/* To distinguish between these, the "initial" parameter is used. If it is */ 7609255332Scy/* 1 then this indicates the rule has just been reloaded and 0 for when we */ 7610255332Scy/* are updating information. This difference is important because in */ 7611255332Scy/* instances where we are not updating address information associated with */ 7612255332Scy/* a network interface, we don't want to disturb what the "next" address to */ 7613255332Scy/* come out of ipf_nat_nextaddr() will be. */ 7614255332Scy/* ------------------------------------------------------------------------ */ 7615255332Scystatic int 7616255332Scyipf_nat_nextaddrinit(softc, base, na, initial, ifp) 7617255332Scy ipf_main_softc_t *softc; 7618255332Scy char *base; 7619255332Scy nat_addr_t *na; 7620255332Scy int initial; 7621255332Scy void *ifp; 7622255332Scy{ 7623255332Scy 7624255332Scy switch (na->na_atype) 7625255332Scy { 7626255332Scy case FRI_LOOKUP : 7627255332Scy if (na->na_subtype == 0) { 7628255332Scy na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT, 7629255332Scy na->na_type, 7630255332Scy na->na_num, 7631255332Scy &na->na_func); 7632255332Scy } else if (na->na_subtype == 1) { 7633255332Scy na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT, 7634255332Scy na->na_type, 7635255332Scy base + na->na_num, 7636255332Scy &na->na_func); 7637255332Scy } 7638255332Scy if (na->na_func == NULL) { 7639255332Scy IPFERROR(60060); 7640255332Scy return ESRCH; 7641255332Scy } 7642255332Scy if (na->na_ptr == NULL) { 7643255332Scy IPFERROR(60056); 7644255332Scy return ESRCH; 7645255332Scy } 7646255332Scy break; 7647255332Scy 7648255332Scy case FRI_DYNAMIC : 7649255332Scy case FRI_BROADCAST : 7650255332Scy case FRI_NETWORK : 7651255332Scy case FRI_NETMASKED : 7652255332Scy case FRI_PEERADDR : 7653255332Scy if (ifp != NULL) 7654255332Scy (void )ipf_ifpaddr(softc, 4, na->na_atype, ifp, 7655255332Scy &na->na_addr[0], &na->na_addr[1]); 7656255332Scy break; 7657255332Scy 7658255332Scy case FRI_SPLIT : 7659255332Scy case FRI_RANGE : 7660255332Scy if (initial) 7661255332Scy na->na_nextip = ntohl(na->na_addr[0].in4.s_addr); 7662255332Scy break; 7663255332Scy 7664255332Scy case FRI_NONE : 7665255332Scy na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr; 7666255332Scy return 0; 7667255332Scy 7668255332Scy case FRI_NORMAL : 7669255332Scy na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr; 7670255332Scy break; 7671255332Scy 7672255332Scy default : 7673255332Scy IPFERROR(60054); 7674255332Scy return EINVAL; 7675255332Scy } 7676255332Scy 7677255332Scy if (initial && (na->na_atype == FRI_NORMAL)) { 7678255332Scy if (na->na_addr[0].in4.s_addr == 0) { 7679255332Scy if ((na->na_addr[1].in4.s_addr == 0xffffffff) || 7680255332Scy (na->na_addr[1].in4.s_addr == 0)) { 7681255332Scy return 0; 7682255332Scy } 7683255332Scy } 7684255332Scy 7685255332Scy if (na->na_addr[1].in4.s_addr == 0xffffffff) { 7686255332Scy na->na_nextip = ntohl(na->na_addr[0].in4.s_addr); 7687255332Scy } else { 7688255332Scy na->na_nextip = ntohl(na->na_addr[0].in4.s_addr) + 1; 7689255332Scy } 7690255332Scy } 7691255332Scy 7692255332Scy return 0; 7693255332Scy} 7694255332Scy 7695255332Scy 7696255332Scy/* ------------------------------------------------------------------------ */ 7697255332Scy/* Function: ipf_nat_matchflush */ 7698255332Scy/* Returns: int - -1 == error, 0 == success */ 7699255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7700255332Scy/* softn(I) - pointer to NAT context structure */ 7701255332Scy/* nat(I) - pointer to current NAT session */ 7702255332Scy/* */ 7703255332Scy/* ------------------------------------------------------------------------ */ 7704255332Scystatic int 7705255332Scyipf_nat_matchflush(softc, softn, data) 7706255332Scy ipf_main_softc_t *softc; 7707255332Scy ipf_nat_softc_t *softn; 7708255332Scy caddr_t data; 7709255332Scy{ 7710255332Scy int *array, flushed, error; 7711255332Scy nat_t *nat, *natnext; 7712255332Scy ipfobj_t obj; 7713255332Scy 7714255332Scy error = ipf_matcharray_load(softc, data, &obj, &array); 7715255332Scy if (error != 0) 7716255332Scy return error; 7717255332Scy 7718255332Scy flushed = 0; 7719255332Scy 7720255332Scy for (nat = softn->ipf_nat_instances; nat != NULL; nat = natnext) { 7721255332Scy natnext = nat->nat_next; 7722255332Scy if (ipf_nat_matcharray(nat, array, softc->ipf_ticks) == 0) { 7723255332Scy ipf_nat_delete(softc, nat, NL_FLUSH); 7724255332Scy flushed++; 7725255332Scy } 7726255332Scy } 7727255332Scy 7728255332Scy obj.ipfo_retval = flushed; 7729255332Scy error = BCOPYOUT(&obj, data, sizeof(obj)); 7730255332Scy 7731255332Scy KFREES(array, array[0] * sizeof(*array)); 7732255332Scy 7733255332Scy return error; 7734255332Scy} 7735255332Scy 7736255332Scy 7737255332Scy/* ------------------------------------------------------------------------ */ 7738255332Scy/* Function: ipf_nat_matcharray */ 7739255332Scy/* Returns: int - -1 == error, 0 == success */ 7740255332Scy/* Parameters: fin(I) - pointer to packet information */ 7741255332Scy/* nat(I) - pointer to current NAT session */ 7742255332Scy/* */ 7743255332Scy/* ------------------------------------------------------------------------ */ 7744255332Scystatic int 7745255332Scyipf_nat_matcharray(nat, array, ticks) 7746255332Scy nat_t *nat; 7747255332Scy int *array; 7748255332Scy u_long ticks; 7749255332Scy{ 7750255332Scy int i, n, *x, e, p; 7751255332Scy 7752255332Scy e = 0; 7753255332Scy n = array[0]; 7754255332Scy x = array + 1; 7755255332Scy 7756255332Scy for (; n > 0; x += 3 + x[2]) { 7757255332Scy if (x[0] == IPF_EXP_END) 7758255332Scy break; 7759255332Scy e = 0; 7760255332Scy 7761255332Scy n -= x[2] + 3; 7762255332Scy if (n < 0) 7763255332Scy break; 7764255332Scy 7765255332Scy p = x[0] >> 16; 7766255332Scy if (p != 0 && p != nat->nat_pr[1]) 7767255332Scy break; 7768255332Scy 7769255332Scy switch (x[0]) 7770255332Scy { 7771255332Scy case IPF_EXP_IP_PR : 7772255332Scy for (i = 0; !e && i < x[2]; i++) { 7773255332Scy e |= (nat->nat_pr[1] == x[i + 3]); 7774255332Scy } 7775255332Scy break; 7776255332Scy 7777255332Scy case IPF_EXP_IP_SRCADDR : 7778255332Scy if (nat->nat_v[0] == 4) { 7779255332Scy for (i = 0; !e && i < x[2]; i++) { 7780255332Scy e |= ((nat->nat_osrcaddr & x[i + 4]) == 7781255332Scy x[i + 3]); 7782255332Scy } 7783255332Scy } 7784255332Scy if (nat->nat_v[1] == 4) { 7785255332Scy for (i = 0; !e && i < x[2]; i++) { 7786255332Scy e |= ((nat->nat_nsrcaddr & x[i + 4]) == 7787255332Scy x[i + 3]); 7788255332Scy } 7789255332Scy } 7790255332Scy break; 7791255332Scy 7792255332Scy case IPF_EXP_IP_DSTADDR : 7793255332Scy if (nat->nat_v[0] == 4) { 7794255332Scy for (i = 0; !e && i < x[2]; i++) { 7795255332Scy e |= ((nat->nat_odstaddr & x[i + 4]) == 7796255332Scy x[i + 3]); 7797255332Scy } 7798255332Scy } 7799255332Scy if (nat->nat_v[1] == 4) { 7800255332Scy for (i = 0; !e && i < x[2]; i++) { 7801255332Scy e |= ((nat->nat_ndstaddr & x[i + 4]) == 7802255332Scy x[i + 3]); 7803255332Scy } 7804255332Scy } 7805255332Scy break; 7806255332Scy 7807255332Scy case IPF_EXP_IP_ADDR : 7808255332Scy for (i = 0; !e && i < x[2]; i++) { 7809255332Scy if (nat->nat_v[0] == 4) { 7810255332Scy e |= ((nat->nat_osrcaddr & x[i + 4]) == 7811255332Scy x[i + 3]); 7812255332Scy } 7813255332Scy if (nat->nat_v[1] == 4) { 7814255332Scy e |= ((nat->nat_nsrcaddr & x[i + 4]) == 7815255332Scy x[i + 3]); 7816255332Scy } 7817255332Scy if (nat->nat_v[0] == 4) { 7818255332Scy e |= ((nat->nat_odstaddr & x[i + 4]) == 7819255332Scy x[i + 3]); 7820255332Scy } 7821255332Scy if (nat->nat_v[1] == 4) { 7822255332Scy e |= ((nat->nat_ndstaddr & x[i + 4]) == 7823255332Scy x[i + 3]); 7824255332Scy } 7825255332Scy } 7826255332Scy break; 7827255332Scy 7828255332Scy#ifdef USE_INET6 7829255332Scy case IPF_EXP_IP6_SRCADDR : 7830255332Scy if (nat->nat_v[0] == 6) { 7831255332Scy for (i = 0; !e && i < x[3]; i++) { 7832255332Scy e |= IP6_MASKEQ(&nat->nat_osrc6, 7833255332Scy x + i + 7, x + i + 3); 7834255332Scy } 7835255332Scy } 7836255332Scy if (nat->nat_v[1] == 6) { 7837255332Scy for (i = 0; !e && i < x[3]; i++) { 7838255332Scy e |= IP6_MASKEQ(&nat->nat_nsrc6, 7839255332Scy x + i + 7, x + i + 3); 7840255332Scy } 7841255332Scy } 7842255332Scy break; 7843255332Scy 7844255332Scy case IPF_EXP_IP6_DSTADDR : 7845255332Scy if (nat->nat_v[0] == 6) { 7846255332Scy for (i = 0; !e && i < x[3]; i++) { 7847255332Scy e |= IP6_MASKEQ(&nat->nat_odst6, 7848255332Scy x + i + 7, 7849255332Scy x + i + 3); 7850255332Scy } 7851255332Scy } 7852255332Scy if (nat->nat_v[1] == 6) { 7853255332Scy for (i = 0; !e && i < x[3]; i++) { 7854255332Scy e |= IP6_MASKEQ(&nat->nat_ndst6, 7855255332Scy x + i + 7, 7856255332Scy x + i + 3); 7857255332Scy } 7858255332Scy } 7859255332Scy break; 7860255332Scy 7861255332Scy case IPF_EXP_IP6_ADDR : 7862255332Scy for (i = 0; !e && i < x[3]; i++) { 7863255332Scy if (nat->nat_v[0] == 6) { 7864255332Scy e |= IP6_MASKEQ(&nat->nat_osrc6, 7865255332Scy x + i + 7, 7866255332Scy x + i + 3); 7867255332Scy } 7868255332Scy if (nat->nat_v[0] == 6) { 7869255332Scy e |= IP6_MASKEQ(&nat->nat_odst6, 7870255332Scy x + i + 7, 7871255332Scy x + i + 3); 7872255332Scy } 7873255332Scy if (nat->nat_v[1] == 6) { 7874255332Scy e |= IP6_MASKEQ(&nat->nat_nsrc6, 7875255332Scy x + i + 7, 7876255332Scy x + i + 3); 7877255332Scy } 7878255332Scy if (nat->nat_v[1] == 6) { 7879255332Scy e |= IP6_MASKEQ(&nat->nat_ndst6, 7880255332Scy x + i + 7, 7881255332Scy x + i + 3); 7882255332Scy } 7883255332Scy } 7884255332Scy break; 7885255332Scy#endif 7886255332Scy 7887255332Scy case IPF_EXP_UDP_PORT : 7888255332Scy case IPF_EXP_TCP_PORT : 7889255332Scy for (i = 0; !e && i < x[2]; i++) { 7890255332Scy e |= (nat->nat_nsport == x[i + 3]) || 7891255332Scy (nat->nat_ndport == x[i + 3]); 7892255332Scy } 7893255332Scy break; 7894255332Scy 7895255332Scy case IPF_EXP_UDP_SPORT : 7896255332Scy case IPF_EXP_TCP_SPORT : 7897255332Scy for (i = 0; !e && i < x[2]; i++) { 7898255332Scy e |= (nat->nat_nsport == x[i + 3]); 7899255332Scy } 7900255332Scy break; 7901255332Scy 7902255332Scy case IPF_EXP_UDP_DPORT : 7903255332Scy case IPF_EXP_TCP_DPORT : 7904255332Scy for (i = 0; !e && i < x[2]; i++) { 7905255332Scy e |= (nat->nat_ndport == x[i + 3]); 7906255332Scy } 7907255332Scy break; 7908255332Scy 7909255332Scy case IPF_EXP_TCP_STATE : 7910255332Scy for (i = 0; !e && i < x[2]; i++) { 7911255332Scy e |= (nat->nat_tcpstate[0] == x[i + 3]) || 7912255332Scy (nat->nat_tcpstate[1] == x[i + 3]); 7913255332Scy } 7914255332Scy break; 7915255332Scy 7916255332Scy case IPF_EXP_IDLE_GT : 7917255332Scy e |= (ticks - nat->nat_touched > x[3]); 7918255332Scy break; 7919255332Scy } 7920255332Scy e ^= x[1]; 7921255332Scy 7922255332Scy if (!e) 7923255332Scy break; 7924255332Scy } 7925255332Scy 7926255332Scy return e; 7927255332Scy} 7928255332Scy 7929255332Scy 7930255332Scy/* ------------------------------------------------------------------------ */ 7931255332Scy/* Function: ipf_nat_gettable */ 7932172776Sdarrenr/* Returns: int - 0 = success, else error */ 7933255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7934255332Scy/* softn(I) - pointer to NAT context structure */ 7935255332Scy/* data(I) - pointer to ioctl data */ 7936172776Sdarrenr/* */ 7937172776Sdarrenr/* This function handles ioctl requests for tables of nat information. */ 7938172776Sdarrenr/* At present the only table it deals with is the hash bucket statistics. */ 7939172776Sdarrenr/* ------------------------------------------------------------------------ */ 7940255332Scystatic int 7941255332Scyipf_nat_gettable(softc, softn, data) 7942255332Scy ipf_main_softc_t *softc; 7943255332Scy ipf_nat_softc_t *softn; 7944255332Scy char *data; 7945172776Sdarrenr{ 7946172776Sdarrenr ipftable_t table; 7947172776Sdarrenr int error; 7948172776Sdarrenr 7949255332Scy error = ipf_inobj(softc, data, NULL, &table, IPFOBJ_GTABLE); 7950172776Sdarrenr if (error != 0) 7951172776Sdarrenr return error; 7952172776Sdarrenr 7953172776Sdarrenr switch (table.ita_type) 7954172776Sdarrenr { 7955172776Sdarrenr case IPFTABLE_BUCKETS_NATIN : 7956255332Scy error = COPYOUT(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, 7957255332Scy table.ita_table, 7958255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 7959172776Sdarrenr break; 7960172776Sdarrenr 7961172776Sdarrenr case IPFTABLE_BUCKETS_NATOUT : 7962255332Scy error = COPYOUT(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, 7963255332Scy table.ita_table, 7964255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 7965172776Sdarrenr break; 7966172776Sdarrenr 7967172776Sdarrenr default : 7968255332Scy IPFERROR(60058); 7969172776Sdarrenr return EINVAL; 7970172776Sdarrenr } 7971172776Sdarrenr 7972172776Sdarrenr if (error != 0) { 7973255332Scy IPFERROR(60059); 7974172776Sdarrenr error = EFAULT; 7975172776Sdarrenr } 7976172776Sdarrenr return error; 7977172776Sdarrenr} 7978255332Scy 7979255332Scy 7980255332Scy/* ------------------------------------------------------------------------ */ 7981255332Scy/* Function: ipf_nat_settimeout */ 7982255332Scy/* Returns: int - 0 = success, else failure */ 7983255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7984255332Scy/* t(I) - pointer to tunable */ 7985255332Scy/* p(I) - pointer to new tuning data */ 7986255332Scy/* */ 7987255332Scy/* Apply the timeout change to the NAT timeout queues. */ 7988255332Scy/* ------------------------------------------------------------------------ */ 7989255332Scyint 7990255332Scyipf_nat_settimeout(softc, t, p) 7991255332Scy struct ipf_main_softc_s *softc; 7992255332Scy ipftuneable_t *t; 7993255332Scy ipftuneval_t *p; 7994255332Scy{ 7995255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 7996255332Scy 7997255332Scy if (!strncmp(t->ipft_name, "tcp_", 4)) 7998255332Scy return ipf_settimeout_tcp(t, p, softn->ipf_nat_tcptq); 7999255332Scy 8000255332Scy if (!strcmp(t->ipft_name, "udp_timeout")) { 8001255332Scy ipf_apply_timeout(&softn->ipf_nat_udptq, p->ipftu_int); 8002255332Scy } else if (!strcmp(t->ipft_name, "udp_ack_timeout")) { 8003255332Scy ipf_apply_timeout(&softn->ipf_nat_udpacktq, p->ipftu_int); 8004255332Scy } else if (!strcmp(t->ipft_name, "icmp_timeout")) { 8005255332Scy ipf_apply_timeout(&softn->ipf_nat_icmptq, p->ipftu_int); 8006255332Scy } else if (!strcmp(t->ipft_name, "icmp_ack_timeout")) { 8007255332Scy ipf_apply_timeout(&softn->ipf_nat_icmpacktq, p->ipftu_int); 8008255332Scy } else if (!strcmp(t->ipft_name, "ip_timeout")) { 8009255332Scy ipf_apply_timeout(&softn->ipf_nat_iptq, p->ipftu_int); 8010255332Scy } else { 8011255332Scy IPFERROR(60062); 8012255332Scy return ESRCH; 8013255332Scy } 8014255332Scy return 0; 8015255332Scy} 8016255332Scy 8017255332Scy 8018255332Scy/* ------------------------------------------------------------------------ */ 8019255332Scy/* Function: ipf_nat_rehash */ 8020255332Scy/* Returns: int - 0 = success, else failure */ 8021255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8022255332Scy/* t(I) - pointer to tunable */ 8023255332Scy/* p(I) - pointer to new tuning data */ 8024255332Scy/* */ 8025255332Scy/* To change the size of the basic NAT table, we need to first allocate the */ 8026255332Scy/* new tables (lest it fails and we've got nowhere to store all of the NAT */ 8027255332Scy/* sessions currently active) and then walk through the entire list and */ 8028255332Scy/* insert them into the table. There are two tables here: an inbound one */ 8029255332Scy/* and an outbound one. Each NAT entry goes into each table once. */ 8030255332Scy/* ------------------------------------------------------------------------ */ 8031255332Scyint 8032255332Scyipf_nat_rehash(softc, t, p) 8033255332Scy ipf_main_softc_t *softc; 8034255332Scy ipftuneable_t *t; 8035255332Scy ipftuneval_t *p; 8036255332Scy{ 8037255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 8038255332Scy nat_t **newtab[2], *nat, **natp; 8039255332Scy u_int *bucketlens[2]; 8040255332Scy u_int maxbucket; 8041255332Scy u_int newsize; 8042255332Scy int error; 8043255332Scy u_int hv; 8044255332Scy int i; 8045255332Scy 8046255332Scy newsize = p->ipftu_int; 8047255332Scy /* 8048255332Scy * In case there is nothing to do... 8049255332Scy */ 8050255332Scy if (newsize == softn->ipf_nat_table_sz) 8051255332Scy return 0; 8052255332Scy 8053255332Scy newtab[0] = NULL; 8054255332Scy newtab[1] = NULL; 8055255332Scy bucketlens[0] = NULL; 8056255332Scy bucketlens[1] = NULL; 8057255332Scy /* 8058255332Scy * 4 tables depend on the NAT table size: the inbound looking table, 8059255332Scy * the outbound lookup table and the hash chain length for each. 8060255332Scy */ 8061255332Scy KMALLOCS(newtab[0], nat_t **, newsize * sizeof(nat_t *)); 8062288242Sbz if (newtab[0] == NULL) { 8063255332Scy error = 60063; 8064255332Scy goto badrehash; 8065255332Scy } 8066255332Scy 8067255332Scy KMALLOCS(newtab[1], nat_t **, newsize * sizeof(nat_t *)); 8068288242Sbz if (newtab[1] == NULL) { 8069255332Scy error = 60064; 8070255332Scy goto badrehash; 8071255332Scy } 8072255332Scy 8073255332Scy KMALLOCS(bucketlens[0], u_int *, newsize * sizeof(u_int)); 8074255332Scy if (bucketlens[0] == NULL) { 8075255332Scy error = 60065; 8076255332Scy goto badrehash; 8077255332Scy } 8078255332Scy 8079255332Scy KMALLOCS(bucketlens[1], u_int *, newsize * sizeof(u_int)); 8080255332Scy if (bucketlens[1] == NULL) { 8081255332Scy error = 60066; 8082255332Scy goto badrehash; 8083255332Scy } 8084255332Scy 8085255332Scy /* 8086255332Scy * Recalculate the maximum length based on the new size. 8087255332Scy */ 8088255332Scy for (maxbucket = 0, i = newsize; i > 0; i >>= 1) 8089255332Scy maxbucket++; 8090255332Scy maxbucket *= 2; 8091255332Scy 8092255332Scy bzero((char *)newtab[0], newsize * sizeof(nat_t *)); 8093255332Scy bzero((char *)newtab[1], newsize * sizeof(nat_t *)); 8094255332Scy bzero((char *)bucketlens[0], newsize * sizeof(u_int)); 8095255332Scy bzero((char *)bucketlens[1], newsize * sizeof(u_int)); 8096255332Scy 8097255332Scy WRITE_ENTER(&softc->ipf_nat); 8098255332Scy 8099255332Scy if (softn->ipf_nat_table[0] != NULL) { 8100255332Scy KFREES(softn->ipf_nat_table[0], 8101255332Scy softn->ipf_nat_table_sz * 8102255332Scy sizeof(*softn->ipf_nat_table[0])); 8103255332Scy } 8104255332Scy softn->ipf_nat_table[0] = newtab[0]; 8105255332Scy 8106255332Scy if (softn->ipf_nat_table[1] != NULL) { 8107255332Scy KFREES(softn->ipf_nat_table[1], 8108255332Scy softn->ipf_nat_table_sz * 8109255332Scy sizeof(*softn->ipf_nat_table[1])); 8110255332Scy } 8111255332Scy softn->ipf_nat_table[1] = newtab[1]; 8112255332Scy 8113255332Scy if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) { 8114255332Scy KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, 8115255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 8116255332Scy } 8117255332Scy softn->ipf_nat_stats.ns_side[0].ns_bucketlen = bucketlens[0]; 8118255332Scy 8119255332Scy if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) { 8120255332Scy KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, 8121255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 8122255332Scy } 8123255332Scy softn->ipf_nat_stats.ns_side[1].ns_bucketlen = bucketlens[1]; 8124255332Scy 8125255332Scy#ifdef USE_INET6 8126255332Scy if (softn->ipf_nat_stats.ns_side6[0].ns_bucketlen != NULL) { 8127255332Scy KFREES(softn->ipf_nat_stats.ns_side6[0].ns_bucketlen, 8128255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 8129255332Scy } 8130255332Scy softn->ipf_nat_stats.ns_side6[0].ns_bucketlen = bucketlens[0]; 8131255332Scy 8132255332Scy if (softn->ipf_nat_stats.ns_side6[1].ns_bucketlen != NULL) { 8133255332Scy KFREES(softn->ipf_nat_stats.ns_side6[1].ns_bucketlen, 8134255332Scy softn->ipf_nat_table_sz * sizeof(u_int)); 8135255332Scy } 8136255332Scy softn->ipf_nat_stats.ns_side6[1].ns_bucketlen = bucketlens[1]; 8137255332Scy#endif 8138255332Scy 8139255332Scy softn->ipf_nat_maxbucket = maxbucket; 8140255332Scy softn->ipf_nat_table_sz = newsize; 8141255332Scy /* 8142255332Scy * Walk through the entire list of NAT table entries and put them 8143255332Scy * in the new NAT table, somewhere. Because we have a new table, 8144255332Scy * we need to restart the counter of how many chains are in use. 8145255332Scy */ 8146255332Scy softn->ipf_nat_stats.ns_side[0].ns_inuse = 0; 8147255332Scy softn->ipf_nat_stats.ns_side[1].ns_inuse = 0; 8148255332Scy#ifdef USE_INET6 8149255332Scy softn->ipf_nat_stats.ns_side6[0].ns_inuse = 0; 8150255332Scy softn->ipf_nat_stats.ns_side6[1].ns_inuse = 0; 8151255332Scy#endif 8152255332Scy 8153255332Scy for (nat = softn->ipf_nat_instances; nat != NULL; nat = nat->nat_next) { 8154255332Scy nat->nat_hnext[0] = NULL; 8155255332Scy nat->nat_phnext[0] = NULL; 8156255332Scy hv = nat->nat_hv[0] % softn->ipf_nat_table_sz; 8157255332Scy 8158255332Scy natp = &softn->ipf_nat_table[0][hv]; 8159255332Scy if (*natp) { 8160255332Scy (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; 8161255332Scy } else { 8162255332Scy NBUMPSIDE(0, ns_inuse); 8163255332Scy } 8164255332Scy nat->nat_phnext[0] = natp; 8165255332Scy nat->nat_hnext[0] = *natp; 8166255332Scy *natp = nat; 8167255332Scy NBUMPSIDE(0, ns_bucketlen[hv]); 8168255332Scy 8169255332Scy nat->nat_hnext[1] = NULL; 8170255332Scy nat->nat_phnext[1] = NULL; 8171255332Scy hv = nat->nat_hv[1] % softn->ipf_nat_table_sz; 8172255332Scy 8173255332Scy natp = &softn->ipf_nat_table[1][hv]; 8174255332Scy if (*natp) { 8175255332Scy (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; 8176255332Scy } else { 8177255332Scy NBUMPSIDE(1, ns_inuse); 8178255332Scy } 8179255332Scy nat->nat_phnext[1] = natp; 8180255332Scy nat->nat_hnext[1] = *natp; 8181255332Scy *natp = nat; 8182255332Scy NBUMPSIDE(1, ns_bucketlen[hv]); 8183255332Scy } 8184255332Scy RWLOCK_EXIT(&softc->ipf_nat); 8185255332Scy 8186255332Scy return 0; 8187255332Scy 8188255332Scybadrehash: 8189255332Scy if (bucketlens[1] != NULL) { 8190255332Scy KFREES(bucketlens[0], newsize * sizeof(u_int)); 8191255332Scy } 8192255332Scy if (bucketlens[0] != NULL) { 8193255332Scy KFREES(bucketlens[0], newsize * sizeof(u_int)); 8194255332Scy } 8195255332Scy if (newtab[0] != NULL) { 8196255332Scy KFREES(newtab[0], newsize * sizeof(nat_t *)); 8197255332Scy } 8198255332Scy if (newtab[1] != NULL) { 8199255332Scy KFREES(newtab[1], newsize * sizeof(nat_t *)); 8200255332Scy } 8201255332Scy IPFERROR(error); 8202255332Scy return ENOMEM; 8203255332Scy} 8204255332Scy 8205255332Scy 8206255332Scy/* ------------------------------------------------------------------------ */ 8207255332Scy/* Function: ipf_nat_rehash_rules */ 8208255332Scy/* Returns: int - 0 = success, else failure */ 8209255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8210255332Scy/* t(I) - pointer to tunable */ 8211255332Scy/* p(I) - pointer to new tuning data */ 8212255332Scy/* */ 8213255332Scy/* All of the NAT rules hang off of a hash table that is searched with a */ 8214255332Scy/* hash on address after the netmask is applied. There is a different table*/ 8215255332Scy/* for both inbound rules (rdr) and outbound (map.) The resizing will only */ 8216255332Scy/* affect one of these two tables. */ 8217255332Scy/* ------------------------------------------------------------------------ */ 8218255332Scyint 8219255332Scyipf_nat_rehash_rules(softc, t, p) 8220255332Scy ipf_main_softc_t *softc; 8221255332Scy ipftuneable_t *t; 8222255332Scy ipftuneval_t *p; 8223255332Scy{ 8224255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 8225255332Scy ipnat_t **newtab, *np, ***old, **npp; 8226255332Scy u_int newsize; 8227255332Scy u_int mask; 8228255332Scy u_int hv; 8229255332Scy 8230255332Scy newsize = p->ipftu_int; 8231255332Scy /* 8232255332Scy * In case there is nothing to do... 8233255332Scy */ 8234255332Scy if (newsize == *t->ipft_pint) 8235255332Scy return 0; 8236255332Scy 8237255332Scy /* 8238255332Scy * All inbound rules have the NAT_REDIRECT bit set in in_redir and 8239255332Scy * all outbound rules have either NAT_MAP or MAT_MAPBLK set. 8240255332Scy * This if statement allows for some more generic code to be below, 8241255332Scy * rather than two huge gobs of code that almost do the same thing. 8242255332Scy */ 8243255332Scy if (t->ipft_pint == &softn->ipf_nat_rdrrules_sz) { 8244255332Scy old = &softn->ipf_nat_rdr_rules; 8245255332Scy mask = NAT_REDIRECT; 8246255332Scy } else { 8247255332Scy old = &softn->ipf_nat_map_rules; 8248255332Scy mask = NAT_MAP|NAT_MAPBLK; 8249255332Scy } 8250255332Scy 8251255332Scy KMALLOCS(newtab, ipnat_t **, newsize * sizeof(ipnat_t *)); 8252255332Scy if (newtab == NULL) { 8253255332Scy IPFERROR(60067); 8254255332Scy return ENOMEM; 8255255332Scy } 8256255332Scy 8257255332Scy bzero((char *)newtab, newsize * sizeof(ipnat_t *)); 8258255332Scy 8259255332Scy WRITE_ENTER(&softc->ipf_nat); 8260255332Scy 8261255332Scy if (*old != NULL) { 8262255332Scy KFREES(*old, *t->ipft_pint * sizeof(ipnat_t **)); 8263255332Scy } 8264255332Scy *old = newtab; 8265255332Scy *t->ipft_pint = newsize; 8266255332Scy 8267255332Scy for (np = softn->ipf_nat_list; np != NULL; np = np->in_next) { 8268255332Scy if ((np->in_redir & mask) == 0) 8269255332Scy continue; 8270255332Scy 8271255332Scy if (np->in_redir & NAT_REDIRECT) { 8272255332Scy np->in_rnext = NULL; 8273255332Scy hv = np->in_hv[0] % newsize; 8274255332Scy for (npp = newtab + hv; *npp != NULL; ) 8275255332Scy npp = &(*npp)->in_rnext; 8276255332Scy np->in_prnext = npp; 8277255332Scy *npp = np; 8278255332Scy } 8279255332Scy if (np->in_redir & NAT_MAP) { 8280255332Scy np->in_mnext = NULL; 8281255332Scy hv = np->in_hv[1] % newsize; 8282255332Scy for (npp = newtab + hv; *npp != NULL; ) 8283255332Scy npp = &(*npp)->in_mnext; 8284255332Scy np->in_pmnext = npp; 8285255332Scy *npp = np; 8286255332Scy } 8287255332Scy 8288255332Scy } 8289255332Scy RWLOCK_EXIT(&softc->ipf_nat); 8290255332Scy 8291255332Scy return 0; 8292255332Scy} 8293255332Scy 8294255332Scy 8295255332Scy/* ------------------------------------------------------------------------ */ 8296255332Scy/* Function: ipf_nat_hostmap_rehash */ 8297255332Scy/* Returns: int - 0 = success, else failure */ 8298255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8299255332Scy/* t(I) - pointer to tunable */ 8300255332Scy/* p(I) - pointer to new tuning data */ 8301255332Scy/* */ 8302255332Scy/* Allocate and populate a new hash table that will contain a reference to */ 8303255332Scy/* all of the active IP# translations currently in place. */ 8304255332Scy/* ------------------------------------------------------------------------ */ 8305255332Scyint 8306255332Scyipf_nat_hostmap_rehash(softc, t, p) 8307255332Scy ipf_main_softc_t *softc; 8308255332Scy ipftuneable_t *t; 8309255332Scy ipftuneval_t *p; 8310255332Scy{ 8311255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 8312255332Scy hostmap_t *hm, **newtab; 8313255332Scy u_int newsize; 8314255332Scy u_int hv; 8315255332Scy 8316255332Scy newsize = p->ipftu_int; 8317255332Scy /* 8318255332Scy * In case there is nothing to do... 8319255332Scy */ 8320255332Scy if (newsize == *t->ipft_pint) 8321255332Scy return 0; 8322255332Scy 8323255332Scy KMALLOCS(newtab, hostmap_t **, newsize * sizeof(hostmap_t *)); 8324255332Scy if (newtab == NULL) { 8325255332Scy IPFERROR(60068); 8326255332Scy return ENOMEM; 8327255332Scy } 8328255332Scy 8329255332Scy bzero((char *)newtab, newsize * sizeof(hostmap_t *)); 8330255332Scy 8331255332Scy WRITE_ENTER(&softc->ipf_nat); 8332255332Scy if (softn->ipf_hm_maptable != NULL) { 8333255332Scy KFREES(softn->ipf_hm_maptable, 8334255332Scy softn->ipf_nat_hostmap_sz * sizeof(hostmap_t *)); 8335255332Scy } 8336255332Scy softn->ipf_hm_maptable = newtab; 8337255332Scy softn->ipf_nat_hostmap_sz = newsize; 8338255332Scy 8339255332Scy for (hm = softn->ipf_hm_maplist; hm != NULL; hm = hm->hm_next) { 8340255332Scy hv = hm->hm_hv % softn->ipf_nat_hostmap_sz; 8341255332Scy hm->hm_hnext = softn->ipf_hm_maptable[hv]; 8342255332Scy hm->hm_phnext = softn->ipf_hm_maptable + hv; 8343255332Scy if (softn->ipf_hm_maptable[hv] != NULL) 8344255332Scy softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; 8345255332Scy softn->ipf_hm_maptable[hv] = hm; 8346255332Scy } 8347255332Scy RWLOCK_EXIT(&softc->ipf_nat); 8348255332Scy 8349255332Scy return 0; 8350255332Scy} 8351255332Scy 8352255332Scy 8353255332Scy/* ------------------------------------------------------------------------ */ 8354255332Scy/* Function: ipf_nat_add_tq */ 8355255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8356255332Scy/* */ 8357255332Scy/* ------------------------------------------------------------------------ */ 8358255332Scyipftq_t * 8359255332Scyipf_nat_add_tq(softc, ttl) 8360255332Scy ipf_main_softc_t *softc; 8361255332Scy int ttl; 8362255332Scy{ 8363255332Scy ipf_nat_softc_t *softs = softc->ipf_nat_soft; 8364255332Scy 8365255332Scy return ipf_addtimeoutqueue(softc, &softs->ipf_nat_utqe, ttl); 8366255332Scy} 8367255332Scy 8368255332Scy/* ------------------------------------------------------------------------ */ 8369255332Scy/* Function: ipf_nat_uncreate */ 8370255332Scy/* Returns: Nil */ 8371255332Scy/* Parameters: fin(I) - pointer to packet information */ 8372255332Scy/* */ 8373255332Scy/* This function is used to remove a NAT entry from the NAT table when we */ 8374255332Scy/* decide that the create was actually in error. It is thus assumed that */ 8375255332Scy/* fin_flx will have both FI_NATED and FI_NATNEW set. Because we're dealing */ 8376255332Scy/* with the translated packet (not the original), we have to reverse the */ 8377255332Scy/* lookup. Although doing the lookup is expensive (relatively speaking), it */ 8378255332Scy/* is not anticipated that this will be a frequent occurance for normal */ 8379255332Scy/* traffic patterns. */ 8380255332Scy/* ------------------------------------------------------------------------ */ 8381255332Scyvoid 8382255332Scyipf_nat_uncreate(fin) 8383255332Scy fr_info_t *fin; 8384255332Scy{ 8385255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 8386255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 8387255332Scy int nflags; 8388255332Scy nat_t *nat; 8389255332Scy 8390255332Scy switch (fin->fin_p) 8391255332Scy { 8392255332Scy case IPPROTO_TCP : 8393255332Scy nflags = IPN_TCP; 8394255332Scy break; 8395255332Scy case IPPROTO_UDP : 8396255332Scy nflags = IPN_UDP; 8397255332Scy break; 8398255332Scy default : 8399255332Scy nflags = 0; 8400255332Scy break; 8401255332Scy } 8402255332Scy 8403255332Scy WRITE_ENTER(&softc->ipf_nat); 8404255332Scy 8405255332Scy if (fin->fin_out == 0) { 8406255332Scy nat = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p, 8407255332Scy fin->fin_dst, fin->fin_src); 8408255332Scy } else { 8409255332Scy nat = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p, 8410255332Scy fin->fin_src, fin->fin_dst); 8411255332Scy } 8412255332Scy 8413255332Scy if (nat != NULL) { 8414255332Scy NBUMPSIDE(fin->fin_out, ns_uncreate[0]); 8415255332Scy ipf_nat_delete(softc, nat, NL_DESTROY); 8416255332Scy } else { 8417255332Scy NBUMPSIDE(fin->fin_out, ns_uncreate[1]); 8418255332Scy } 8419255332Scy 8420255332Scy RWLOCK_EXIT(&softc->ipf_nat); 8421255332Scy} 8422255332Scy 8423255332Scy 8424255332Scy/* ------------------------------------------------------------------------ */ 8425255332Scy/* Function: ipf_nat_cmp_rules */ 8426255332Scy/* Returns: int - 0 == success, else rules do not match. */ 8427255332Scy/* Parameters: n1(I) - first rule to compare */ 8428255332Scy/* n2(I) - first rule to compare */ 8429255332Scy/* */ 8430255332Scy/* Compare two rules using pointers to each rule. A straight bcmp will not */ 8431255332Scy/* work as some fields (such as in_dst, in_pkts) actually do change once */ 8432255332Scy/* the rule has been loaded into the kernel. Whilst this function returns */ 8433255332Scy/* various non-zero returns, they're strictly to aid in debugging. Use of */ 8434255332Scy/* this function should simply care if the result is zero or not. */ 8435255332Scy/* ------------------------------------------------------------------------ */ 8436255332Scystatic int 8437255332Scyipf_nat_cmp_rules(n1, n2) 8438255332Scy ipnat_t *n1, *n2; 8439255332Scy{ 8440255332Scy if (n1->in_size != n2->in_size) 8441255332Scy return 1; 8442255332Scy 8443255332Scy if (bcmp((char *)&n1->in_v, (char *)&n2->in_v, 8444255332Scy offsetof(ipnat_t, in_ndst) - offsetof(ipnat_t, in_v)) != 0) 8445255332Scy return 2; 8446255332Scy 8447255332Scy if (bcmp((char *)&n1->in_tuc, (char *)&n2->in_tuc, 8448255332Scy n1->in_size - offsetof(ipnat_t, in_tuc)) != 0) 8449255332Scy return 3; 8450255332Scy if (n1->in_ndst.na_atype != n2->in_ndst.na_atype) 8451255332Scy return 5; 8452255332Scy if (n1->in_ndst.na_function != n2->in_ndst.na_function) 8453255332Scy return 6; 8454255332Scy if (bcmp((char *)&n1->in_ndst.na_addr, (char *)&n2->in_ndst.na_addr, 8455255332Scy sizeof(n1->in_ndst.na_addr))) 8456255332Scy return 7; 8457255332Scy if (n1->in_nsrc.na_atype != n2->in_nsrc.na_atype) 8458255332Scy return 8; 8459255332Scy if (n1->in_nsrc.na_function != n2->in_nsrc.na_function) 8460255332Scy return 9; 8461255332Scy if (bcmp((char *)&n1->in_nsrc.na_addr, (char *)&n2->in_nsrc.na_addr, 8462255332Scy sizeof(n1->in_nsrc.na_addr))) 8463255332Scy return 10; 8464255332Scy if (n1->in_odst.na_atype != n2->in_odst.na_atype) 8465255332Scy return 11; 8466255332Scy if (n1->in_odst.na_function != n2->in_odst.na_function) 8467255332Scy return 12; 8468255332Scy if (bcmp((char *)&n1->in_odst.na_addr, (char *)&n2->in_odst.na_addr, 8469255332Scy sizeof(n1->in_odst.na_addr))) 8470255332Scy return 13; 8471255332Scy if (n1->in_osrc.na_atype != n2->in_osrc.na_atype) 8472255332Scy return 14; 8473255332Scy if (n1->in_osrc.na_function != n2->in_osrc.na_function) 8474255332Scy return 15; 8475255332Scy if (bcmp((char *)&n1->in_osrc.na_addr, (char *)&n2->in_osrc.na_addr, 8476255332Scy sizeof(n1->in_osrc.na_addr))) 8477255332Scy return 16; 8478255332Scy return 0; 8479255332Scy} 8480255332Scy 8481255332Scy 8482255332Scy/* ------------------------------------------------------------------------ */ 8483255332Scy/* Function: ipf_nat_rule_init */ 8484255332Scy/* Returns: int - 0 == success, else rules do not match. */ 8485255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8486255332Scy/* softn(I) - pointer to NAT context structure */ 8487255332Scy/* n(I) - first rule to compare */ 8488255332Scy/* */ 8489255332Scy/* ------------------------------------------------------------------------ */ 8490255332Scystatic int 8491255332Scyipf_nat_rule_init(softc, softn, n) 8492255332Scy ipf_main_softc_t *softc; 8493255332Scy ipf_nat_softc_t *softn; 8494255332Scy ipnat_t *n; 8495255332Scy{ 8496255332Scy int error = 0; 8497255332Scy 8498255332Scy if ((n->in_flags & IPN_SIPRANGE) != 0) 8499255332Scy n->in_nsrcatype = FRI_RANGE; 8500255332Scy 8501255332Scy if ((n->in_flags & IPN_DIPRANGE) != 0) 8502255332Scy n->in_ndstatype = FRI_RANGE; 8503255332Scy 8504255332Scy if ((n->in_flags & IPN_SPLIT) != 0) 8505255332Scy n->in_ndstatype = FRI_SPLIT; 8506255332Scy 8507255332Scy if ((n->in_redir & (NAT_MAP|NAT_REWRITE|NAT_DIVERTUDP)) != 0) 8508255332Scy n->in_spnext = n->in_spmin; 8509255332Scy 8510255332Scy if ((n->in_redir & (NAT_REWRITE|NAT_DIVERTUDP)) != 0) { 8511255332Scy n->in_dpnext = n->in_dpmin; 8512255332Scy } else if (n->in_redir == NAT_REDIRECT) { 8513255332Scy n->in_dpnext = n->in_dpmin; 8514255332Scy } 8515255332Scy 8516255332Scy n->in_stepnext = 0; 8517255332Scy 8518255332Scy switch (n->in_v[0]) 8519255332Scy { 8520255332Scy case 4 : 8521255332Scy error = ipf_nat_ruleaddrinit(softc, softn, n); 8522255332Scy if (error != 0) 8523255332Scy return error; 8524255332Scy break; 8525255332Scy#ifdef USE_INET6 8526255332Scy case 6 : 8527255332Scy error = ipf_nat6_ruleaddrinit(softc, softn, n); 8528255332Scy if (error != 0) 8529255332Scy return error; 8530255332Scy break; 8531255332Scy#endif 8532255332Scy default : 8533255332Scy break; 8534255332Scy } 8535255332Scy 8536255332Scy if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) { 8537255332Scy /* 8538255332Scy * Prerecord whether or not the destination of the divert 8539255332Scy * is local or not to the interface the packet is going 8540255332Scy * to be sent out. 8541255332Scy */ 8542255332Scy n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1], 8543255332Scy n->in_ifps[1], &n->in_ndstip6); 8544255332Scy } 8545255332Scy 8546255332Scy return error; 8547255332Scy} 8548255332Scy 8549255332Scy 8550255332Scy/* ------------------------------------------------------------------------ */ 8551255332Scy/* Function: ipf_nat_rule_fini */ 8552255332Scy/* Returns: int - 0 == success, else rules do not match. */ 8553255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8554255332Scy/* n(I) - rule to work on */ 8555255332Scy/* */ 8556255332Scy/* This function is used to release any objects that were referenced during */ 8557255332Scy/* the rule initialisation. This is useful both when free'ing the rule and */ 8558255332Scy/* when handling ioctls that need to initialise these fields but not */ 8559255332Scy/* actually use them after the ioctl processing has finished. */ 8560255332Scy/* ------------------------------------------------------------------------ */ 8561255332Scystatic void 8562255332Scyipf_nat_rule_fini(softc, n) 8563255332Scy ipf_main_softc_t *softc; 8564255332Scy ipnat_t *n; 8565255332Scy{ 8566255332Scy if (n->in_odst.na_atype == FRI_LOOKUP && n->in_odst.na_ptr != NULL) 8567255332Scy ipf_lookup_deref(softc, n->in_odst.na_type, n->in_odst.na_ptr); 8568255332Scy 8569255332Scy if (n->in_osrc.na_atype == FRI_LOOKUP && n->in_osrc.na_ptr != NULL) 8570255332Scy ipf_lookup_deref(softc, n->in_osrc.na_type, n->in_osrc.na_ptr); 8571255332Scy 8572255332Scy if (n->in_ndst.na_atype == FRI_LOOKUP && n->in_ndst.na_ptr != NULL) 8573255332Scy ipf_lookup_deref(softc, n->in_ndst.na_type, n->in_ndst.na_ptr); 8574255332Scy 8575255332Scy if (n->in_nsrc.na_atype == FRI_LOOKUP && n->in_nsrc.na_ptr != NULL) 8576255332Scy ipf_lookup_deref(softc, n->in_nsrc.na_type, n->in_nsrc.na_ptr); 8577255332Scy 8578255332Scy if (n->in_divmp != NULL) 8579255332Scy FREE_MB_T(n->in_divmp); 8580255332Scy} 8581