Page MenuHomeFreeBSD

D46689.id144862.diff
No OneTemporary

D46689.id144862.diff

diff --git a/sys/netinet/libalias/alias.h b/sys/netinet/libalias/alias.h
--- a/sys/netinet/libalias/alias.h
+++ b/sys/netinet/libalias/alias.h
@@ -227,6 +227,30 @@
*/
#define PKT_ALIAS_UNREGISTERED_CGN 0x400
+/*
+ * If PKT_ALIAS_ENDPOINT_INDEPENDENT_MAPPING is set, UDP mapping is
+ * endpoint-independent as per RFC 4787 req 1 (a.k.a. "full cone" NAT of
+ * RFC 3489). This means that all UDP packets from the same internal
+ * address:port, are mapped to the same NAT address:port, regardless of their
+ * destination. Furthermore, if PKT_ALIAS_DENY_INCOMING is unset, any other
+ * external address:port can send to that internal address:port through the
+ * mapped NAT address:port. UDP applications can discover their NAT
+ * addess:port by using a STUN server, and communicate it to others who can
+ * contact them there, which is highly desirable for VPNs, VOIP, peer-to-peer,
+ * games, etc.
+ *
+ * If this bit is unset, UDP mapping is address and port-dependent
+ * ("symmetric" NAT). The internal application cannot know or predict the NAT
+ * address:port, and is crippled to communicating with only a single external
+ * address:port that is known ahead of time. Two such applications each behind
+ * its own such NAT can only connect to each other by tunnelling through an
+ * intermediate server (eg. TURN, RFC 8656). However, up to 2^32 UDP mappings
+ * can be created per external interface, while with the bit set, only 2^16
+ * can be. This bit is set after a call to PacketAliasInit(), so it is
+ * a default mode of operation.
+ */
+#define PKT_ALIAS_ENDPOINT_INDEPENDENT_MAPPING 0x800
+
/* Function return codes. */
#define PKT_ALIAS_ERROR -1
#define PKT_ALIAS_OK 1
diff --git a/sys/netinet/libalias/alias_db.h b/sys/netinet/libalias/alias_db.h
--- a/sys/netinet/libalias/alias_db.h
+++ b/sys/netinet/libalias/alias_db.h
@@ -292,6 +292,7 @@
struct {
SPLAY_ENTRY(alias_link) out;
LIST_ENTRY (alias_link) in;
+ SPLAY_ENTRY(alias_link) source;
} all;
struct {
LIST_ENTRY (alias_link) list;
@@ -374,6 +375,17 @@
}
SPLAY_PROTOTYPE(splay_in, group_in, in, cmp_in);
+static inline int
+cmp_source(struct alias_link *a, struct alias_link *b) {
+ int i = a->link_type - b->link_type;
+ if (i != 0) return (i);
+ if (a->src_addr.s_addr > b->src_addr.s_addr) return (1);
+ if (a->src_addr.s_addr < b->src_addr.s_addr) return (-1);
+ i = a->src_port - b->src_port;
+ return (i);
+}
+SPLAY_PROTOTYPE(splay_source, alias_link, all.source, cmp_source);
+
/* Internal routines for finding, deleting and adding links
Port Allocation:
@@ -390,6 +402,7 @@
Link search:
FindLinkOut() - find link for outgoing packets
FindLinkIn() - find link for incoming packets
+ FindLinkBySource() - find link by a packet's source address and port
Port search:
FindNewPortGroup() - find an available group of ports
@@ -417,6 +430,9 @@
static struct alias_link *
FindLinkIn(struct libalias *, struct in_addr, struct in_addr, u_short, u_short, int, int);
+static struct alias_link *
+FindLinkBySource(struct libalias *, struct in_addr, u_short, int);
+
static u_short _RandomPort(struct libalias *la);
#define GET_NEW_PORT_MAX_ATTEMPTS 20
diff --git a/sys/netinet/libalias/alias_db.c b/sys/netinet/libalias/alias_db.c
--- a/sys/netinet/libalias/alias_db.c
+++ b/sys/netinet/libalias/alias_db.c
@@ -93,6 +93,7 @@
SPLAY_GENERATE(splay_out, alias_link, all.out, cmp_out);
SPLAY_GENERATE(splay_in, group_in, in, cmp_in);
+SPLAY_GENERATE(splay_source, alias_link, all.source, cmp_source);
static struct group_in *
StartPointIn(struct libalias *la,
@@ -235,6 +236,19 @@
max_trials = GET_NEW_PORT_MAX_ATTEMPTS;
+ if ((la->packetAliasMode & PKT_ALIAS_ENDPOINT_INDEPENDENT_MAPPING) &&
+ lnk->link_type == LINK_UDP) {
+ /* Try reuse the same alias address:port for all destinations
+ * from the same internal address:port, as per RFC 4787.
+ */
+ struct alias_link *search_result = FindLinkBySource(la,
+ lnk->src_addr, lnk->src_port, lnk->link_type);
+ if (search_result != NULL) {
+ lnk->alias_port = search_result->alias_port;
+ return (0);
+ }
+ }
+
/*
* When the PKT_ALIAS_SAME_PORTS option is chosen,
* the first try will be the actual source port. If
@@ -254,13 +268,20 @@
if (grp == NULL)
break;
- LIST_FOREACH(search_result, &grp->full, all.in) {
- if (lnk->dst_addr.s_addr == search_result->dst_addr.s_addr &&
- lnk->dst_port == search_result->dst_port)
- break; /* found match */
+ /* As per RFC 4787, UDP cannot share the same alias port among
+ * multiple internal endpoints
+ */
+ if (!(la->packetAliasMode & PKT_ALIAS_ENDPOINT_INDEPENDENT_MAPPING) ||
+ lnk->link_type != LINK_UDP) {
+ LIST_FOREACH(search_result, &grp->full, all.in) {
+ if (lnk->dst_addr.s_addr ==
+ search_result->dst_addr.s_addr &&
+ lnk->dst_port == search_result->dst_port)
+ break; /* found match */
+ }
+ if (search_result == NULL)
+ break;
}
- if (search_result == NULL)
- break;
}
if (i >= max_trials) {
@@ -496,6 +517,9 @@
/* Adjust input table pointers */
LIST_REMOVE(lnk, all.in);
+ /* Adjust "by source" table pointer */
+ SPLAY_REMOVE(splay_source, &la->linkSplayBySource, lnk);
+
/* Remove intermediate node, if empty */
grp = StartPointIn(la, lnk->alias_addr, lnk->alias_port, lnk->link_type, 0);
if (grp != NULL &&
@@ -696,6 +720,9 @@
LIST_INSERT_HEAD(&grp->partial, lnk, all.in);
else
LIST_INSERT_HEAD(&grp->full, lnk, all.in);
+
+ /* Set up pointers for "by source" lookup table */
+ SPLAY_INSERT(splay_source, &la->linkSplayBySource, lnk);
}
break;
}
@@ -964,6 +991,14 @@
lnk = _FindLinkIn(la, dst_addr, alias_addr, dst_port, alias_port,
link_type, replace_partial_links);
+ if (lnk == NULL &&
+ (la->packetAliasMode & PKT_ALIAS_ENDPOINT_INDEPENDENT_MAPPING) &&
+ link_type == LINK_UDP &&
+ !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) {
+ lnk = _FindLinkIn(la, ANY_ADDR, alias_addr, 0, alias_port,
+ link_type, replace_partial_links);
+ }
+
if (lnk == NULL) {
/*
* The following allows permanent links to be specified as
@@ -980,6 +1015,20 @@
return (lnk);
}
+static struct alias_link *
+FindLinkBySource(struct libalias *la, struct in_addr src_addr,
+ u_short src_port,
+ int link_type)
+{
+ struct alias_link needle = {
+ .src_addr = src_addr,
+ .src_port = src_port,
+ .link_type = link_type
+ };
+ LIBALIAS_LOCK_ASSERT(la);
+ return SPLAY_FIND(splay_source, &la->linkSplayBySource, &needle);
+}
+
/* External routines for finding/adding links
-- "external" means outside alias_db.c, but within alias*.c --
@@ -2110,6 +2159,7 @@
SPLAY_INIT(&la->linkSplayIn);
SPLAY_INIT(&la->linkSplayOut);
+ SPLAY_INIT(&la->linkSplayBySource);
LIST_INIT(&la->pptpList);
TAILQ_INIT(&la->checkExpire);
#ifdef _KERNEL
@@ -2145,7 +2195,8 @@
#ifndef NO_USE_SOCKETS
| PKT_ALIAS_USE_SOCKETS
#endif
- | PKT_ALIAS_RESET_ON_ADDR_CHANGE;
+ | PKT_ALIAS_RESET_ON_ADDR_CHANGE
+ | PKT_ALIAS_ENDPOINT_INDEPENDENT_MAPPING;
#ifndef NO_FW_PUNCH
la->fireWallFD = -1;
#endif
diff --git a/sys/netinet/libalias/alias_local.h b/sys/netinet/libalias/alias_local.h
--- a/sys/netinet/libalias/alias_local.h
+++ b/sys/netinet/libalias/alias_local.h
@@ -94,10 +94,11 @@
* if no aliasing link already exists */
struct in_addr targetAddress;
/* Lookup table of pointers to chains of link records.
- * Each link record is doubly indexed into input and
- * output lookup tables. */
+ * Each link record is indexed into input,
+ * output and "by source" lookup tables. */
SPLAY_HEAD(splay_out, alias_link) linkSplayOut;
SPLAY_HEAD(splay_in, group_in) linkSplayIn;
+ SPLAY_HEAD(splay_source, alias_link) linkSplayBySource;
LIST_HEAD (, alias_link) pptpList;
/* HouseKeeping */
TAILQ_HEAD (, alias_link) checkExpire;
diff --git a/sys/netinet/libalias/libalias.3 b/sys/netinet/libalias/libalias.3
--- a/sys/netinet/libalias/libalias.3
+++ b/sys/netinet/libalias/libalias.3
@@ -92,6 +92,8 @@
.Dv PKT_ALIAS_USE_SOCKETS
.It
.Dv PKT_ALIAS_RESET_ON_ADDR_CHANGE
+.It
+.Dv PKT_ALIAS_ENDPOINT_INDEPENDENT_MAPPING
.El
.Pp
This function will always return the packet aliasing engine to the same
@@ -270,6 +272,24 @@
in
.Xr ipfw 8
for more details.
+.It Dv PKT_ALIAS_ENDPOINT_INDEPENDENT_MAPPING
+If this bit is set, UDP mapping is endpoint-independent as per RFC 4787 req 1
+(a.k.a. "full cone" NAT of RFC 3489). This means that all UDP packets from the
+same internal address:port, are mapped to the same NAT address:port,
+regardless of their destination. Furthermore, if PKT_ALIAS_DENY_INCOMING is
+unset, any other external address:port can send to that internal address:port
+through the mapped NAT address:port. UDP applications can discover their NAT
+addess:port by using a STUN server, and communicate it to others who can
+contact them there, which is highly desirable for VPNs, VOIP, peer-to-peer,
+games, etc.
+.Pp
+If this bit is unset, UDP mapping is address and port-dependent ("symmetric"
+NAT). The internal application cannot know or predict the NAT address:port,
+and is crippled to communicating with only a single external address:port that
+is known ahead of time. Two such applications each behind its own such NAT can
+only connect to each other by tunnelling through an intermediate server (eg.
+TURN, RFC 8656). However, up to 2^32 UDP mappings can be created per external
+interface, while with the bit set, only 2^16 can be.
.El
.Ed
.Pp
diff --git a/tests/sys/netinet/libalias/2_natout.c b/tests/sys/netinet/libalias/2_natout.c
--- a/tests/sys/netinet/libalias/2_natout.c
+++ b/tests/sys/netinet/libalias/2_natout.c
@@ -311,7 +311,12 @@
struct libalias *la = LibAliasInit(NULL);
struct ip *po;
struct udphdr *uo;
- uint16_t sport = 0x1234;
+ uint16_t sport1 = 0x1234;
+ uint16_t sport2 = 0x1235;
+ uint16_t sport3 = 0x1236;
+ uint16_t sport4 = 0x1237;
+ uint16_t sport5 = 0x1238;
+ uint16_t sport6 = 0x1239;
uint16_t dport = 0x5678;
uint16_t aport;
@@ -321,37 +326,35 @@
po = ip_packet(0, 64);
LibAliasSetAliasPortRange(la, 0, 0); /* reinit like ipfw */
- UDP_NAT_CHECK(po, uo, prv1, sport, ext, dport, masq);
+ UDP_NAT_CHECK(po, uo, prv1, sport1, ext, dport, masq);
aport = ntohs(uo->uh_sport);
ATF_CHECK(aport >= 0x8000);
/* Different larger range */
LibAliasSetAliasPortRange(la, 2000, 3000);
dport++;
- UDP_NAT_CHECK(po, uo, prv1, sport, ext, dport, masq);
+ UDP_NAT_CHECK(po, uo, prv1, sport2, ext, dport, masq);
aport = ntohs(uo->uh_sport);
ATF_CHECK(aport >= 2000 && aport < 3000);
/* Different small range (contains two ports) */
LibAliasSetAliasPortRange(la, 4000, 4001);
dport++;
- UDP_NAT_CHECK(po, uo, prv1, sport, ext, dport, masq);
+ UDP_NAT_CHECK(po, uo, prv1, sport3, ext, dport, masq);
aport = ntohs(uo->uh_sport);
ATF_CHECK(aport >= 4000 && aport <= 4001);
- sport++;
- UDP_NAT_CHECK(po, uo, prv1, sport, ext, dport, masq);
+ UDP_NAT_CHECK(po, uo, prv1, sport4, ext, dport, masq);
aport = ntohs(uo->uh_sport);
ATF_CHECK(aport >= 4000 && aport <= 4001);
/* Third port not available in the range */
- sport++;
- UDP_NAT_FAIL(po, uo, prv1, sport, ext, dport);
+ UDP_NAT_FAIL(po, uo, prv1, sport5, ext, dport);
/* Back to normal */
LibAliasSetAliasPortRange(la, 0, 0);
dport++;
- UDP_NAT_CHECK(po, uo, prv1, sport, ext, dport, masq);
+ UDP_NAT_CHECK(po, uo, prv1, sport6, ext, dport, masq);
aport = ntohs(uo->uh_sport);
ATF_CHECK(aport >= 0x8000);
@@ -359,6 +362,172 @@
LibAliasUninit(la);
}
+ATF_TC_WITHOUT_HEAD(9_udp_mapping);
+ATF_TC_BODY(9_udp_mapping, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct ip *po, *po2, *po3;
+ struct udphdr *uo, *uo2, *uo3;
+ uint16_t sport = 0x1234;
+ uint16_t dport = 0x5678;
+ uint16_t dport2 = 0x6789;
+ uint16_t aport, aport2, aport3;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetAddress(la, masq);
+ LibAliasSetMode(la, PKT_ALIAS_ENDPOINT_INDEPENDENT_MAPPING, ~0);
+
+ po = ip_packet(0, 64);
+ UDP_NAT_CHECK(po, uo, prv1, sport, ext, dport, masq);
+ aport = ntohs(uo->uh_sport);
+
+ /* Change of dst port shouldn't change alias port */
+ po2 = ip_packet(0, 64);
+ UDP_NAT_CHECK(po2, uo2, prv1, sport, ext, dport2, masq);
+ aport2 = ntohs(uo2->uh_sport);
+ ATF_CHECK_EQ_MSG(aport, aport2,
+ "NAT uses address- and port-dependent mapping (%uh -> %uh)",
+ aport, aport2);
+
+ /* Change of dst address shouldn't change alias port */
+ po3 = ip_packet(0, 64);
+ UDP_NAT_CHECK(po3, uo3, prv1, sport, pub, dport, masq);
+ aport3 = ntohs(uo3->uh_sport);
+ ATF_CHECK_EQ_MSG(aport, aport3, "NAT uses address-dependent mapping");
+
+ free(po);
+ free(po2);
+ free(po3);
+ LibAliasUninit(la);
+}
+
+ATF_TC_WITHOUT_HEAD(10_udp_out_in);
+ATF_TC_BODY(10_udp_out_in, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct ip *po, *po2, *po3;
+ struct udphdr *uo, *uo2, *uo3;
+ uint16_t sport = 0x1234;
+ uint16_t dport = 0x5678;
+ uint16_t dport2 = 0x6789;
+ uint16_t aport;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetAddress(la, masq);
+ LibAliasSetMode(la, PKT_ALIAS_ENDPOINT_INDEPENDENT_MAPPING, ~0);
+
+ po = ip_packet(0, 64);
+ UDP_NAT_CHECK(po, uo, prv1, sport, pub, dport, masq);
+ aport = ntohs(uo->uh_sport);
+
+ /* Accepts inbound packets from different port */
+ po2 = ip_packet(0, 64);
+ UDP_UNNAT_CHECK(po2, uo2, pub, dport2, masq, aport, prv1, sport);
+
+ /* Accepts inbound packets from differerent host and port */
+ po3 = ip_packet(0, 64);
+ UDP_UNNAT_CHECK(po3, uo3, pub2, dport2, masq, aport, prv1, sport);
+
+ free(po);
+ free(po2);
+ free(po3);
+ LibAliasUninit(la);
+}
+
+ATF_TC_WITHOUT_HEAD(11_udp_with_deny_incoming);
+ATF_TC_BODY(11_udp_with_deny_incoming, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct ip *po, *po2, *po3, *po4;
+ struct udphdr *uo;
+ uint16_t sport = 0x1234;
+ uint16_t dport = 0x5678;
+ uint16_t dport2 = 0x6789;
+ uint16_t aport;
+ int ret;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetAddress(la, masq);
+ LibAliasSetMode(la,
+ PKT_ALIAS_ENDPOINT_INDEPENDENT_MAPPING | PKT_ALIAS_DENY_INCOMING,
+ ~0);
+
+ po = ip_packet(0, 64);
+ UDP_NAT_CHECK(po, uo, prv1, sport, pub, dport, masq);
+ aport = ntohs(uo->uh_sport);
+
+ po2 = ip_packet(0, 64);
+ po2->ip_src = pub;
+ po2->ip_dst = masq;
+ set_udp(po2, dport, aport);
+ ret = LibAliasIn(la, po2, 64);
+ ATF_CHECK_EQ_MSG(PKT_ALIAS_OK, ret,
+ "LibAliasIn failed with error %d\n", ret);
+
+ po3 = ip_packet(0, 64);
+ po3->ip_src = pub;
+ po3->ip_dst = masq;
+ set_udp(po3, dport2, aport);
+ ret = LibAliasIn(la, po3, 64);
+ ATF_CHECK_EQ_MSG(PKT_ALIAS_IGNORED, ret,
+ "incoming packet from different port not ignored "
+ "with PKT_ALIAS_DENY_INCOMING");
+
+ po4 = ip_packet(0, 64);
+ po4->ip_src = pub2;
+ po4->ip_dst = masq;
+ set_udp(po4, dport2, aport);
+ ret = LibAliasIn(la, po4, 64);
+ ATF_CHECK_EQ_MSG(PKT_ALIAS_IGNORED, ret,
+ "incoming packet from different address and port not ignored "
+ "with PKT_ALIAS_DENY_INCOMING");
+
+ free(po);
+ free(po2);
+ free(po3);
+ free(po4);
+ LibAliasUninit(la);
+}
+
+ATF_TC_WITHOUT_HEAD(12_udp_hairpinning);
+ATF_TC_BODY(12_udp_hairpinning, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct ip *po, *po2, *po3;
+ struct udphdr *uo, *uo2, *uo3;
+ uint16_t sport1 = 0x1234;
+ uint16_t sport2 = 0x2345;
+ uint16_t dport = 0x5678;
+ uint16_t extport1, extport2;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetAddress(la, masq);
+ LibAliasSetMode(la, PKT_ALIAS_ENDPOINT_INDEPENDENT_MAPPING, ~0);
+
+ /* prv1 sends out somewhere (eg. a STUN server) */
+ po = ip_packet(0, 64);
+ UDP_NAT_CHECK(po, uo, prv1, sport1, pub, dport, masq);
+ extport1 = ntohs(uo->uh_sport);
+
+ /* prv2, behind the same NAT as prv1, also sends out somewhere */
+ po2 = ip_packet(0, 64);
+ UDP_NAT_CHECK(po2, uo2, prv2, sport2, pub, dport, masq);
+ extport2 = ntohs(uo2->uh_sport);
+
+ /* hairpin: prv1 sends to prv2's external NAT mapping
+ * (unaware it could address it internally instead).
+ */
+ po3 = ip_packet(0, 64);
+ UDP_NAT_CHECK(po3, uo3, prv1, sport1, masq, extport2, masq);
+ UDP_UNNAT_CHECK(po3, uo3, masq, extport1, masq, extport2,
+ prv2, sport2);
+
+ free(po);
+ free(po2);
+ free(po3);
+ LibAliasUninit(la);
+}
+
ATF_TP_ADD_TCS(natout)
{
/* Use "dd if=/dev/random bs=2 count=1 | od -x" to reproduce */
@@ -372,6 +541,10 @@
ATF_TP_ADD_TC(natout, 6_cleartable);
ATF_TP_ADD_TC(natout, 7_stress);
ATF_TP_ADD_TC(natout, 8_portrange);
+ ATF_TP_ADD_TC(natout, 9_udp_mapping);
+ ATF_TP_ADD_TC(natout, 10_udp_out_in);
+ ATF_TP_ADD_TC(natout, 11_udp_with_deny_incoming);
+ ATF_TP_ADD_TC(natout, 12_udp_hairpinning);
return atf_no_error();
}
diff --git a/tests/sys/netinet/libalias/util.h b/tests/sys/netinet/libalias/util.h
--- a/tests/sys/netinet/libalias/util.h
+++ b/tests/sys/netinet/libalias/util.h
@@ -41,7 +41,7 @@
#define _UTIL_H
/* common ip ranges */
-extern struct in_addr masq, pub, prv1, prv2, prv3, cgn, ext, ANY_ADDR;
+extern struct in_addr masq, pub, pub2, prv1, prv2, prv3, cgn, ext, ANY_ADDR;
int randcmp(const void *a, const void *b);
void hexdump(void *p, size_t len);
diff --git a/tests/sys/netinet/libalias/util.c b/tests/sys/netinet/libalias/util.c
--- a/tests/sys/netinet/libalias/util.c
+++ b/tests/sys/netinet/libalias/util.c
@@ -41,6 +41,7 @@
/* common ip ranges */
struct in_addr masq = { htonl(0x01020304) };
struct in_addr pub = { htonl(0x0102dead) };
+struct in_addr pub2 = { htonl(0x0102beef) };
struct in_addr prv1 = { htonl(0x0a00dead) };
struct in_addr prv2 = { htonl(0xac10dead) };
struct in_addr prv3 = { htonl(0xc0a8dead) };

File Metadata

Mime Type
text/plain
Expires
Sun, Jul 5, 11:46 AM (9 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34708018
Default Alt Text
D46689.id144862.diff (17 KB)

Event Timeline