version 1.6, 2011/02/02 02:20:25 |
version 1.6.6.5, 2014/05/22 11:41:09 |
|
|
/* $NetBSD$ */ |
/* $NetBSD$ */ |
|
|
/*- |
/*- |
* Copyright (c) 2010-2011 The NetBSD Foundation, Inc. |
* Copyright (c) 2014 Mindaugas Rasiukevicius <rmind at netbsd org> |
|
* Copyright (c) 2010-2013 The NetBSD Foundation, Inc. |
* All rights reserved. |
* All rights reserved. |
* |
* |
* This material is based upon work partially supported by The |
* This material is based upon work partially supported by The |
|
|
*/ |
*/ |
|
|
/* |
/* |
* NPF network address port translation (NAPT). |
* NPF network address port translation (NAPT) and other forms of NAT. |
* Described in RFC 2663, RFC 3022. Commonly just "NAT". |
* Described in RFC 2663, RFC 3022, etc. |
* |
* |
* Overview |
* Overview |
* |
* |
|
|
* |
* |
* There are two types of translation: outbound (NPF_NATOUT) and |
* There are two types of translation: outbound (NPF_NATOUT) and |
* inbound (NPF_NATIN). It should not be confused with connection |
* inbound (NPF_NATIN). It should not be confused with connection |
* direction. |
* direction. See npf_nat_which() for the description of how the |
* |
* addresses are rewritten. |
* Outbound NAT rewrites: |
|
* - Source on "forwards" stream. |
|
* - Destination on "backwards" stream. |
|
* Inbound NAT rewrites: |
|
* - Destination on "forwards" stream. |
|
* - Source on "backwards" stream. |
|
* |
* |
* It should be noted that bi-directional NAT is a combined outbound |
* It should be noted that bi-directional NAT is a combined outbound |
* and inbound translation, therefore constructed as two policies. |
* and inbound translation, therefore constructed as two policies. |
|
|
__KERNEL_RCSID(0, "$NetBSD$"); |
__KERNEL_RCSID(0, "$NetBSD$"); |
|
|
#include <sys/param.h> |
#include <sys/param.h> |
#include <sys/kernel.h> |
#include <sys/types.h> |
|
|
#include <sys/atomic.h> |
#include <sys/atomic.h> |
#include <sys/bitops.h> |
#include <sys/bitops.h> |
Line 87 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 82 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <sys/kmem.h> |
#include <sys/kmem.h> |
#include <sys/mutex.h> |
#include <sys/mutex.h> |
#include <sys/pool.h> |
#include <sys/pool.h> |
|
#include <sys/proc.h> |
|
#include <sys/cprng.h> |
|
|
#include <net/pfil.h> |
#include <net/pfil.h> |
#include <netinet/in.h> |
#include <netinet/in.h> |
|
|
Line 103 typedef struct { |
|
Line 101 typedef struct { |
|
/* Portmap range: [ 1024 .. 65535 ] */ |
/* Portmap range: [ 1024 .. 65535 ] */ |
#define PORTMAP_FIRST (1024) |
#define PORTMAP_FIRST (1024) |
#define PORTMAP_SIZE ((65536 - PORTMAP_FIRST) / 32) |
#define PORTMAP_SIZE ((65536 - PORTMAP_FIRST) / 32) |
#define PORTMAP_FILLED ((uint32_t)~0) |
#define PORTMAP_FILLED ((uint32_t)~0U) |
#define PORTMAP_MASK (31) |
#define PORTMAP_MASK (31) |
#define PORTMAP_SHIFT (5) |
#define PORTMAP_SHIFT (5) |
|
|
#define PORTMAP_MEM_SIZE \ |
#define PORTMAP_MEM_SIZE \ |
(sizeof(npf_portmap_t) + (PORTMAP_SIZE * sizeof(uint32_t))) |
(sizeof(npf_portmap_t) + (PORTMAP_SIZE * sizeof(uint32_t))) |
|
|
/* NAT policy structure. */ |
/* |
|
* NAT policy structure. |
|
*/ |
struct npf_natpolicy { |
struct npf_natpolicy { |
LIST_HEAD(, npf_nat) n_nat_list; |
LIST_HEAD(, npf_nat) n_nat_list; |
|
volatile u_int n_refcnt; |
kmutex_t n_lock; |
kmutex_t n_lock; |
kcondvar_t n_cv; |
kcondvar_t n_cv; |
npf_portmap_t * n_portmap; |
npf_portmap_t * n_portmap; |
|
/* NPF_NP_CMP_START */ |
int n_type; |
int n_type; |
u_int n_flags; |
u_int n_flags; |
size_t n_addr_sz; |
size_t n_addr_sz; |
npf_addr_t n_taddr; |
npf_addr_t n_taddr; |
|
npf_netmask_t n_tmask; |
in_port_t n_tport; |
in_port_t n_tport; |
|
u_int n_algo; |
|
union { |
|
uint16_t n_npt66_adj; |
|
}; |
}; |
}; |
|
|
#define NPF_NP_CMP_START offsetof(npf_natpolicy_t, n_type) |
#define NPF_NP_CMP_START offsetof(npf_natpolicy_t, n_type) |
#define NPF_NP_CMP_SIZE (sizeof(npf_natpolicy_t) - NPF_NP_CMP_START) |
#define NPF_NP_CMP_SIZE (sizeof(npf_natpolicy_t) - NPF_NP_CMP_START) |
|
|
/* NAT translation entry for a session. */ |
/* |
|
* NAT translation entry for a session. |
|
*/ |
struct npf_nat { |
struct npf_nat { |
/* Association (list entry and a link pointer) with NAT policy. */ |
/* Association (list entry and a link pointer) with NAT policy. */ |
LIST_ENTRY(npf_nat) nt_entry; |
LIST_ENTRY(npf_nat) nt_entry; |
Line 151 static pool_cache_t nat_cache __read_mo |
|
Line 160 static pool_cache_t nat_cache __read_mo |
|
void |
void |
npf_nat_sysinit(void) |
npf_nat_sysinit(void) |
{ |
{ |
|
|
nat_cache = pool_cache_init(sizeof(npf_nat_t), coherency_unit, |
nat_cache = pool_cache_init(sizeof(npf_nat_t), coherency_unit, |
0, 0, "npfnatpl", NULL, IPL_NET, NULL, NULL, NULL); |
0, 0, "npfnatpl", NULL, IPL_NET, NULL, NULL, NULL); |
KASSERT(nat_cache != NULL); |
KASSERT(nat_cache != NULL); |
Line 160 npf_nat_sysinit(void) |
|
Line 168 npf_nat_sysinit(void) |
|
void |
void |
npf_nat_sysfini(void) |
npf_nat_sysfini(void) |
{ |
{ |
|
/* All NAT policies should already be destroyed. */ |
/* NAT policies should already be destroyed. */ |
|
pool_cache_destroy(nat_cache); |
pool_cache_destroy(nat_cache); |
} |
} |
|
|
Line 169 npf_nat_sysfini(void) |
|
Line 176 npf_nat_sysfini(void) |
|
* npf_nat_newpolicy: create a new NAT policy. |
* npf_nat_newpolicy: create a new NAT policy. |
* |
* |
* => Shares portmap if policy is on existing translation address. |
* => Shares portmap if policy is on existing translation address. |
* => XXX: serialise at upper layer. |
|
*/ |
*/ |
npf_natpolicy_t * |
npf_natpolicy_t * |
npf_nat_newpolicy(prop_dictionary_t natdict, npf_ruleset_t *nrlset) |
npf_nat_newpolicy(prop_dictionary_t natdict, npf_ruleset_t *nrlset) |
Line 179 npf_nat_newpolicy(prop_dictionary_t natd |
|
Line 185 npf_nat_newpolicy(prop_dictionary_t natd |
|
npf_portmap_t *pm; |
npf_portmap_t *pm; |
|
|
np = kmem_zalloc(sizeof(npf_natpolicy_t), KM_SLEEP); |
np = kmem_zalloc(sizeof(npf_natpolicy_t), KM_SLEEP); |
mutex_init(&np->n_lock, MUTEX_DEFAULT, IPL_SOFTNET); |
|
cv_init(&np->n_cv, "npfnatcv"); |
|
LIST_INIT(&np->n_nat_list); |
|
|
|
/* Translation type and flags. */ |
/* Translation type and flags. */ |
prop_dictionary_get_int32(natdict, "type", &np->n_type); |
prop_dictionary_get_int32(natdict, "type", &np->n_type); |
prop_dictionary_get_uint32(natdict, "flags", &np->n_flags); |
prop_dictionary_get_uint32(natdict, "flags", &np->n_flags); |
KASSERT(np->n_type == NPF_NATIN || np->n_type == NPF_NATOUT); |
|
|
|
/* Translation IP. */ |
/* Should be exclusively either inbound or outbound NAT. */ |
|
if (((np->n_type == NPF_NATIN) ^ (np->n_type == NPF_NATOUT)) == 0) { |
|
goto err; |
|
} |
|
mutex_init(&np->n_lock, MUTEX_DEFAULT, IPL_SOFTNET); |
|
cv_init(&np->n_cv, "npfnatcv"); |
|
LIST_INIT(&np->n_nat_list); |
|
|
|
/* Translation IP, mask and port (if applicable). */ |
obj = prop_dictionary_get(natdict, "translation-ip"); |
obj = prop_dictionary_get(natdict, "translation-ip"); |
np->n_addr_sz = prop_data_size(obj); |
np->n_addr_sz = prop_data_size(obj); |
KASSERT(np->n_addr_sz > 0 && np->n_addr_sz <= sizeof(npf_addr_t)); |
if (np->n_addr_sz == 0 || np->n_addr_sz > sizeof(npf_addr_t)) { |
|
goto err; |
|
} |
memcpy(&np->n_taddr, prop_data_data_nocopy(obj), np->n_addr_sz); |
memcpy(&np->n_taddr, prop_data_data_nocopy(obj), np->n_addr_sz); |
|
prop_dictionary_get_uint8(natdict, "translation-mask", &np->n_tmask); |
/* Translation port (for redirect case). */ |
|
prop_dictionary_get_uint16(natdict, "translation-port", &np->n_tport); |
prop_dictionary_get_uint16(natdict, "translation-port", &np->n_tport); |
|
|
|
prop_dictionary_get_uint32(natdict, "translation-algo", &np->n_algo); |
|
switch (np->n_algo) { |
|
case NPF_ALGO_NPT66: |
|
prop_dictionary_get_uint16(natdict, "npt66-adjustment", |
|
&np->n_npt66_adj); |
|
break; |
|
default: |
|
if (np->n_tmask != NPF_NO_NETMASK) |
|
goto err; |
|
break; |
|
} |
|
|
/* Determine if port map is needed. */ |
/* Determine if port map is needed. */ |
np->n_portmap = NULL; |
np->n_portmap = NULL; |
if ((np->n_flags & NPF_NAT_PORTMAP) == 0) { |
if ((np->n_flags & NPF_NAT_PORTMAP) == 0) { |
Line 218 npf_nat_newpolicy(prop_dictionary_t natd |
|
Line 241 npf_nat_newpolicy(prop_dictionary_t natd |
|
KASSERT(np->n_portmap != NULL); |
KASSERT(np->n_portmap != NULL); |
} |
} |
return np; |
return np; |
|
err: |
|
kmem_free(np, sizeof(npf_natpolicy_t)); |
|
return NULL; |
} |
} |
|
|
/* |
/* |
Line 232 npf_nat_freepolicy(npf_natpolicy_t *np) |
|
Line 258 npf_nat_freepolicy(npf_natpolicy_t *np) |
|
npf_session_t *se; |
npf_session_t *se; |
npf_nat_t *nt; |
npf_nat_t *nt; |
|
|
/* De-associate all entries from the policy. */ |
/* |
|
* Disassociate all entries from the policy. At this point, |
|
* new entries can no longer be created for this policy. |
|
*/ |
mutex_enter(&np->n_lock); |
mutex_enter(&np->n_lock); |
LIST_FOREACH(nt, &np->n_nat_list, nt_entry) { |
LIST_FOREACH(nt, &np->n_nat_list, nt_entry) { |
se = nt->nt_session; /* XXXSMP */ |
se = nt->nt_session; |
if (se == NULL) { |
KASSERT(se != NULL); |
continue; |
|
} |
|
npf_session_expire(se); |
npf_session_expire(se); |
} |
} |
while (!LIST_EMPTY(&np->n_nat_list)) { |
while (!LIST_EMPTY(&np->n_nat_list)) { |
Line 246 npf_nat_freepolicy(npf_natpolicy_t *np) |
|
Line 273 npf_nat_freepolicy(npf_natpolicy_t *np) |
|
} |
} |
mutex_exit(&np->n_lock); |
mutex_exit(&np->n_lock); |
|
|
|
/* Kick the worker - all references should be going away. */ |
|
npf_worker_signal(); |
|
while (np->n_refcnt) { |
|
kpause("npfgcnat", false, 1, NULL); |
|
} |
|
KASSERT(LIST_EMPTY(&np->n_nat_list)); |
|
|
/* Destroy the port map, on last reference. */ |
/* Destroy the port map, on last reference. */ |
if (pm && --pm->p_refcnt == 0) { |
if (pm && --pm->p_refcnt == 0) { |
KASSERT((np->n_flags & NPF_NAT_PORTMAP) != 0); |
KASSERT((np->n_flags & NPF_NAT_PORTMAP) != 0); |
Line 256 npf_nat_freepolicy(npf_natpolicy_t *np) |
|
Line 290 npf_nat_freepolicy(npf_natpolicy_t *np) |
|
kmem_free(np, sizeof(npf_natpolicy_t)); |
kmem_free(np, sizeof(npf_natpolicy_t)); |
} |
} |
|
|
|
void |
|
npf_nat_freealg(npf_natpolicy_t *np, npf_alg_t *alg) |
|
{ |
|
npf_nat_t *nt; |
|
|
|
mutex_enter(&np->n_lock); |
|
LIST_FOREACH(nt, &np->n_nat_list, nt_entry) { |
|
if (nt->nt_alg != alg) { |
|
continue; |
|
} |
|
nt->nt_alg = NULL; |
|
} |
|
mutex_exit(&np->n_lock); |
|
} |
|
|
/* |
/* |
* npf_nat_matchpolicy: compare two NAT policies. |
* npf_nat_matchpolicy: compare two NAT policies. |
* |
* |
Line 295 npf_nat_sharepm(npf_natpolicy_t *np, npf |
|
Line 344 npf_nat_sharepm(npf_natpolicy_t *np, npf |
|
/* If NAT policy has an old port map - drop the reference. */ |
/* If NAT policy has an old port map - drop the reference. */ |
mpm = mnp->n_portmap; |
mpm = mnp->n_portmap; |
if (mpm) { |
if (mpm) { |
/* Note: in such case, we must not be a last reference. */ |
/* Note: at this point we cannot hold a last reference. */ |
KASSERT(mpm->p_refcnt > 1); |
KASSERT(mpm->p_refcnt > 1); |
mpm->p_refcnt--; |
mpm->p_refcnt--; |
} |
} |
Line 319 npf_nat_getport(npf_natpolicy_t *np) |
|
Line 368 npf_nat_getport(npf_natpolicy_t *np) |
|
u_int n = PORTMAP_SIZE, idx, bit; |
u_int n = PORTMAP_SIZE, idx, bit; |
uint32_t map, nmap; |
uint32_t map, nmap; |
|
|
idx = arc4random() % PORTMAP_SIZE; |
idx = cprng_fast32() % PORTMAP_SIZE; |
for (;;) { |
for (;;) { |
KASSERT(idx < PORTMAP_SIZE); |
KASSERT(idx < PORTMAP_SIZE); |
map = pm->p_bitmap[idx]; |
map = pm->p_bitmap[idx]; |
Line 387 npf_nat_putport(npf_natpolicy_t *np, in_ |
|
Line 436 npf_nat_putport(npf_natpolicy_t *np, in_ |
|
} |
} |
|
|
/* |
/* |
|
* npf_nat_which: tell which address (source or destination) should be |
|
* rewritten given the combination of the NAT type and flow direction. |
|
*/ |
|
static inline u_int |
|
npf_nat_which(const int type, bool forw) |
|
{ |
|
/* |
|
* Outbound NAT rewrites: |
|
* - Source (NPF_SRC) on "forwards" stream. |
|
* - Destination (NPF_DST) on "backwards" stream. |
|
* Inbound NAT is other way round. |
|
*/ |
|
if (type == NPF_NATOUT) { |
|
forw = !forw; |
|
} else { |
|
KASSERT(type == NPF_NATIN); |
|
} |
|
CTASSERT(NPF_SRC == 0 && NPF_DST == 1); |
|
KASSERT(forw == NPF_SRC || forw == NPF_DST); |
|
return (u_int)forw; |
|
} |
|
|
|
/* |
* npf_nat_inspect: inspect packet against NAT ruleset and return a policy. |
* npf_nat_inspect: inspect packet against NAT ruleset and return a policy. |
|
* |
|
* => Acquire a reference on the policy, if found. |
*/ |
*/ |
static npf_natpolicy_t * |
static npf_natpolicy_t * |
npf_nat_inspect(npf_cache_t *npc, nbuf_t *nbuf, ifnet_t *ifp, const int di) |
npf_nat_inspect(npf_cache_t *npc, nbuf_t *nbuf, const int di) |
{ |
{ |
npf_ruleset_t *rlset; |
int slock = npf_config_read_enter(); |
|
npf_ruleset_t *rlset = npf_config_natset(); |
npf_natpolicy_t *np; |
npf_natpolicy_t *np; |
npf_rule_t *rl; |
npf_rule_t *rl; |
|
|
npf_core_enter(); |
rl = npf_ruleset_inspect(npc, nbuf, rlset, di, NPF_LAYER_3); |
rlset = npf_core_natset(); |
|
rl = npf_ruleset_inspect(npc, nbuf, rlset, ifp, di, NPF_LAYER_3); |
|
if (rl == NULL) { |
if (rl == NULL) { |
|
npf_config_read_exit(slock); |
return NULL; |
return NULL; |
} |
} |
np = npf_rule_getnat(rl); |
np = npf_rule_getnat(rl); |
if (np == NULL) { |
atomic_inc_uint(&np->n_refcnt); |
npf_core_exit(); |
npf_config_read_exit(slock); |
return NULL; |
|
} |
|
return np; |
return np; |
} |
} |
|
|
Line 414 npf_nat_inspect(npf_cache_t *npc, nbuf_t |
|
Line 486 npf_nat_inspect(npf_cache_t *npc, nbuf_t |
|
* npf_nat_create: create a new NAT translation entry. |
* npf_nat_create: create a new NAT translation entry. |
*/ |
*/ |
static npf_nat_t * |
static npf_nat_t * |
npf_nat_create(npf_cache_t *npc, npf_natpolicy_t *np) |
npf_nat_create(npf_cache_t *npc, npf_natpolicy_t *np, npf_session_t *se) |
{ |
{ |
const int proto = npf_cache_ipproto(npc); |
const int proto = npc->npc_proto; |
npf_nat_t *nt; |
npf_nat_t *nt; |
|
|
KASSERT(npf_iscached(npc, NPC_IP46 | NPC_LAYER4)); |
KASSERT(npf_iscached(npc, NPC_IP46)); |
|
KASSERT(npf_iscached(npc, NPC_LAYER4)); |
|
|
/* New NAT association. */ |
/* Construct a new NAT entry and associate it with the session. */ |
nt = pool_cache_get(nat_cache, PR_NOWAIT); |
nt = pool_cache_get(nat_cache, PR_NOWAIT); |
if (nt == NULL){ |
if (nt == NULL){ |
return NULL; |
return NULL; |
} |
} |
npf_stats_inc(NPF_STAT_NAT_CREATE); |
npf_stats_inc(NPF_STAT_NAT_CREATE); |
nt->nt_natpolicy = np; |
nt->nt_natpolicy = np; |
nt->nt_session = NULL; |
nt->nt_session = se; |
nt->nt_alg = NULL; |
nt->nt_alg = NULL; |
|
|
mutex_enter(&np->n_lock); |
|
LIST_INSERT_HEAD(&np->n_nat_list, nt, nt_entry); |
|
mutex_exit(&np->n_lock); |
|
|
|
/* Save the original address which may be rewritten. */ |
/* Save the original address which may be rewritten. */ |
if (np->n_type == NPF_NATOUT) { |
if (np->n_type == NPF_NATOUT) { |
/* Source (local) for Outbound NAT. */ |
/* Outbound NAT: source (think internal) address. */ |
memcpy(&nt->nt_oaddr, npc->npc_srcip, npc->npc_ipsz); |
memcpy(&nt->nt_oaddr, npc->npc_ips[NPF_SRC], npc->npc_alen); |
} else { |
} else { |
/* Destination (external) for Inbound NAT. */ |
/* Inbound NAT: destination (think external) address. */ |
KASSERT(np->n_type == NPF_NATIN); |
KASSERT(np->n_type == NPF_NATIN); |
memcpy(&nt->nt_oaddr, npc->npc_dstip, npc->npc_ipsz); |
memcpy(&nt->nt_oaddr, npc->npc_ips[NPF_DST], npc->npc_alen); |
} |
} |
|
|
/* |
/* |
Line 452 npf_nat_create(npf_cache_t *npc, npf_nat |
|
Line 521 npf_nat_create(npf_cache_t *npc, npf_nat |
|
(proto != IPPROTO_TCP && proto != IPPROTO_UDP)) { |
(proto != IPPROTO_TCP && proto != IPPROTO_UDP)) { |
nt->nt_oport = 0; |
nt->nt_oport = 0; |
nt->nt_tport = 0; |
nt->nt_tport = 0; |
return nt; |
goto out; |
} |
} |
|
|
/* Save the relevant TCP/UDP port. */ |
/* Save the relevant TCP/UDP port. */ |
if (proto == IPPROTO_TCP) { |
if (proto == IPPROTO_TCP) { |
struct tcphdr *th = &npc->npc_l4.tcp; |
const struct tcphdr *th = npc->npc_l4.tcp; |
nt->nt_oport = (np->n_type == NPF_NATOUT) ? |
nt->nt_oport = (np->n_type == NPF_NATOUT) ? |
th->th_sport : th->th_dport; |
th->th_sport : th->th_dport; |
} else { |
} else { |
struct udphdr *uh = &npc->npc_l4.udp; |
const struct udphdr *uh = npc->npc_l4.udp; |
nt->nt_oport = (np->n_type == NPF_NATOUT) ? |
nt->nt_oport = (np->n_type == NPF_NATOUT) ? |
uh->uh_sport : uh->uh_dport; |
uh->uh_sport : uh->uh_dport; |
} |
} |
Line 471 npf_nat_create(npf_cache_t *npc, npf_nat |
|
Line 541 npf_nat_create(npf_cache_t *npc, npf_nat |
|
} else { |
} else { |
nt->nt_tport = np->n_tport; |
nt->nt_tport = np->n_tport; |
} |
} |
|
out: |
|
mutex_enter(&np->n_lock); |
|
LIST_INSERT_HEAD(&np->n_nat_list, nt, nt_entry); |
|
mutex_exit(&np->n_lock); |
return nt; |
return nt; |
} |
} |
|
|
/* |
/* |
* npf_nat_translate: perform address and/or port translation. |
* npf_nat_translate: perform translation given the state data. |
*/ |
*/ |
static int |
static inline int |
npf_nat_translate(npf_cache_t *npc, nbuf_t *nbuf, npf_nat_t *nt, |
npf_nat_translate(npf_cache_t *npc, nbuf_t *nbuf, npf_nat_t *nt, bool forw) |
const bool forw, const int di) |
|
{ |
{ |
void *n_ptr = nbuf_dataptr(nbuf); |
const npf_natpolicy_t *np = nt->nt_natpolicy; |
npf_natpolicy_t *np = nt->nt_natpolicy; |
const u_int which = npf_nat_which(np->n_type, forw); |
npf_addr_t *addr; |
const npf_addr_t *addr; |
in_port_t port; |
in_port_t port; |
|
|
KASSERT(npf_iscached(npc, NPC_IP46)); |
KASSERT(npf_iscached(npc, NPC_IP46)); |
|
KASSERT(npf_iscached(npc, NPC_LAYER4)); |
|
|
if (forw) { |
if (forw) { |
/* "Forwards" stream: use translation address/port. */ |
/* "Forwards" stream: use translation address/port. */ |
KASSERT( |
|
(np->n_type == NPF_NATIN && di == PFIL_IN) ^ |
|
(np->n_type == NPF_NATOUT && di == PFIL_OUT) |
|
); |
|
addr = &np->n_taddr; |
addr = &np->n_taddr; |
port = nt->nt_tport; |
port = nt->nt_tport; |
} else { |
} else { |
/* "Backwards" stream: use original address/port. */ |
/* "Backwards" stream: use original address/port. */ |
KASSERT( |
|
(np->n_type == NPF_NATIN && di == PFIL_OUT) ^ |
|
(np->n_type == NPF_NATOUT && di == PFIL_IN) |
|
); |
|
addr = &nt->nt_oaddr; |
addr = &nt->nt_oaddr; |
port = nt->nt_oport; |
port = nt->nt_oport; |
} |
} |
KASSERT((np->n_flags & NPF_NAT_PORTS) != 0 || port == 0); |
KASSERT((np->n_flags & NPF_NAT_PORTS) != 0 || port == 0); |
|
|
/* Execute ALG hook first. */ |
/* Execute ALG translation first. */ |
npf_alg_exec(npc, nbuf, nt, di); |
if ((npc->npc_info & NPC_ALG_EXEC) == 0) { |
|
npc->npc_info |= NPC_ALG_EXEC; |
/* |
npf_alg_exec(npc, nbuf, nt, forw); |
* Rewrite IP and/or TCP/UDP checksums first, since it will use |
npf_recache(npc, nbuf); |
* the cache containing original values for checksum calculation. |
|
*/ |
|
if (!npf_rwrcksum(npc, nbuf, n_ptr, di, addr, port)) { |
|
return EINVAL; |
|
} |
|
/* |
|
* Address translation: rewrite source/destination address, depending |
|
* on direction (PFIL_OUT - for source, PFIL_IN - for destination). |
|
*/ |
|
if (!npf_rwrip(npc, nbuf, n_ptr, di, addr)) { |
|
return EINVAL; |
|
} |
} |
if ((np->n_flags & NPF_NAT_PORTS) == 0) { |
KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET)); |
/* Done. */ |
|
return 0; |
/* Finally, perform the translation. */ |
} |
return npf_napt_rwr(npc, which, addr, port); |
switch (npf_cache_ipproto(npc)) { |
} |
case IPPROTO_TCP: |
|
case IPPROTO_UDP: |
/* |
KASSERT(npf_iscached(npc, NPC_TCP | NPC_UDP)); |
* npf_nat_algo: perform the translation given the algorithm. |
/* Rewrite source/destination port. */ |
*/ |
if (!npf_rwrport(npc, nbuf, n_ptr, di, port)) { |
static inline int |
return EINVAL; |
npf_nat_algo(npf_cache_t *npc, const npf_natpolicy_t *np, bool forw) |
} |
{ |
break; |
const u_int which = npf_nat_which(np->n_type, forw); |
case IPPROTO_ICMP: |
int error; |
KASSERT(npf_iscached(npc, NPC_ICMP)); |
|
/* Nothing. */ |
switch (np->n_algo) { |
|
case NPF_ALGO_NPT66: |
|
error = npf_npt66_rwr(npc, which, &np->n_taddr, |
|
np->n_tmask, np->n_npt66_adj); |
break; |
break; |
default: |
default: |
return ENOTSUP; |
error = npf_napt_rwr(npc, which, &np->n_taddr, np->n_tport); |
|
break; |
} |
} |
return 0; |
|
} |
return error; |
|
} |
|
|
/* |
/* |
* npf_do_nat: |
* npf_do_nat: |
Line 557 npf_nat_translate(npf_cache_t *npc, nbuf |
|
Line 617 npf_nat_translate(npf_cache_t *npc, nbuf |
|
* - Associate a NAT policy with a session (may establish a new). |
* - Associate a NAT policy with a session (may establish a new). |
*/ |
*/ |
int |
int |
npf_do_nat(npf_cache_t *npc, npf_session_t *se, nbuf_t *nbuf, |
npf_do_nat(npf_cache_t *npc, npf_session_t *se, nbuf_t *nbuf, const int di) |
ifnet_t *ifp, const int di) |
|
{ |
{ |
npf_session_t *nse = NULL; |
npf_session_t *nse = NULL; |
npf_natpolicy_t *np; |
npf_natpolicy_t *np; |
npf_nat_t *nt; |
npf_nat_t *nt; |
int error; |
int error; |
bool forw, new; |
bool forw; |
|
|
/* All relevant IPv4 data should be already cached. */ |
/* All relevant IPv4 data should be already cached. */ |
if (!npf_iscached(npc, NPC_IP46) || !npf_iscached(npc, NPC_LAYER4)) { |
if (!npf_iscached(npc, NPC_IP46) || !npf_iscached(npc, NPC_LAYER4)) { |
return 0; |
return 0; |
} |
} |
|
KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET)); |
|
|
/* |
/* |
* Return the NAT entry associated with the session, if any. |
* Return the NAT entry associated with the session, if any. |
Line 578 npf_do_nat(npf_cache_t *npc, npf_session |
|
Line 638 npf_do_nat(npf_cache_t *npc, npf_session |
|
*/ |
*/ |
if (se && (nt = npf_session_retnat(se, di, &forw)) != NULL) { |
if (se && (nt = npf_session_retnat(se, di, &forw)) != NULL) { |
np = nt->nt_natpolicy; |
np = nt->nt_natpolicy; |
new = false; |
|
goto translate; |
goto translate; |
} |
} |
|
|
/* |
/* |
* Inspect the packet for a NAT policy, if there is no session. |
* Inspect the packet for a NAT policy, if there is no session. |
* Note: acquires the lock (releases, if not found). |
* Note: acquires a reference if found. |
*/ |
*/ |
np = npf_nat_inspect(npc, nbuf, ifp, di); |
np = npf_nat_inspect(npc, nbuf, di); |
if (np == NULL) { |
if (np == NULL) { |
/* If packet does not match - done. */ |
/* If packet does not match - done. */ |
return 0; |
return 0; |
} |
} |
forw = true; |
forw = true; |
|
|
/* |
/* Static NAT - just perform the translation. */ |
* Create a new NAT entry. Note: it is safe to unlock, since the |
if (np->n_flags & NPF_NAT_STATIC) { |
* NAT policy wont be desotroyed while there are list entries, which |
if (nbuf_cksum_barrier(nbuf, di)) { |
* are removed only on session expiration. Currently, NAT entry is |
npf_recache(npc, nbuf); |
* not yet associated with any session. |
} |
*/ |
error = npf_nat_algo(npc, np, forw); |
nt = npf_nat_create(npc, np); |
atomic_dec_uint(&np->n_refcnt); |
if (nt == NULL) { |
return error; |
npf_core_exit(); |
|
return ENOMEM; |
|
} |
|
npf_core_exit(); |
|
new = true; |
|
|
|
/* Determine whether any ALG matches. */ |
|
if (npf_alg_match(npc, nbuf, nt)) { |
|
KASSERT(nt->nt_alg != NULL); |
|
} |
} |
|
|
/* |
/* |
* If there is no local session (no "keep state" rule - unusual, but |
* If there is no local session (no "stateful" rule - unusual, but |
* possible configuration), establish one before translation. Note |
* possible configuration), establish one before translation. Note |
* that it is not a "pass" session, therefore passing of "backwards" |
* that it is not a "pass" session, therefore passing of "backwards" |
* stream depends on other, stateless filtering rules. |
* stream depends on other, stateless filtering rules. |
*/ |
*/ |
if (se == NULL) { |
if (se == NULL) { |
nse = npf_session_establish(npc, nbuf, di); |
nse = npf_session_establish(npc, nbuf, di, true); |
if (nse == NULL) { |
if (nse == NULL) { |
error = ENOMEM; |
atomic_dec_uint(&np->n_refcnt); |
goto out; |
return ENOMEM; |
} |
} |
se = nse; |
se = nse; |
} |
} |
translate: |
|
/* Perform the translation. */ |
/* |
error = npf_nat_translate(npc, nbuf, nt, forw, di); |
* Create a new NAT entry and associate with the session. |
|
* We will consume the reference on success (release on error). |
|
*/ |
|
nt = npf_nat_create(npc, np, se); |
|
if (nt == NULL) { |
|
atomic_dec_uint(&np->n_refcnt); |
|
error = ENOMEM; |
|
goto out; |
|
} |
|
|
|
/* Associate the NAT translation entry with the session. */ |
|
error = npf_session_setnat(se, nt, np->n_type); |
if (error) { |
if (error) { |
|
/* Will release the reference. */ |
|
npf_nat_destroy(nt); |
goto out; |
goto out; |
} |
} |
|
|
if (__predict_false(new)) { |
/* Determine whether any ALG matches. */ |
/* |
if (npf_alg_match(npc, nbuf, nt, di)) { |
* Associate NAT translation entry with the session. |
KASSERT(nt->nt_alg != NULL); |
* Note: packet now has a translated address in the cache. |
} |
*/ |
|
nt->nt_session = se; |
translate: |
error = npf_session_setnat(se, nt, di); |
/* May need to process the delayed checksums first (XXX: NetBSD). */ |
|
if (nbuf_cksum_barrier(nbuf, di)) { |
|
npf_recache(npc, nbuf); |
|
} |
|
|
|
/* Perform the translation. */ |
|
error = npf_nat_translate(npc, nbuf, nt, forw); |
out: |
out: |
|
if (__predict_false(nse)) { |
if (error) { |
if (error) { |
/* If session was for NAT only - expire it. */ |
/* It created for NAT - just expire. */ |
if (nse) { |
npf_session_expire(nse); |
npf_session_expire(nse); |
|
} |
|
/* Will free the structure and return the port. */ |
|
npf_nat_expire(nt); |
|
} |
|
if (nse != NULL) { |
|
npf_session_release(nse); |
|
} |
} |
|
npf_session_release(nse); |
} |
} |
return error; |
return error; |
} |
} |
Line 674 npf_nat_gettrans(npf_nat_t *nt, npf_addr |
|
Line 738 npf_nat_gettrans(npf_nat_t *nt, npf_addr |
|
void |
void |
npf_nat_getorig(npf_nat_t *nt, npf_addr_t **addr, in_port_t *port) |
npf_nat_getorig(npf_nat_t *nt, npf_addr_t **addr, in_port_t *port) |
{ |
{ |
|
|
*addr = &nt->nt_oaddr; |
*addr = &nt->nt_oaddr; |
*port = nt->nt_oport; |
*port = nt->nt_oport; |
} |
} |
Line 685 npf_nat_getorig(npf_nat_t *nt, npf_addr_ |
|
Line 748 npf_nat_getorig(npf_nat_t *nt, npf_addr_ |
|
void |
void |
npf_nat_setalg(npf_nat_t *nt, npf_alg_t *alg, uintptr_t arg) |
npf_nat_setalg(npf_nat_t *nt, npf_alg_t *alg, uintptr_t arg) |
{ |
{ |
|
|
nt->nt_alg = alg; |
nt->nt_alg = alg; |
nt->nt_alg_arg = arg; |
nt->nt_alg_arg = arg; |
} |
} |
|
|
/* |
/* |
* npf_nat_expire: free NAT-related data structures on session expiration. |
* npf_nat_destroy: destroy NAT structure (performed on session expiration). |
*/ |
*/ |
void |
void |
npf_nat_expire(npf_nat_t *nt) |
npf_nat_destroy(npf_nat_t *nt) |
{ |
{ |
npf_natpolicy_t *np = nt->nt_natpolicy; |
npf_natpolicy_t *np = nt->nt_natpolicy; |
|
|
Line 703 npf_nat_expire(npf_nat_t *nt) |
|
Line 765 npf_nat_expire(npf_nat_t *nt) |
|
npf_nat_putport(np, nt->nt_tport); |
npf_nat_putport(np, nt->nt_tport); |
} |
} |
|
|
/* Remove NAT entry from the list, notify any waiters if last entry. */ |
|
mutex_enter(&np->n_lock); |
mutex_enter(&np->n_lock); |
LIST_REMOVE(nt, nt_entry); |
LIST_REMOVE(nt, nt_entry); |
if (LIST_EMPTY(&np->n_nat_list)) { |
if (LIST_EMPTY(&np->n_nat_list)) { |
|
/* Notify any waiters if empty. */ |
cv_broadcast(&np->n_cv); |
cv_broadcast(&np->n_cv); |
} |
} |
|
atomic_dec_uint(&np->n_refcnt); |
mutex_exit(&np->n_lock); |
mutex_exit(&np->n_lock); |
|
|
/* Free structure, increase the counter. */ |
|
pool_cache_put(nat_cache, nt); |
pool_cache_put(nat_cache, nt); |
npf_stats_inc(NPF_STAT_NAT_DESTROY); |
npf_stats_inc(NPF_STAT_NAT_DESTROY); |
} |
} |
Line 726 npf_nat_save(prop_dictionary_t sedict, p |
|
Line 788 npf_nat_save(prop_dictionary_t sedict, p |
|
prop_object_iterator_t it; |
prop_object_iterator_t it; |
prop_dictionary_t npdict; |
prop_dictionary_t npdict; |
prop_data_t nd, npd; |
prop_data_t nd, npd; |
uintptr_t itnp; |
uint64_t itnp; |
|
|
/* Set NAT entry data. */ |
/* Set NAT entry data. */ |
nd = prop_data_create_data(nt, sizeof(npf_nat_t)); |
nd = prop_data_create_data(nt, sizeof(npf_nat_t)); |
Line 737 npf_nat_save(prop_dictionary_t sedict, p |
|
Line 799 npf_nat_save(prop_dictionary_t sedict, p |
|
it = prop_array_iterator(natlist); |
it = prop_array_iterator(natlist); |
while ((npdict = prop_object_iterator_next(it)) != NULL) { |
while ((npdict = prop_object_iterator_next(it)) != NULL) { |
CTASSERT(sizeof(uintptr_t) <= sizeof(uint64_t)); |
CTASSERT(sizeof(uintptr_t) <= sizeof(uint64_t)); |
prop_dictionary_get_uint64(npdict, "id-ptr", (uint64_t *)&itnp); |
prop_dictionary_get_uint64(npdict, "id-ptr", &itnp); |
if (itnp == (uintptr_t)np) { |
if ((uintptr_t)itnp == (uintptr_t)np) { |
break; |
break; |
} |
} |
} |
} |
Line 789 npf_nat_restore(prop_dictionary_t sedict |
|
Line 851 npf_nat_restore(prop_dictionary_t sedict |
|
return NULL; |
return NULL; |
} |
} |
|
|
/* Match if there is an existing NAT policy. */ |
/* |
rl = npf_ruleset_matchnat(npf_core_natset(), __UNCONST(onp)); |
* Match if there is an existing NAT policy. Will acquire the |
|
* reference on it if further operations are successful. |
|
*/ |
|
KASSERT(npf_config_locked_p()); |
|
rl = npf_ruleset_matchnat(npf_config_natset(), __UNCONST(onp)); |
if (rl == NULL) { |
if (rl == NULL) { |
return NULL; |
return NULL; |
} |
} |
Line 801 npf_nat_restore(prop_dictionary_t sedict |
|
Line 867 npf_nat_restore(prop_dictionary_t sedict |
|
if (!npf_nat_takeport(np, ntraw->nt_tport)) { |
if (!npf_nat_takeport(np, ntraw->nt_tport)) { |
return NULL; |
return NULL; |
} |
} |
|
atomic_inc_uint(&np->n_refcnt); |
|
|
/* Create and return NAT entry for association. */ |
/* Create and return NAT entry for association. */ |
nt = pool_cache_get(nat_cache, PR_WAITOK); |
nt = pool_cache_get(nat_cache, PR_WAITOK); |
Line 815 npf_nat_restore(prop_dictionary_t sedict |
|
Line 882 npf_nat_restore(prop_dictionary_t sedict |
|
#if defined(DDB) || defined(_NPF_TESTING) |
#if defined(DDB) || defined(_NPF_TESTING) |
|
|
void |
void |
npf_nat_dump(npf_nat_t *nt) |
npf_nat_dump(const npf_nat_t *nt) |
{ |
{ |
npf_natpolicy_t *np; |
const npf_natpolicy_t *np; |
struct in_addr ip; |
struct in_addr ip; |
|
|
np = nt->nt_natpolicy; |
np = nt->nt_natpolicy; |