From 67a50bc5356530d2988730acbab2f406fda0516e Mon Sep 17 00:00:00 2001 From: Damjan Jovanovic Date: Sat, 10 Jun 2017 15:26:04 +0000 Subject: [PATCH] Make PF's NAT use endpoint-independent mapping for UDP, conforming to RFC 4787 requirements 1 and 3. --- sys/net/pfvar.h | 44 ++++++++++- sys/netinet/libalias/alias_db.c | 96 ++++++++++++++++++++---- sys/netpfil/pf/pf.c | 162 +++++++++++++++++++++++++++++++++++++--- sys/netpfil/pf/pf_lb.c | 92 ++++++++++++++++++----- 4 files changed, 351 insertions(+), 43 deletions(-) diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index c5e8567..5b6664b 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -683,6 +683,29 @@ struct pf_state_peer { u_int8_t pad[1]; }; +/* Keep synced with struct pf_udp_endpoint. */ +struct pf_udp_endpoint_cmp { + struct pf_addr addr; + u_int16_t port; + sa_family_t af; + u_int8_t pad[1]; +}; + +struct pf_udp_endpoint { + struct pf_addr addr; + u_int16_t port; + sa_family_t af; + u_int8_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]; @@ -728,6 +751,7 @@ struct pf_state { union pf_rule_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_kif *kif; struct pfi_kif *rt_kif; struct pf_src_node *src_node; @@ -1452,6 +1476,11 @@ struct pf_srchash { 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; @@ -1465,8 +1494,10 @@ struct pf_idhash { extern u_long pf_hashmask; extern u_long pf_srchashmask; #define PF_HASHSIZ (32768) +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); @@ -1517,6 +1548,8 @@ VNET_DECLARE(uma_zone_t, pf_state_z); #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) @@ -1555,6 +1588,14 @@ pf_release_state(struct pf_state *s) extern struct pf_state *pf_find_state_byid(uint64_t, uint32_t); extern struct pf_state *pf_find_state_all(struct pf_state_key_cmp *, u_int, 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, u_int16_t src_port, + struct pf_addr *nat_addr, u_int16_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_src_node *pf_find_src_node(struct pf_addr *, struct pf_rule *, sa_family_t, int); extern void pf_unlink_src_node(struct pf_src_node *); @@ -1749,7 +1790,8 @@ struct pf_rule *pf_get_translation(struct pf_pdesc *, struct mbuf *, int, int, struct pfi_kif *, struct pf_src_node **, struct pf_state_key **, struct pf_state_key **, struct pf_addr *, struct pf_addr *, - uint16_t, uint16_t, struct pf_anchor_stackframe *); + uint16_t, uint16_t, struct pf_anchor_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/netinet/libalias/alias_db.c b/sys/netinet/libalias/alias_db.c index 4f8159b..3211990 100644 --- a/sys/netinet/libalias/alias_db.c +++ b/sys/netinet/libalias/alias_db.c @@ -385,9 +385,7 @@ Miscellaneous: /* Local prototypes */ static u_int StartPointIn(struct in_addr, u_short, int); -static u_int -StartPointOut(struct in_addr, struct in_addr, - u_short, u_short, int); +static u_int StartPointOut(struct in_addr, u_short, int); static int SeqDiff(u_long, u_long); @@ -422,16 +420,15 @@ StartPointIn(struct in_addr alias_addr, static u_int -StartPointOut(struct in_addr src_addr, struct in_addr dst_addr, - u_short src_port, u_short dst_port, int link_type) +StartPointOut(struct in_addr src_addr, + u_short src_port, + int link_type) { u_int n; n = src_addr.s_addr; - n += dst_addr.s_addr; if (link_type != LINK_PPTP) { n += src_port; - n += dst_port; } n += link_type; @@ -553,8 +550,15 @@ static struct alias_link * FindLinkOut (struct libalias *, struct in_addr, struct in_addr, u_short, u_short, int, int); static struct alias_link * + FindLinkOutBySource (struct libalias *, struct in_addr, u_short, int); + +static struct alias_link * FindLinkIn (struct libalias *, struct in_addr, struct in_addr, u_short, u_short, int, int); +static struct alias_link * + FindLinkInByAlias (struct libalias *, struct in_addr, u_short, int); + + #define ALIAS_PORT_BASE 0x08000 #define ALIAS_PORT_MASK 0x07fff @@ -594,6 +598,19 @@ GetNewPort(struct libalias *la, struct alias_link *lnk, int alias_port_param) */ max_trials = GET_NEW_PORT_MAX_ATTEMPTS; + if (lnk->link_type == LINK_UDP) { + /* + * For UDP, we try to reuse the alias address:port for all destinations + * from the same internal address:port, as per RFC 4787. + */ + struct alias_link *search_result; + search_result = FindLinkOutBySource(la, lnk->src_addr, lnk->src_port, LINK_UDP); + if (search_result != NULL) { + lnk->alias_port = search_result->alias_port; + return (0); + } + } + if (la->packetAliasMode & PKT_ALIAS_SAME_PORTS) { /* * When the PKT_ALIAS_SAME_PORTS option is chosen, @@ -626,9 +643,14 @@ GetNewPort(struct libalias *la, struct alias_link *lnk, int alias_port_param) int go_ahead; struct alias_link *search_result; - search_result = FindLinkIn(la, lnk->dst_addr, lnk->alias_addr, - lnk->dst_port, port_net, - lnk->link_type, 0); + if (lnk->link_type == LINK_UDP) { + search_result = FindLinkInByAlias(la, lnk->alias_addr, + port_net, lnk->link_type); + } else { + search_result = FindLinkIn(la, lnk->dst_addr, lnk->alias_addr, + lnk->dst_port, port_net, + lnk->link_type, 0); + } if (search_result == NULL) go_ahead = 1; @@ -1040,8 +1062,7 @@ AddLink(struct libalias *la, struct in_addr src_addr, struct in_addr dst_addr, } /* Set up pointers for output lookup table */ - start_point = StartPointOut(src_addr, dst_addr, - src_port, dst_port, link_type); + start_point = StartPointOut(src_addr, src_port, link_type); LIST_INSERT_HEAD(&la->linkTableOut[start_point], lnk, list_out); /* Set up pointers for input lookup table */ @@ -1100,7 +1121,7 @@ _FindLinkOut(struct libalias *la, struct in_addr src_addr, struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); - i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type); + i = StartPointOut(src_addr, src_port, link_type); LIST_FOREACH(lnk, &la->linkTableOut[i], list_out) { if (lnk->dst_addr.s_addr == dst_addr.s_addr && lnk->src_addr.s_addr == src_addr.s_addr && @@ -1169,6 +1190,51 @@ FindLinkOut(struct libalias *la, struct in_addr src_addr, static struct alias_link * +FindLinkOutBySource(struct libalias *la, struct in_addr src_addr, + u_short src_port, + int link_type) +{ + u_int i; + struct alias_link *lnk; + + LIBALIAS_LOCK_ASSERT(la); + + i = StartPointOut(src_addr, src_port, link_type); + LIST_FOREACH(lnk, &la->linkTableOut[i], list_out) { + if (lnk->src_addr.s_addr == src_addr.s_addr && + lnk->src_port == src_port && + lnk->link_type == link_type && + lnk->server == NULL) { + break; + } + } + return lnk; +} + + +static struct alias_link * +FindLinkInByAlias(struct libalias *la, struct in_addr alias_addr, + u_short alias_port, + int link_type) +{ + u_int i; + struct alias_link *lnk; + + LIBALIAS_LOCK_ASSERT(la); + + i = StartPointIn(alias_addr, alias_port, link_type); + LIST_FOREACH(lnk, &la->linkTableIn[i], list_in) { + if (lnk->alias_addr.s_addr == alias_addr.s_addr && + lnk->alias_port == alias_port && + lnk->link_type == link_type) { + break; + } + } + return lnk; +} + + +static struct alias_link * _FindLinkIn(struct libalias *la, struct in_addr dst_addr, struct in_addr alias_addr, u_short dst_port, @@ -1592,7 +1658,7 @@ FindPptpOutByCallId(struct libalias *la, struct in_addr src_addr, struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); - i = StartPointOut(src_addr, dst_addr, 0, 0, LINK_PPTP); + i = StartPointOut(src_addr, 0, LINK_PPTP); LIST_FOREACH(lnk, &la->linkTableOut[i], list_out) if (lnk->link_type == LINK_PPTP && lnk->src_addr.s_addr == src_addr.s_addr && @@ -1613,7 +1679,7 @@ FindPptpOutByPeerCallId(struct libalias *la, struct in_addr src_addr, struct alias_link *lnk; LIBALIAS_LOCK_ASSERT(la); - i = StartPointOut(src_addr, dst_addr, 0, 0, LINK_PPTP); + i = StartPointOut(src_addr, 0, LINK_PPTP); LIST_FOREACH(lnk, &la->linkTableOut[i], list_out) if (lnk->link_type == LINK_PPTP && lnk->src_addr.s_addr == src_addr.s_addr && diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 7d6c53d..9bd9b11 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -194,6 +194,7 @@ static VNET_DEFINE(uma_zone_t, pf_sources_z); 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(uint64_t, pf_stateid[MAXCPU]); #define PFID_CPUBITS 8 @@ -241,7 +242,7 @@ static int pf_create_state(struct pf_rule *, struct pf_rule *, struct pf_state_key *, struct mbuf *, int, u_int16_t, u_int16_t, int *, struct pfi_kif *, struct pf_state **, int, u_int16_t, u_int16_t, - int); + int, struct pf_udp_mapping *); static int pf_test_fragment(struct pf_rule **, int, struct pfi_kif *, struct mbuf *, void *, struct pf_pdesc *, struct pf_rule **, @@ -356,6 +357,7 @@ static MALLOC_DEFINE(M_PFHASH, "pf_hash", "pf(4) hash header structures"); 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, 0, "pf(4)"); @@ -426,6 +428,18 @@ pf_hashkey(struct pf_state_key *sk) } 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) { uint32_t h; @@ -779,6 +793,7 @@ pf_initialize() { struct pf_keyhash *kh; struct pf_idhash *ih; + struct pf_udpendpointhash *uh; struct pf_srchash *sh; u_int i; @@ -799,15 +814,20 @@ pf_initialize() 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 = malloc(pf_hashsize * sizeof(struct pf_keyhash), M_PFHASH, M_WAITOK | M_ZERO); V_pf_idhash = malloc(pf_hashsize * sizeof(struct pf_idhash), M_PFHASH, M_WAITOK | M_ZERO); + V_pf_udpendpointhash = malloc(pf_hashsize * sizeof(struct pf_udpendpointhash), + M_PFHASH, M_WAITOK | M_ZERO); 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. */ @@ -851,21 +871,26 @@ pf_cleanup() { 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), @@ -882,6 +907,7 @@ pf_cleanup() 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 @@ -1369,6 +1395,111 @@ second_run: return (ret); } +struct pf_udp_mapping* +pf_udp_mapping_create(sa_family_t af, struct pf_addr *src_addr, u_int16_t src_port, + struct pf_addr *nat_addr, u_int16_t nat_port) +{ + struct pf_udp_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 @@ -1650,6 +1781,9 @@ pf_unlink_state(struct pf_state *s, u_int flags) pf_detach_state(s); refcount_release(&s->refs); + if (s->udp_mapping) + pf_udp_mapping_release(s->udp_mapping); + return (pf_release_state(s)); } @@ -3136,6 +3270,7 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, u_int16_t bproto_sum = 0, bip_sum = 0; u_int8_t icmptype = 0, icmpcode = 0; struct pf_anchor_stackframe anchor_stack[PF_ANCHOR_STACKSIZE]; + struct pf_udp_mapping *udp_mapping = NULL; PF_RULES_RASSERT(); @@ -3199,7 +3334,7 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, /* check packet for BINAT/NAT/RDR */ if ((nr = pf_get_translation(pd, m, off, direction, 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__)); @@ -3506,14 +3641,19 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, 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); - if (action != PF_PASS) + hdrlen, udp_mapping); + if (action != PF_PASS) { + if (udp_mapping != NULL) + pf_udp_mapping_release(udp_mapping); return (action); + } } else { if (sk != NULL) uma_zfree(V_pf_state_key_z, sk); if (nk != NULL) 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 */ @@ -3538,6 +3678,8 @@ cleanup: uma_zfree(V_pf_state_key_z, sk); if (nk != NULL) uma_zfree(V_pf_state_key_z, nk); + if (udp_mapping != NULL) + pf_udp_mapping_release(udp_mapping); return (PF_DROP); } @@ -3546,7 +3688,8 @@ pf_create_state(struct pf_rule *r, struct pf_rule *nr, struct pf_rule *a, struct pf_pdesc *pd, struct pf_src_node *nsn, struct pf_state_key *nk, struct pf_state_key *sk, struct mbuf *m, int off, u_int16_t sport, u_int16_t dport, int *rewrite, struct pfi_kif *kif, struct pf_state **sm, - int tag, u_int16_t bproto_sum, u_int16_t bip_sum, int hdrlen) + int tag, u_int16_t bproto_sum, u_int16_t bip_sum, int hdrlen, + struct pf_udp_mapping *udp_mapping) { struct pf_state *s = NULL; struct pf_src_node *sn = NULL; @@ -3752,6 +3895,7 @@ pf_create_state(struct pf_rule *r, struct pf_rule *nr, struct pf_rule *a, 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 index eac5317..efa456d 100644 --- a/sys/netpfil/pf/pf_lb.c +++ b/sys/netpfil/pf/pf_lb.c @@ -63,7 +63,8 @@ static struct pf_rule *pf_match_translation(struct pf_pdesc *, struct mbuf *, uint16_t, int, struct pf_anchor_stackframe *); static int pf_get_sport(sa_family_t, uint8_t, struct pf_rule *, struct pf_addr *, uint16_t, struct pf_addr *, uint16_t, struct pf_addr *, - uint16_t *, uint16_t, uint16_t, struct pf_src_node **); + uint16_t *, uint16_t, uint16_t, struct pf_src_node **, + struct pf_udp_mapping**); #define mix(a,b,c) \ do { \ @@ -214,14 +215,36 @@ static int pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *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_src_node **sn) + uint16_t high, struct pf_src_node **sn, struct pf_udp_mapping **udp_mapping) { struct pf_state_key_cmp key; struct pf_addr init_addr; + 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, 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; if (proto == IPPROTO_ICMP) { low = 1; @@ -236,6 +259,8 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r, 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; @@ -255,8 +280,16 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r, } else if (low == high) { key.port[1] = htons(low); if (pf_find_state_all(&key, PF_IN, NULL) == NULL) { - *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 { uint16_t tmp, cut; @@ -270,19 +303,35 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r, cut = arc4random() % (1 + high - low) + low; /* low <= cut <= high */ for (tmp = cut; tmp <= high; ++(tmp)) { - key.port[1] = htons(tmp); - if (pf_find_state_all(&key, PF_IN, NULL) == - NULL) { - *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(&key, PF_IN, NULL) == + NULL) { + *nport = htons(tmp); + return (0); + } } } for (tmp = cut - 1; tmp >= low; --(tmp)) { - key.port[1] = htons(tmp); - if (pf_find_state_all(&key, PF_IN, NULL) == - NULL) { - *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(&key, PF_IN, NULL) == + NULL) { + *nport = htons(tmp); + return (0); + } } } } @@ -300,7 +349,13 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r, 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); } int @@ -512,7 +567,8 @@ pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, struct pfi_kif *kif, struct pf_src_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_anchor_stackframe *anchor_stack) + uint16_t sport, uint16_t dport, struct pf_anchor_stackframe *anchor_stack, + struct pf_udp_mapping **udp_mapping) { struct pf_rule *r = NULL; struct pf_addr *naddr; @@ -566,7 +622,7 @@ pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, case PF_NAT: if (pf_get_sport(pd->af, pd->proto, r, saddr, sport, daddr, dport, naddr, nport, r->rpool.proxy_port[0], - r->rpool.proxy_port[1], sn)) { + r->rpool.proxy_port[1], 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])); -- 2.6.4