diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -3403,6 +3403,26 @@ .It Cm port_range Ar lower-upper Set the aliasing ports between the ranges given. Upper port has to be greater than lower. +.It Cm udp_eim +When enabled, UDP packets use endpoint-independent mapping (EIM) from RFC 4787 +("full cone" NAT of RFC 3489). +All packets from the same internal address:port are mapped to the same NAT +address:port, regardless of their destination address:port. +If filtering rules allow, and if +.Em deny_in +is unset, any other external address:port can +also send to the internal address:port through its mapped NAT address:port. +This is more compatible with applications, and can reduce the need for port +forwarding, but less scalable as each NAT address:port can only be +concurrently used by at most one internal address:port. +.Pp +When disabled, UDP packets use endpoint-dependent mapping (EDM) ("symmetric" +NAT). +Each connection from a particular internal address:port to different +external addresses:ports is mapped to a random and unpredictable NAT +address:port. +Two appplications behind EDM NATs can only connect to each other +by port forwarding on the NAT, or tunnelling through an in-between server. .El .Pp Some special values can be supplied instead of diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h --- a/sbin/ipfw/ipfw2.h +++ b/sbin/ipfw/ipfw2.h @@ -324,6 +324,7 @@ TOK_SETMARK, TOK_SKIPACTION, + TOK_UDP_EIM, }; /* diff --git a/sbin/ipfw/main.c b/sbin/ipfw/main.c --- a/sbin/ipfw/main.c +++ b/sbin/ipfw/main.c @@ -43,7 +43,7 @@ "add [num] [set N] [prob x] RULE-BODY\n" "{pipe|queue} N config PIPE-BODY\n" "[pipe|queue] {zero|delete|show} [N{,N}]\n" -"nat N config {ip IPADDR|if IFNAME|log|deny_in|same_ports|unreg_only|unreg_cgn|\n" +"nat N config {ip IPADDR|if IFNAME|log|deny_in|same_ports|unreg_only|unreg_cgn|udp_eim|\n" " reset|reverse|proxy_only|redirect_addr linkspec|\n" " redirect_port linkspec|redirect_proto linkspec|\n" " port_range lower-upper}\n" diff --git a/sbin/ipfw/nat.c b/sbin/ipfw/nat.c --- a/sbin/ipfw/nat.c +++ b/sbin/ipfw/nat.c @@ -67,6 +67,7 @@ { "redirect_addr", TOK_REDIR_ADDR }, { "redirect_port", TOK_REDIR_PORT }, { "redirect_proto", TOK_REDIR_PROTO }, + { "udp_eim", TOK_UDP_EIM }, { NULL, 0 } /* terminator */ }; @@ -676,6 +677,9 @@ } else if (n->mode & PKT_ALIAS_PROXY_ONLY) { printf(" proxy_only"); n->mode &= ~PKT_ALIAS_PROXY_ONLY; + } else if (n->mode & PKT_ALIAS_UDP_EIM) { + printf(" udp_eim"); + n->mode &= ~PKT_ALIAS_UDP_EIM; } } /* Print all the redirect's data configuration. */ @@ -821,6 +825,7 @@ case TOK_RESET_ADDR: case TOK_ALIAS_REV: case TOK_PROXY_ONLY: + case TOK_UDP_EIM: break; case TOK_REDIR_ADDR: if (ac1 < 2) @@ -927,6 +932,9 @@ case TOK_PROXY_ONLY: n->mode |= PKT_ALIAS_PROXY_ONLY; break; + case TOK_UDP_EIM: + n->mode |= PKT_ALIAS_UDP_EIM; + break; /* * All the setup_redir_* functions work directly in * the final buffer, see above for details. diff --git a/sbin/natd/natd.8 b/sbin/natd/natd.8 --- a/sbin/natd/natd.8 +++ b/sbin/natd/natd.8 @@ -14,6 +14,7 @@ .Op Fl deny_incoming | d .Op Fl use_sockets | s .Op Fl same_ports | m +.Op Fl udp_eim .Op Fl verbose | v .Op Fl dynamic .Op Fl in_port | i Ar port @@ -114,6 +115,26 @@ of working. If it is not possible to maintain the port number, it will be silently changed as per normal. +.It Fl udp_eim +When enabled, UDP packets use endpoint-independent mapping (EIM) from RFC 4787 +("full cone" NAT of RFC 3489). +All packets from the same internal address:port are mapped to the same NAT +address:port, regardless of their destination address:port. +If filtering rules allow, and if +.Em deny_incoming +is disabled, any other external address:port can +also send to the internal address:port through its mapped NAT address:port. +This is more compatible with applications, and can reduce the need for port +forwarding, but less scalable as each NAT address:port can only be +concurrently used by at most one internal address:port. +.Pp +When disabled, UDP packets use endpoint-dependent mapping (EDM) ("symmetric" +NAT). +Each connection from a particular internal address:port to different +external addresses:ports is mapped to a random and unpredictable NAT +address:port. +Two appplications behind EDM NATs can only connect to each other +by port forwarding on the NAT, or tunnelling through an in-between server. .It Fl verbose | v Do not call .Xr daemon 3 diff --git a/sbin/natd/natd.c b/sbin/natd/natd.c --- a/sbin/natd/natd.c +++ b/sbin/natd/natd.c @@ -1138,6 +1138,14 @@ "same_ports", "m" }, + { LibAliasOption, + PKT_ALIAS_UDP_EIM, + YesNo, + "[yes|no]", + "UDP traffic uses endpoint-independent mapping (\"full cone\" NAT)", + "udp_eim", + NULL }, + { Verbose, 0, YesNo, diff --git a/share/man/man4/ng_nat.4 b/share/man/man4/ng_nat.4 --- a/share/man/man4/ng_nat.4 +++ b/share/man/man4/ng_nat.4 @@ -74,6 +74,7 @@ #define NG_NAT_PROXY_ONLY 0x40 #define NG_NAT_REVERSE 0x80 #define NG_NAT_UNREGISTERED_CGN 0x100 +#define NG_NAT_UDP_EIM 0x800 .Ed .Pp The corresponding libalias flags can be found by replacing the diff --git a/sys/netgraph/ng_nat.h b/sys/netgraph/ng_nat.h --- a/sys/netgraph/ng_nat.h +++ b/sys/netgraph/ng_nat.h @@ -53,6 +53,7 @@ #define NG_NAT_PROXY_ONLY 0x40 #define NG_NAT_REVERSE 0x80 #define NG_NAT_UNREGISTERED_CGN 0x100 +#define NG_NAT_UDP_EIM 0x800 #define NG_NAT_DESC_LENGTH 64 #define NG_NAT_REDIRPROTO_ADDR (IPPROTO_MAX + 3) /* LibAlias' LINK_ADDR, also unused in in.h */ diff --git a/sys/netgraph/ng_nat.c b/sys/netgraph/ng_nat.c --- a/sys/netgraph/ng_nat.c +++ b/sys/netgraph/ng_nat.c @@ -967,6 +967,8 @@ res |= PKT_ALIAS_REVERSE; if (x & NG_NAT_UNREGISTERED_CGN) res |= PKT_ALIAS_UNREGISTERED_CGN; + if (x & NG_NAT_UDP_EIM) + res |= PKT_ALIAS_UDP_EIM; return (res); } 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,26 @@ */ #define PKT_ALIAS_UNREGISTERED_CGN 0x400 +/* + * When this bit is set, UDP uses endpoint-independent mapping (EIM), as per + * RFC 4787 ("full cone" NAT of RFC 3489). All packets from the same internal + * address:port are mapped to the same NAT address:port, regardless of their + * destination address:port. If filtering rules allow, and if + * PKT_ALIAS_DENY_INCOMING is unset, any other external address:port can also + * send to the internal address:port through its mapped NAT address:port. This + * is more compatible with applications, and can reduce the need for port + * forwarding, but less scalable as each NAT address:port can only be + * concurrently used by at most one internal address:port. + * + * When this bit is unset, UDP packets use endpoint-dependent mapping (EDM) + * ("symmetric" NAT). Each connection from a particular internal address:port + * to different external addresses:ports is mapped to a random and + * unpredictable NAT address:port. Two appplications behind EDM NATs can only + * connect to each other by port forwarding on the NAT, or tunnelling through + * an in-between server. + */ +#define PKT_ALIAS_UDP_EIM 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 @@ -208,12 +208,14 @@ stored in the auxiliary space. Pointers to unresolved fragments can also be stored. - The link records support two independent chainings. Lookup + The link records support several independent chainings. Lookup tables for input and out tables hold the initial pointers the link chains. On input, the lookup table indexes on alias port and link type. On output, the lookup table indexes on source address, destination address, source port, destination - port and link type. + port and link type. A internal_endpoint table is used for + endpoint-independent mapping, and indexes on source address, + source port and link type. */ /* used to save changes to ACK/sequence numbers */ @@ -292,6 +294,7 @@ struct { SPLAY_ENTRY(alias_link) out; LIST_ENTRY (alias_link) in; + SPLAY_ENTRY(alias_link) internal_endpoint; } all; struct { LIST_ENTRY (alias_link) list; @@ -374,25 +377,38 @@ } SPLAY_PROTOTYPE(splay_in, group_in, in, cmp_in); +static inline int +cmp_internal_endpoint(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_internal_endpoint, alias_link, all.internal_endpoint, + cmp_internal_endpoint); + /* Internal routines for finding, deleting and adding links Port Allocation: - GetNewPort() -- find and reserve new alias port number - GetSocket() -- try to allocate a socket for a given port + GetNewPort() -- find and reserve new alias port number + GetSocket() -- try to allocate a socket for a given port Link creation and deletion: - CleanupAliasData() - remove all link chains from lookup table - CleanupLink() - look for a stale link - DeleteLink() - remove link - AddLink() - add link - ReLink() - change link + CleanupAliasData() - remove all link chains from lookup table + CleanupLink() - look for a stale link + DeleteLink() - remove link + AddLink() - add link + ReLink() - change link Link search: - FindLinkOut() - find link for outgoing packets - FindLinkIn() - find link for incoming packets + FindLinkOut() - find link for outgoing packets + FindLinkIn() - find link for incoming packets + FindLinkByInternalEndpoint() - find link by a packet's internal endpoint Port search: - FindNewPortGroup() - find an available group of ports + FindNewPortGroup() - find an available group of ports */ /* Local prototypes */ @@ -417,6 +433,9 @@ static struct alias_link * FindLinkIn(struct libalias *, struct in_addr, struct in_addr, u_short, u_short, int, int); +static struct alias_link * +FindLinkByInternalEndpoint(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,8 @@ SPLAY_GENERATE(splay_out, alias_link, all.out, cmp_out); SPLAY_GENERATE(splay_in, group_in, in, cmp_in); +SPLAY_GENERATE(splay_internal_endpoint, alias_link, all.internal_endpoint, + cmp_internal_endpoint); static struct group_in * StartPointIn(struct libalias *la, @@ -235,6 +237,19 @@ max_trials = GET_NEW_PORT_MAX_ATTEMPTS; + if ((la->packetAliasMode & PKT_ALIAS_UDP_EIM) && + 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 = FindLinkByInternalEndpoint( + 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,10 +269,18 @@ if (grp == NULL) break; + /* As per RFC 4787, UDP cannot share the same alias port among + * multiple internal endpoints + */ + if ((la->packetAliasMode & PKT_ALIAS_UDP_EIM) && + lnk->link_type == LINK_UDP) + continue; + LIST_FOREACH(search_result, &grp->full, all.in) { - if (lnk->dst_addr.s_addr == search_result->dst_addr.s_addr && + if (lnk->dst_addr.s_addr == + search_result->dst_addr.s_addr && lnk->dst_port == search_result->dst_port) - break; /* found match */ + break; /* found match */ } if (search_result == NULL) break; @@ -496,6 +519,10 @@ /* Adjust input table pointers */ LIST_REMOVE(lnk, all.in); + /* Adjust "internal endpoint" table pointer */ + SPLAY_REMOVE(splay_internal_endpoint, + &la->linkSplayInternalEndpoint, lnk); + /* Remove intermediate node, if empty */ grp = StartPointIn(la, lnk->alias_addr, lnk->alias_port, lnk->link_type, 0); if (grp != NULL && @@ -696,6 +723,10 @@ LIST_INSERT_HEAD(&grp->partial, lnk, all.in); else LIST_INSERT_HEAD(&grp->full, lnk, all.in); + + /* Set up pointers for "internal endpoint" lookup table */ + SPLAY_INSERT(splay_internal_endpoint, + &la->linkSplayInternalEndpoint, lnk); } break; } @@ -964,6 +995,14 @@ lnk = _FindLinkIn(la, dst_addr, alias_addr, dst_port, alias_port, link_type, replace_partial_links); + if (lnk == NULL && + (la->packetAliasMode & PKT_ALIAS_UDP_EIM) && + 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 +1019,20 @@ return (lnk); } +static struct alias_link * +FindLinkByInternalEndpoint(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_internal_endpoint, &la->linkSplayInternalEndpoint, &needle); +} + /* External routines for finding/adding links -- "external" means outside alias_db.c, but within alias*.c -- @@ -2110,6 +2163,7 @@ SPLAY_INIT(&la->linkSplayIn); SPLAY_INIT(&la->linkSplayOut); + SPLAY_INIT(&la->linkSplayInternalEndpoint); LIST_INIT(&la->pptpList); TAILQ_INIT(&la->checkExpire); #ifdef _KERNEL 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,12 @@ * 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 "internal endpoint" lookup tables. */ SPLAY_HEAD(splay_out, alias_link) linkSplayOut; SPLAY_HEAD(splay_in, group_in) linkSplayIn; + SPLAY_HEAD(splay_internal_endpoint, alias_link) + linkSplayInternalEndpoint; 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 @@ -270,6 +270,26 @@ in .Xr ipfw 8 for more details. +.It Dv PKT_ALIAS_UDP_EIM +When this bit is set, UDP uses endpoint-independent mapping (EIM), as per +RFC 4787 ("full cone" NAT of RFC 3489). +All packets from the same internal address:port are mapped to the same NAT +address:port, regardless of their destination address:port. +If filtering rules allow, and if +.Em PKT_ALIAS_DENY_INCOMING +is unset, any other external address:port can +also send to the internal address:port through its mapped NAT address:port. +This is more compatible with applications, and can reduce the need for port +forwarding, but less scalable as each NAT address:port can only be +concurrently used by at most one internal address:port. +.Pp +When this bit is unset, UDP packets use endpoint-dependent mapping (EDM) +("symmetric" NAT). +Each connection from a particular internal address:port to different +external addresses:ports is mapped to a random and unpredictable NAT +address:port. +Two appplications behind EDM NATs can only connect to each other +by port forwarding on the NAT, or tunnelling through an in-between server. .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 @@ -359,6 +359,172 @@ LibAliasUninit(la); } +ATF_TC_WITHOUT_HEAD(9_udp_eim_mapping); +ATF_TC_BODY(9_udp_eim_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_UDP_EIM, ~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_eim_out_in); +ATF_TC_BODY(10_udp_eim_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_UDP_EIM, ~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_eim_with_deny_incoming); +ATF_TC_BODY(11_udp_eim_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_UDP_EIM | 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_eim_hairpinning); +ATF_TC_BODY(12_udp_eim_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_UDP_EIM, ~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 +538,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_eim_mapping); + ATF_TP_ADD_TC(natout, 10_udp_eim_out_in); + ATF_TP_ADD_TC(natout, 11_udp_eim_with_deny_incoming); + ATF_TP_ADD_TC(natout, 12_udp_eim_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) }; diff --git a/usr.sbin/ppp/command.c b/usr.sbin/ppp/command.c --- a/usr.sbin/ppp/command.c +++ b/usr.sbin/ppp/command.c @@ -793,6 +793,10 @@ {"use_sockets", NULL, NatOption, LOCAL_AUTH, "allocate host sockets", "nat use_sockets yes|no", (const void *) PKT_ALIAS_USE_SOCKETS}, + {"udp_eim", NULL, NatOption, LOCAL_AUTH, + "UDP uses endpoint-independent mapping (\"full cone\" NAT)", + "nat udp_eim yes|no", + (const void *) PKT_ALIAS_UDP_EIM}, {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH, "Display this message", "nat help|? [command]", NatCommands}, {NULL, NULL, NULL, 0, NULL, NULL, NULL}, diff --git a/usr.sbin/ppp/ppp.8 b/usr.sbin/ppp/ppp.8 --- a/usr.sbin/ppp/ppp.8 +++ b/usr.sbin/ppp/ppp.8 @@ -3570,6 +3570,26 @@ The target address may be set to .Dq MYADDR , in which case libalias will redirect all packets to the interface address. +.It nat udp_eim yes|no +When enabled, UDP packets use endpoint-independent mapping (EIM) from RFC 4787 +("full cone" NAT of RFC 3489). +All packets from the same internal address:port are mapped to the same NAT +address:port, regardless of their destination address:port. +If filtering rules allow, and if +.Em deny_incoming +is disabled, any other external address:port can +also send to the internal address:port through its mapped NAT address:port. +This is more compatible with applications, and can reduce the need for port +forwarding, but less scalable as each NAT address:port can only be +concurrently used by at most one internal address:port. +.Pp +When disabled, UDP packets use endpoint-dependent mapping (EDM) ("symmetric" +NAT). +Each connection from a particular internal address:port to different +external addresses:ports is mapped to a random and unpredictable NAT +address:port. +Two appplications behind EDM NATs can only connect to each other +by port forwarding on the NAT, or tunnelling through an in-between server. .It nat use_sockets yes|no When enabled, this option tells the network address translation engine to create a socket so that it can guarantee a correct incoming ftp data or