Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F153840214
D11137.id125490.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
21 KB
Referenced Files
None
Subscribers
None
D11137.id125490.diff
View Options
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -923,6 +923,29 @@
u_int8_t pad[1];
};
+/* Keep synced with struct pf_udp_endpoint. */
+struct pf_udp_endpoint_cmp {
+ struct pf_addr addr;
+ uint16_t port;
+ sa_family_t af;
+ uint8_t pad[1];
+};
+
+struct pf_udp_endpoint {
+ struct pf_addr addr;
+ uint16_t port;
+ sa_family_t af;
+ uint8_t pad[1];
+
+ struct pf_udp_mapping *mapping;
+ LIST_ENTRY(pf_udp_endpoint) entry;
+};
+
+struct pf_udp_mapping {
+ struct pf_udp_endpoint endpoints[2];
+ u_int refs;
+};
+
/* Keep synced with struct pf_state_key. */
struct pf_state_key_cmp {
struct pf_addr addr[2];
@@ -1052,6 +1075,7 @@
union pf_krule_ptr nat_rule;
struct pf_addr rt_addr;
struct pf_state_key *key[2]; /* addresses stack and wire */
+ struct pf_udp_mapping *udp_mapping;
struct pfi_kkif *kif;
struct pfi_kkif *orig_kif; /* The real kif, even if we're a floating state (i.e. if == V_pfi_all). */
struct pfi_kkif *rt_kif;
@@ -2071,6 +2095,11 @@
struct mtx lock;
};
+struct pf_udpendpointhash {
+ LIST_HEAD(, pf_udp_endpoint) endpoints;
+ struct mtx lock;
+};
+
struct pf_keyhash {
LIST_HEAD(, pf_state_key) keys;
struct mtx lock;
@@ -2086,8 +2115,10 @@
extern u_long pf_srchashmask;
#define PF_HASHSIZ (131072)
#define PF_SRCHASHSIZ (PF_HASHSIZ/4)
+VNET_DECLARE(struct pf_udpendpointhash *, pf_udpendpointhash);
VNET_DECLARE(struct pf_keyhash *, pf_keyhash);
VNET_DECLARE(struct pf_idhash *, pf_idhash);
+#define V_pf_udpendpointhash VNET(pf_udpendpointhash)
#define V_pf_keyhash VNET(pf_keyhash)
#define V_pf_idhash VNET(pf_idhash)
VNET_DECLARE(struct pf_srchash *, pf_srchash);
@@ -2162,6 +2193,8 @@
#define V_pf_state_z VNET(pf_state_z)
VNET_DECLARE(uma_zone_t, pf_state_key_z);
#define V_pf_state_key_z VNET(pf_state_key_z)
+VNET_DECLARE(uma_zone_t, pf_udp_mapping_z);
+#define V_pf_udp_mapping_z VNET(pf_udp_mapping_z)
VNET_DECLARE(uma_zone_t, pf_state_scrub_z);
#define V_pf_state_scrub_z VNET(pf_state_scrub_z)
@@ -2213,6 +2246,13 @@
u_int, int *);
extern bool pf_find_state_all_exists(struct pf_state_key_cmp *,
u_int);
+extern struct pf_udp_mapping *pf_udp_mapping_find(struct pf_udp_endpoint_cmp *endpoint);
+extern struct pf_udp_mapping *pf_udp_mapping_create(sa_family_t af,
+ struct pf_addr *src_addr, uint16_t src_port,
+ struct pf_addr *nat_addr, uint16_t nat_port);
+extern int pf_udp_mapping_insert(struct pf_udp_mapping *mapping);
+extern void pf_udp_mapping_release(struct pf_udp_mapping *mapping);
+
extern struct pf_ksrc_node *pf_find_src_node(struct pf_addr *,
struct pf_krule *, sa_family_t,
struct pf_srchash **, bool);
@@ -2480,7 +2520,8 @@
int, struct pfi_kkif *, struct pf_ksrc_node **,
struct pf_state_key **, struct pf_state_key **,
struct pf_addr *, struct pf_addr *,
- uint16_t, uint16_t, struct pf_kanchor_stackframe *);
+ uint16_t, uint16_t, struct pf_kanchor_stackframe *,
+ struct pf_udp_mapping **udp_mapping);
struct pf_state_key *pf_state_key_setup(struct pf_pdesc *, struct pf_addr *,
struct pf_addr *, u_int16_t, u_int16_t);
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -248,6 +248,7 @@
uma_zone_t pf_mtag_z;
VNET_DEFINE(uma_zone_t, pf_state_z);
VNET_DEFINE(uma_zone_t, pf_state_key_z);
+VNET_DEFINE(uma_zone_t, pf_udp_mapping_z);
VNET_DEFINE(struct unrhdr64, pf_stateid);
@@ -291,7 +292,7 @@
struct pf_state_key *, struct mbuf *, int,
u_int16_t, u_int16_t, int *, struct pfi_kkif *,
struct pf_kstate **, int, u_int16_t, u_int16_t,
- int, struct pf_krule_slist *);
+ int, struct pf_krule_slist *, struct pf_udp_mapping *);
static int pf_test_fragment(struct pf_krule **, struct pfi_kkif *,
struct mbuf *, void *, struct pf_pdesc *,
struct pf_krule **, struct pf_kruleset **);
@@ -417,6 +418,7 @@
VNET_DEFINE(struct pf_keyhash *, pf_keyhash);
VNET_DEFINE(struct pf_idhash *, pf_idhash);
VNET_DEFINE(struct pf_srchash *, pf_srchash);
+VNET_DEFINE(struct pf_udpendpointhash *, pf_udpendpointhash);
SYSCTL_NODE(_net, OID_AUTO, pf, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
"pf(4)");
@@ -600,6 +602,18 @@
return (h & pf_hashmask);
}
+static inline uint32_t
+pf_hashudpendpoint(struct pf_udp_endpoint *endpoint)
+{
+ uint32_t h;
+
+ h = murmur3_32_hash32((uint32_t *)endpoint,
+ sizeof(struct pf_udp_endpoint_cmp)/sizeof(uint32_t),
+ V_pf_hashseed);
+
+ return (h & pf_hashmask);
+}
+
static __inline uint32_t
pf_hashsrc(struct pf_addr *addr, sa_family_t af)
{
@@ -1010,6 +1024,7 @@
{
struct pf_keyhash *kh;
struct pf_idhash *ih;
+ struct pf_udpendpointhash *uh;
struct pf_srchash *sh;
u_int i;
@@ -1030,11 +1045,16 @@
V_pf_state_key_z = uma_zcreate("pf state keys",
sizeof(struct pf_state_key), pf_state_key_ctor, NULL, NULL, NULL,
UMA_ALIGN_PTR, 0);
+
+ V_pf_udp_mapping_z = uma_zcreate("pf UDP mappings",
+ sizeof(struct pf_udp_mapping), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
V_pf_keyhash = mallocarray(pf_hashsize, sizeof(struct pf_keyhash),
M_PFHASH, M_NOWAIT | M_ZERO);
V_pf_idhash = mallocarray(pf_hashsize, sizeof(struct pf_idhash),
M_PFHASH, M_NOWAIT | M_ZERO);
+ V_pf_udpendpointhash = mallocarray(pf_hashsize, sizeof(struct pf_udpendpointhash),
+ M_PFHASH, M_WAITOK | M_ZERO);
if (V_pf_keyhash == NULL || V_pf_idhash == NULL) {
printf("pf: Unable to allocate memory for "
"state_hashsize %lu.\n", pf_hashsize);
@@ -1050,10 +1070,11 @@
}
pf_hashmask = pf_hashsize - 1;
- for (i = 0, kh = V_pf_keyhash, ih = V_pf_idhash; i <= pf_hashmask;
- i++, kh++, ih++) {
+ for (i = 0, kh = V_pf_keyhash, ih = V_pf_idhash, uh=V_pf_udpendpointhash; i <= pf_hashmask;
+ i++, kh++, ih++, uh++) {
mtx_init(&kh->lock, "pf_keyhash", NULL, MTX_DEF | MTX_DUPOK);
mtx_init(&ih->lock, "pf_idhash", NULL, MTX_DEF);
+ mtx_init(&uh->lock, "pf_udpendpointhash", NULL, MTX_DEF | MTX_DUPOK);
}
/* Source nodes. */
@@ -1111,21 +1132,26 @@
{
struct pf_keyhash *kh;
struct pf_idhash *ih;
+ struct pf_udpendpointhash *uh;
struct pf_srchash *sh;
struct pf_send_entry *pfse, *next;
u_int i;
- for (i = 0, kh = V_pf_keyhash, ih = V_pf_idhash; i <= pf_hashmask;
- i++, kh++, ih++) {
+ for (i = 0, kh = V_pf_keyhash, ih = V_pf_idhash, uh=V_pf_udpendpointhash; i <= pf_hashmask;
+ i++, kh++, ih++, uh++) {
KASSERT(LIST_EMPTY(&kh->keys), ("%s: key hash not empty",
__func__));
KASSERT(LIST_EMPTY(&ih->states), ("%s: id hash not empty",
__func__));
+ KASSERT(LIST_EMPTY(&uh->endpoints), ("%s: udpendpoint hash not empty",
+ __func__));
mtx_destroy(&kh->lock);
mtx_destroy(&ih->lock);
+ mtx_destroy(&uh->lock);
}
free(V_pf_keyhash, M_PFHASH);
free(V_pf_idhash, M_PFHASH);
+ free(V_pf_udpendpointhash, M_PFHASH);
for (i = 0, sh = V_pf_srchash; i <= pf_srchashmask; i++, sh++) {
KASSERT(LIST_EMPTY(&sh->nodes),
@@ -1142,6 +1168,7 @@
uma_zdestroy(V_pf_sources_z);
uma_zdestroy(V_pf_state_z);
uma_zdestroy(V_pf_state_key_z);
+ uma_zdestroy(V_pf_udp_mapping_z);
}
static int
@@ -1656,6 +1683,114 @@
return (false);
}
+struct pf_udp_mapping*
+pf_udp_mapping_create(sa_family_t af, struct pf_addr *src_addr, uint16_t src_port,
+ struct pf_addr *nat_addr, uint16_t nat_port)
+{
+ struct pf_udp_mapping *mapping;
+
+ mapping = uma_zalloc(V_pf_udp_mapping_z, M_NOWAIT | M_ZERO);
+ if (mapping == NULL)
+ return NULL;
+ PF_ACPY(&mapping->endpoints[0].addr, src_addr, af);
+ mapping->endpoints[0].port = src_port;
+ mapping->endpoints[0].af = af;
+ mapping->endpoints[0].mapping = mapping;
+ PF_ACPY(&mapping->endpoints[1].addr, nat_addr, af);
+ mapping->endpoints[1].port = nat_port;
+ mapping->endpoints[1].af = af;
+ mapping->endpoints[1].mapping = mapping;
+ refcount_init(&mapping->refs, 1);
+ return (mapping);
+}
+
+int
+pf_udp_mapping_insert(struct pf_udp_mapping *mapping)
+{
+ struct pf_udpendpointhash *h0, *h1;
+ struct pf_udp_endpoint *endpoint;
+ int ret = 1;
+
+ h0 = &V_pf_udpendpointhash[pf_hashudpendpoint(&mapping->endpoints[0])];
+ h1 = &V_pf_udpendpointhash[pf_hashudpendpoint(&mapping->endpoints[1])];
+ if (h0 == h1) {
+ PF_HASHROW_LOCK(h0);
+ } else if (h0 < h1) {
+ PF_HASHROW_LOCK(h0);
+ PF_HASHROW_LOCK(h1);
+ } else {
+ PF_HASHROW_LOCK(h1);
+ PF_HASHROW_LOCK(h0);
+ }
+
+ LIST_FOREACH(endpoint, &h0->endpoints, entry)
+ if (bcmp(endpoint, &mapping->endpoints[0], sizeof(struct pf_udp_endpoint_cmp)) == 0)
+ break;
+ if (endpoint != NULL)
+ goto cleanup;
+ LIST_FOREACH(endpoint, &h1->endpoints, entry)
+ if (bcmp(endpoint, &mapping->endpoints[1], sizeof(struct pf_udp_endpoint_cmp)) == 0)
+ break;
+ if (endpoint != NULL)
+ goto cleanup;
+ LIST_INSERT_HEAD(&h0->endpoints, &mapping->endpoints[0], entry);
+ LIST_INSERT_HEAD(&h1->endpoints, &mapping->endpoints[1], entry);
+ ret = 0;
+
+cleanup:
+ if (h0 != h1) {
+ PF_HASHROW_UNLOCK(h0);
+ PF_HASHROW_UNLOCK(h1);
+ } else {
+ PF_HASHROW_UNLOCK(h0);
+ }
+ return (ret);
+}
+
+void
+pf_udp_mapping_release(struct pf_udp_mapping *mapping)
+{
+ /* refcount is synchronized on the source endpoint's row lock */
+ struct pf_udpendpointhash *h0, *h1;
+
+ h0 = &V_pf_udpendpointhash[pf_hashudpendpoint(&mapping->endpoints[0])];
+ PF_HASHROW_LOCK(h0);
+ if (refcount_release(&mapping->refs)) {
+ LIST_REMOVE(&mapping->endpoints[0], entry);
+ PF_HASHROW_UNLOCK(h0);
+ h1 = &V_pf_udpendpointhash[pf_hashudpendpoint(&mapping->endpoints[1])];
+ PF_HASHROW_LOCK(h1);
+ LIST_REMOVE(&mapping->endpoints[1], entry);
+ PF_HASHROW_UNLOCK(h1);
+
+ uma_zfree(V_pf_udp_mapping_z, mapping);
+ } else {
+ PF_HASHROW_UNLOCK(h0);
+ }
+}
+
+struct pf_udp_mapping *
+pf_udp_mapping_find(struct pf_udp_endpoint_cmp *key)
+{
+ struct pf_udpendpointhash *uh;
+ struct pf_udp_endpoint *endpoint;
+
+ uh = &V_pf_udpendpointhash[pf_hashudpendpoint((struct pf_udp_endpoint*)key)];
+
+ PF_HASHROW_LOCK(uh);
+ LIST_FOREACH(endpoint, &uh->endpoints, entry)
+ if (bcmp(endpoint, key, sizeof(struct pf_udp_endpoint_cmp)) == 0 &&
+ bcmp(endpoint, &endpoint->mapping->endpoints[0], sizeof(struct pf_udp_endpoint_cmp)) == 0)
+ break;
+ if (endpoint == NULL) {
+ PF_HASHROW_UNLOCK(uh);
+ return NULL;
+ }
+ refcount_acquire(&endpoint->mapping->refs);
+ PF_HASHROW_UNLOCK(uh);
+ return (endpoint->mapping);
+}
+
/* END state table stuff */
static void
@@ -2101,6 +2236,10 @@
PF_HASHROW_UNLOCK(ih);
pf_detach_state(s);
+
+ if (s->udp_mapping)
+ pf_udp_mapping_release(s->udp_mapping);
+
/* pf_state_insert() initialises refs to 2 */
return (pf_release_staten(s, 2));
}
@@ -4360,6 +4499,7 @@
u_int16_t bproto_sum = 0, bip_sum = 0;
u_int8_t icmptype = 0, icmpcode = 0;
struct pf_kanchor_stackframe anchor_stack[PF_ANCHOR_STACKSIZE];
+ struct pf_udp_mapping *udp_mapping = NULL;
PF_RULES_RASSERT();
@@ -4428,7 +4568,7 @@
/* check packet for BINAT/NAT/RDR */
if ((nr = pf_get_translation(pd, m, off, kif, &nsn, &sk,
- &nk, saddr, daddr, sport, dport, anchor_stack)) != NULL) {
+ &nk, saddr, daddr, sport, dport, anchor_stack, &udp_mapping)) != NULL) {
KASSERT(sk != NULL, ("%s: null sk", __func__));
KASSERT(nk != NULL, ("%s: null nk", __func__));
@@ -4719,8 +4859,10 @@
int action;
action = pf_create_state(r, nr, a, pd, nsn, nk, sk, m, off,
sport, dport, &rewrite, kif, sm, tag, bproto_sum, bip_sum,
- hdrlen, &match_rules);
+ hdrlen, &match_rules, udp_mapping);
if (action != PF_PASS) {
+ if (udp_mapping != NULL)
+ pf_udp_mapping_release(udp_mapping);
if (action == PF_DROP &&
(r->rule_flag & PFRULE_RETURN))
pf_return(r, nr, pd, sk, off, m, th, kif,
@@ -4731,6 +4873,8 @@
} else {
uma_zfree(V_pf_state_key_z, sk);
uma_zfree(V_pf_state_key_z, nk);
+ if (udp_mapping != NULL)
+ pf_udp_mapping_release(udp_mapping);
}
/* copy back packet headers if we performed NAT operations */
@@ -4758,6 +4902,10 @@
uma_zfree(V_pf_state_key_z, sk);
uma_zfree(V_pf_state_key_z, nk);
+
+ if (udp_mapping != NULL)
+ pf_udp_mapping_release(udp_mapping);
+
return (PF_DROP);
}
@@ -4767,7 +4915,8 @@
struct pf_state_key *sk, struct mbuf *m, int off, u_int16_t sport,
u_int16_t dport, int *rewrite, struct pfi_kkif *kif, struct pf_kstate **sm,
int tag, u_int16_t bproto_sum, u_int16_t bip_sum, int hdrlen,
- struct pf_krule_slist *match_rules)
+ struct pf_krule_slist *match_rules,
+ struct pf_udp_mapping *udp_mapping)
{
struct pf_kstate *s = NULL;
struct pf_ksrc_node *sn = NULL;
@@ -4993,6 +5142,7 @@
REASON_SET(&reason, PFRES_SYNPROXY);
return (PF_SYNPROXY_DROP);
}
+ s->udp_mapping = udp_mapping;
return (PF_PASS);
diff --git a/sys/netpfil/pf/pf_lb.c b/sys/netpfil/pf/pf_lb.c
--- a/sys/netpfil/pf/pf_lb.c
+++ b/sys/netpfil/pf/pf_lb.c
@@ -64,7 +64,8 @@
uint16_t, int, struct pf_kanchor_stackframe *);
static int pf_get_sport(sa_family_t, uint8_t, struct pf_krule *,
struct pf_addr *, uint16_t, struct pf_addr *, uint16_t, struct pf_addr *,
- uint16_t *, uint16_t, uint16_t, struct pf_ksrc_node **);
+ uint16_t *, uint16_t, uint16_t, struct pf_ksrc_node **,
+ struct pf_udp_mapping**);
#define mix(a,b,c) \
do { \
@@ -218,14 +219,38 @@
pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_krule *r,
struct pf_addr *saddr, uint16_t sport, struct pf_addr *daddr,
uint16_t dport, struct pf_addr *naddr, uint16_t *nport, uint16_t low,
- uint16_t high, struct pf_ksrc_node **sn)
+ uint16_t high, struct pf_ksrc_node **sn, struct pf_udp_mapping **udp_mapping)
{
struct pf_state_key_cmp key;
struct pf_addr init_addr;
+ struct pf_srchash *sh = NULL;
+
+ if (proto == IPPROTO_UDP) {
+ struct pf_udp_endpoint_cmp udp_source;
+
+ bzero(&udp_source, sizeof(udp_source));
+ udp_source.af = af;
+ PF_ACPY(&udp_source.addr, saddr, af);
+ udp_source.port = sport;
+ *udp_mapping = pf_udp_mapping_find(&udp_source);
+ if (*udp_mapping) {
+ PF_ACPY(naddr, &(*udp_mapping)->endpoints[1].addr, af);
+ *nport = (*udp_mapping)->endpoints[1].port;
+ /* as per pf_map_addr(): */
+ if (*sn == NULL && r->rpool.opts & PF_POOL_STICKYADDR &&
+ (r->rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_NONE)
+ *sn = pf_find_src_node(saddr, r, af, &sh, 0);
+ return (0);
+ } else {
+ *udp_mapping = pf_udp_mapping_create(af, saddr, sport, &init_addr, 0);
+ if (*udp_mapping == NULL)
+ return (1);
+ }
+ }
bzero(&init_addr, sizeof(init_addr));
if (pf_map_addr(af, r, saddr, naddr, &init_addr, sn))
- return (1);
+ goto failed;
bzero(&key, sizeof(key));
key.af = af;
@@ -235,6 +260,8 @@
do {
PF_ACPY(&key.addr[1], naddr, key.af);
+ if ((*udp_mapping))
+ PF_ACPY(&(*udp_mapping)->endpoints[1].addr, naddr, af);
/*
* port search; start random, step;
@@ -262,8 +289,16 @@
} else if (low == high) {
key.port[1] = htons(low);
if (!pf_find_state_all_exists(&key, PF_IN)) {
- *nport = htons(low);
- return (0);
+ if (proto == IPPROTO_UDP) {
+ (*udp_mapping)->endpoints[1].port = htons(low);
+ if (pf_udp_mapping_insert(*udp_mapping) == 0) {
+ *nport = htons(low);
+ return (0);
+ }
+ } else {
+ *nport = htons(low);
+ return (0);
+ }
}
} else {
uint32_t tmp;
@@ -278,18 +313,34 @@
cut = arc4random() % (1 + high - low) + low;
/* low <= cut <= high */
for (tmp = cut; tmp <= high && tmp <= 0xffff; ++tmp) {
- key.port[1] = htons(tmp);
- if (!pf_find_state_all_exists(&key, PF_IN)) {
- *nport = htons(tmp);
- return (0);
+ if (proto == IPPROTO_UDP) {
+ (*udp_mapping)->endpoints[1].port = htons(tmp);
+ if (pf_udp_mapping_insert(*udp_mapping) == 0) {
+ *nport = htons(tmp);
+ return (0);
+ }
+ } else {
+ key.port[1] = htons(tmp);
+ if (!pf_find_state_all_exists(&key, PF_IN)) {
+ *nport = htons(tmp);
+ return (0);
+ }
}
}
tmp = cut;
for (tmp -= 1; tmp >= low && tmp <= 0xffff; --tmp) {
+ if (proto == IPPROTO_UDP) {
+ (*udp_mapping)->endpoints[1].port = htons(tmp);
+ if (pf_udp_mapping_insert(*udp_mapping) == 0) {
+ *nport = htons(tmp);
+ return (0);
+ }
+ } else {
key.port[1] = htons(tmp);
- if (!pf_find_state_all_exists(&key, PF_IN)) {
- *nport = htons(tmp);
- return (0);
+ if (!pf_find_state_all_exists(&key, PF_IN)) {
+ *nport = htons(tmp);
+ return (0);
+ }
}
}
}
@@ -311,14 +362,20 @@
return (1);
}
} while (! PF_AEQ(&init_addr, naddr, af) );
- return (1); /* none available */
+ /* none available */
+failed:
+ if (*udp_mapping) {
+ uma_zfree(V_pf_udp_mapping_z, *udp_mapping);
+ *udp_mapping = NULL;
+ }
+ return (1);
}
static int
pf_get_mape_sport(sa_family_t af, u_int8_t proto, struct pf_krule *r,
struct pf_addr *saddr, uint16_t sport, struct pf_addr *daddr,
uint16_t dport, struct pf_addr *naddr, uint16_t *nport,
- struct pf_ksrc_node **sn)
+ struct pf_ksrc_node **sn, struct pf_udp_mapping** udp_mapping)
{
uint16_t psmask, low, highmask;
uint16_t i, ahigh, cut;
@@ -338,13 +395,13 @@
for (i = cut; i <= ahigh; i++) {
low = (i << ashift) | psmask;
if (!pf_get_sport(af, proto, r, saddr, sport, daddr, dport,
- naddr, nport, low, low | highmask, sn))
+ naddr, nport, low, low | highmask, sn, udp_mapping))
return (0);
}
for (i = cut - 1; i > 0; i--) {
low = (i << ashift) | psmask;
if (!pf_get_sport(af, proto, r, saddr, sport, daddr, dport,
- naddr, nport, low, low | highmask, sn))
+ naddr, nport, low, low | highmask, sn, udp_mapping))
return (0);
}
return (1);
@@ -567,7 +624,8 @@
struct pfi_kkif *kif, struct pf_ksrc_node **sn,
struct pf_state_key **skp, struct pf_state_key **nkp,
struct pf_addr *saddr, struct pf_addr *daddr,
- uint16_t sport, uint16_t dport, struct pf_kanchor_stackframe *anchor_stack)
+ uint16_t sport, uint16_t dport, struct pf_kanchor_stackframe *anchor_stack,
+ struct pf_udp_mapping **udp_mapping)
{
struct pf_krule *r = NULL;
struct pf_addr *naddr;
@@ -629,7 +687,7 @@
}
if (r->rpool.mape.offset > 0) {
if (pf_get_mape_sport(pd->af, pd->proto, r, saddr,
- sport, daddr, dport, naddr, nport, sn)) {
+ sport, daddr, dport, naddr, nport, sn, udp_mapping)) {
DPFPRINTF(PF_DEBUG_MISC,
("pf: MAP-E port allocation (%u/%u/%u)"
" failed\n",
@@ -639,7 +697,7 @@
goto notrans;
}
} else if (pf_get_sport(pd->af, pd->proto, r, saddr, sport,
- daddr, dport, naddr, nport, low, high, sn)) {
+ daddr, dport, naddr, nport, low, high, sn, udp_mapping)) {
DPFPRINTF(PF_DEBUG_MISC,
("pf: NAT proxy port allocation (%u-%u) failed\n",
r->rpool.proxy_port[0], r->rpool.proxy_port[1]));
diff --git a/tests/sys/netpfil/pf/nat.sh b/tests/sys/netpfil/pf/nat.sh
--- a/tests/sys/netpfil/pf/nat.sh
+++ b/tests/sys/netpfil/pf/nat.sh
@@ -119,8 +119,93 @@
pft_cleanup
}
+atf_test_case "endpoint_independent" "cleanup"
+endpoint_independent_head()
+{
+ atf_set descr 'Test that a client behind NAT gets the same external IP:port for different servers'
+ atf_set require.user root
+}
+
+endpoint_independent_body()
+{
+ pft_init
+
+ epair_client=$(vnet_mkepair)
+ epair_nat=$(vnet_mkepair)
+ epair_server1=$(vnet_mkepair)
+ epair_server2=$(vnet_mkepair)
+ bridge=$(vnet_mkbridge)
+
+ vnet_mkjail nat ${epair_client}b ${epair_nat}a
+ vnet_mkjail client ${epair_client}a
+ vnet_mkjail server1 ${epair_server1}a
+ vnet_mkjail server2 ${epair_server2}a
+
+ ifconfig ${epair_server1}b up
+ ifconfig ${epair_server2}b up
+ ifconfig ${epair_nat}b up
+ ifconfig ${bridge} \
+ addm ${epair_server1}b \
+ addm ${epair_server2}b \
+ addm ${epair_nat}b \
+ up
+
+ jexec nat ifconfig ${epair_client}b 192.0.2.1/24 up
+ jexec nat ifconfig ${epair_nat}a 10.42.42.42/8 up
+ jexec nat sysctl net.inet.ip.forwarding=1
+
+ jexec client ifconfig ${epair_client}a 192.0.2.2/24 up
+ jexec client route add default 192.0.2.1
+
+ jexec server1 ifconfig ${epair_server1}a 10.32.32.32/8 up
+ jexec server2 ifconfig ${epair_server2}a 10.22.22.22/8 up
+
+ # Enable pf!
+ jexec nat pfctl -e
+ pft_set_rules nat \
+ "nat on ${epair_nat}a inet from ! (${epair_nat}a) to any -> (${epair_nat}a)"
+
+ jexec server1 nc -u -l 1234 -v 2> server1.out &
+ server1pid="$!"
+ jexec server2 nc -u -l 1234 -v 2> server2.out &
+ server2pid="$!"
+
+ # send out three packets because sometimes one fails to go through
+ for i in $(seq 1 3); do
+ echo "ping" | jexec client nc -u 10.32.32.32 1234 -p 4242 -w 0
+ echo "ping" | jexec client nc -u 10.22.22.22 1234 -p 4242 -w 0
+ done
+
+ ipport_server1=$(cat server1.out | grep Connection)
+ ipport_server2=$(cat server2.out | grep Connection)
+
+ if [ -z "$ipport_server1" ]; then
+ atf_fail server1 did not receive connection from client
+ fi
+
+ if [ -z "$ipport_server2" ]; then
+ atf_fail server2 did not receive connection from client
+ fi
+
+ if [ ! "$ipport_server1" = "$ipport_server2" ]; then
+ echo "server1: $ipport_server1"
+ echo "server2: $ipport_server2"
+ atf_fail Received different IP:port on server1 than server2
+ fi
+ kill $server1pid
+ kill $server2pid
+}
+
+endpoint_independent_cleanup()
+{
+ pft_cleanup
+ rm -f server1.out
+ rm -f server2.out
+}
+
atf_init_test_cases()
{
atf_add_test_case "exhaust"
atf_add_test_case "nested_anchor"
+ atf_add_test_case "endpoint_independent"
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Apr 25, 4:23 AM (10 h, 28 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
32108973
Default Alt Text
D11137.id125490.diff (21 KB)
Attached To
Mode
D11137: pf: Add support for endpoint independent NAT bindings for UDP
Attached
Detach File
Event Timeline
Log In to Comment