Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F161517402
D46689.id144862.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
17 KB
Referenced Files
None
Subscribers
None
D46689.id144862.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D46689: LibAlias: implement RFC 4787 REQ 1 and 3 (full cone NAT)
Attached
Detach File
Event Timeline
Log In to Comment