git: 61bf830cbb26 - main - libalias: Add support for EIM NAT

From: Tom Jones <thj_at_FreeBSD.org>
Date: Thu, 05 Dec 2024 16:26:37 UTC
The branch main has been updated by thj:

URL: https://cgit.FreeBSD.org/src/commit/?id=61bf830cbb260c2a046cb44421d319184393e028

commit 61bf830cbb260c2a046cb44421d319184393e028
Author:     Damjan Jovanovic <damjan.jov@gmail.com>
AuthorDate: 2024-12-05 16:19:13 +0000
Commit:     Tom Jones <thj@FreeBSD.org>
CommitDate: 2024-12-05 16:19:13 +0000

    libalias: Add support for EIM NAT
    
    Add support for endpoint-independent mapping ("full cone NAT") in
    Libalias's UDP NAT.
    
    This conforms to RFC 4787 requirements 1 and 3. All UDP packets sent out from a
    particular internal address:port leave via the same NAT address:port,
    regardless of their destination.
    
    Add some libalias tests and supporting defines.
    
    Reviewed by:    igoro, thj
    Differential Revision:  https://reviews.freebsd.org/D46689D
---
 sys/netinet/libalias/alias.h          |  20 ++++
 sys/netinet/libalias/alias_db.c       |  58 +++++++++++-
 sys/netinet/libalias/alias_db.h       |  43 ++++++---
 sys/netinet/libalias/alias_local.h    |   6 +-
 sys/netinet/libalias/libalias.3       |  22 ++++-
 tests/sys/netinet/libalias/2_natout.c | 170 ++++++++++++++++++++++++++++++++++
 tests/sys/netinet/libalias/util.c     |   1 +
 tests/sys/netinet/libalias/util.h     |   2 +-
 8 files changed, 304 insertions(+), 18 deletions(-)

diff --git a/sys/netinet/libalias/alias.h b/sys/netinet/libalias/alias.h
index 706184552429..96d8ceec28be 100644
--- a/sys/netinet/libalias/alias.h
+++ b/sys/netinet/libalias/alias.h
@@ -227,6 +227,26 @@ struct mbuf    *m_megapullup(struct mbuf *, int);
  */
 #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.c b/sys/netinet/libalias/alias_db.c
index 4bb95549aaaf..b09e41935d93 100644
--- a/sys/netinet/libalias/alias_db.c
+++ b/sys/netinet/libalias/alias_db.c
@@ -93,6 +93,8 @@ DECLARE_MODULE(alias, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
 
 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 @@ GetNewPort(struct libalias *la, struct alias_link *lnk, int alias_port_param)
 
 	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 @@ GetNewPort(struct libalias *la, struct alias_link *lnk, int alias_port_param)
 		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 @@ DeleteLink(struct alias_link **plnk, int deletePermanent)
 		/* 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 @@ AddLink(struct libalias *la, struct in_addr src_addr, struct in_addr dst_addr,
 			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 @@ FindLinkIn(struct libalias *la, struct in_addr dst_addr,
 	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 @@ FindLinkIn(struct libalias *la, struct in_addr dst_addr,
 	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 @@ LibAliasInit(struct libalias *la)
 
 		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_db.h b/sys/netinet/libalias/alias_db.h
index 35858099bce2..7175d0a50f4b 100644
--- a/sys/netinet/libalias/alias_db.h
+++ b/sys/netinet/libalias/alias_db.h
@@ -208,12 +208,14 @@ static struct in_addr const ANY_ADDR = { INADDR_ANY };
     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 alias_link {
 		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 @@ cmp_in(struct group_in *a, struct group_in *b) {
 }
 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 @@ FindLinkOut(struct libalias *, struct in_addr, struct in_addr, u_short, u_short,
 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_local.h b/sys/netinet/libalias/alias_local.h
index 7b82621a105b..ef6c89e675d6 100644
--- a/sys/netinet/libalias/alias_local.h
+++ b/sys/netinet/libalias/alias_local.h
@@ -94,10 +94,12 @@ struct libalias {
 	 * 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
index b4d123682f0b..c19acffe03ae 100644
--- a/sys/netinet/libalias/libalias.3
+++ b/sys/netinet/libalias/libalias.3
@@ -23,7 +23,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd May 31, 2021
+.Dd November 29, 2024
 .Dt LIBALIAS 3
 .Os
 .Sh NAME
@@ -270,6 +270,26 @@ See section
 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
index c6f5797b2db7..24ca06d11bf4 100644
--- a/tests/sys/netinet/libalias/2_natout.c
+++ b/tests/sys/netinet/libalias/2_natout.c
@@ -359,6 +359,172 @@ ATF_TC_BODY(8_portrange, dummy)
 	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_TCS(natout)
 	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.c b/tests/sys/netinet/libalias/util.c
index 14ba196a59a5..8ceb8355c8ff 100644
--- 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/tests/sys/netinet/libalias/util.h b/tests/sys/netinet/libalias/util.h
index 786e48e41f37..f58a1ad26248 100644
--- 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);