Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 12 Nov 2018 11:20:59 +0000 (UTC)
From:      "Andrey V. Elsukov" <ae@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r340360 - in head: sbin/ipfw sys/netinet6 sys/netpfil/ipfw/nptv6
Message-ID:  <201811121120.wACBKxMt061432@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ae
Date: Mon Nov 12 11:20:59 2018
New Revision: 340360
URL: https://svnweb.freebsd.org/changeset/base/340360

Log:
  Add ability to use dynamic external prefix in ipfw_nptv6 module.
  
  Now an interface name can be specified for nptv6 instance instead of
  ext_prefix. The module will track if_addr_ext events and when suitable
  IPv6 address will be added to specified interface, it will be configured
  as external prefix. When address disappears instance becomes unusable,
  i.e. it doesn't match any packets.
  
  Reviewed by:	0mp (manpages)
  Tested by:	Dries Michiels <driesm dot michiels gmail com>
  MFC after:	1 month
  Differential Revision:	https://reviews.freebsd.org/D17765

Modified:
  head/sbin/ipfw/ipfw.8
  head/sbin/ipfw/ipfw2.h
  head/sbin/ipfw/nptv6.c
  head/sys/netinet6/ip_fw_nptv6.h
  head/sys/netpfil/ipfw/nptv6/nptv6.c
  head/sys/netpfil/ipfw/nptv6/nptv6.h

Modified: head/sbin/ipfw/ipfw.8
==============================================================================
--- head/sbin/ipfw/ipfw.8	Mon Nov 12 07:14:34 2018	(r340359)
+++ head/sbin/ipfw/ipfw.8	Mon Nov 12 11:20:59 2018	(r340360)
@@ -1,7 +1,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd October 21, 2018
+.Dd November 12, 2018
 .Dt IPFW 8
 .Os
 .Sh NAME
@@ -3495,6 +3495,15 @@ NPTv6 module translates source address when it matches
 .It Cm ext_prefix Ar ipv6_prefix
 IPv6 prefix used in external network.
 NPTv6 module translates destination address when it matches this prefix.
+.It Cm ext_if Ar nic
+The NPTv6 module will use first global IPv6 address from interface
+.Ar nic
+as external prefix.
+It can be useful when IPv6 prefix of external network is dynamically obtained.
+.Cm ext_prefix
+and
+.Cm ext_if
+options are mutually exclusive.
 .It Cm prefixlen Ar length
 The length of specified IPv6 prefixes. It must be in range from 8 to 64.
 .El

Modified: head/sbin/ipfw/ipfw2.h
==============================================================================
--- head/sbin/ipfw/ipfw2.h	Mon Nov 12 07:14:34 2018	(r340359)
+++ head/sbin/ipfw/ipfw2.h	Mon Nov 12 11:20:59 2018	(r340360)
@@ -294,6 +294,7 @@ enum tokens {
 	TOK_INTPREFIX,
 	TOK_EXTPREFIX,
 	TOK_PREFIXLEN,
+	TOK_EXTIF,
 
 	TOK_TCPSETMSS,
 

Modified: head/sbin/ipfw/nptv6.c
==============================================================================
--- head/sbin/ipfw/nptv6.c	Mon Nov 12 07:14:34 2018	(r340359)
+++ head/sbin/ipfw/nptv6.c	Mon Nov 12 11:20:59 2018	(r340360)
@@ -152,6 +152,7 @@ static struct _s_x nptv6newcmds[] = {
       { "int_prefix",	TOK_INTPREFIX },
       { "ext_prefix",	TOK_EXTPREFIX },
       { "prefixlen",	TOK_PREFIXLEN },
+      { "ext_if",	TOK_EXTIF },
       { NULL, 0 }
 };
 
@@ -214,6 +215,9 @@ nptv6_create(const char *name, uint8_t set, int ac, ch
 			ac--; av++;
 			break;
 		case TOK_EXTPREFIX:
+			if (flags & NPTV6_HAS_EXTPREFIX)
+				errx(EX_USAGE,
+				    "Only one ext_prefix or ext_if allowed");
 			NEED1("IPv6 prefix required");
 			nptv6_parse_prefix(*av, &cfg->external, &plen);
 			flags |= NPTV6_HAS_EXTPREFIX;
@@ -221,6 +225,18 @@ nptv6_create(const char *name, uint8_t set, int ac, ch
 				goto check_prefix;
 			ac--; av++;
 			break;
+		case TOK_EXTIF:
+			if (flags & NPTV6_HAS_EXTPREFIX)
+				errx(EX_USAGE,
+				    "Only one ext_prefix or ext_if allowed");
+			NEED1("Interface name required");
+			if (strlen(*av) >= sizeof(cfg->if_name))
+				errx(EX_USAGE, "Invalid interface name");
+			flags |= NPTV6_HAS_EXTPREFIX;
+			cfg->flags |= NPTV6_DYNAMIC_PREFIX;
+			strncpy(cfg->if_name, *av, sizeof(cfg->if_name));
+			ac--; av++;
+			break;
 		case TOK_PREFIXLEN:
 			NEED1("IPv6 prefix length required");
 			plen = strtol(*av, &p, 10);
@@ -245,13 +261,14 @@ check_prefix:
 	if ((flags & NPTV6_HAS_INTPREFIX) != NPTV6_HAS_INTPREFIX)
 		errx(EX_USAGE, "int_prefix required");
 	if ((flags & NPTV6_HAS_EXTPREFIX) != NPTV6_HAS_EXTPREFIX)
-		errx(EX_USAGE, "ext_prefix required");
+		errx(EX_USAGE, "ext_prefix or ext_if required");
 	if ((flags & NPTV6_HAS_PREFIXLEN) != NPTV6_HAS_PREFIXLEN)
 		errx(EX_USAGE, "prefixlen required");
 
 	n2mask(&mask, cfg->plen);
 	APPLY_MASK(&cfg->internal, &mask);
-	APPLY_MASK(&cfg->external, &mask);
+	if ((cfg->flags & NPTV6_DYNAMIC_PREFIX) == 0)
+		APPLY_MASK(&cfg->external, &mask);
 
 	olh->count = 1;
 	olh->objsize = sizeof(*cfg);
@@ -350,8 +367,13 @@ nptv6_show_cb(ipfw_nptv6_cfg *cfg, const char *name, u
 		printf("set %u ", cfg->set);
 	inet_ntop(AF_INET6, &cfg->internal, abuf, sizeof(abuf));
 	printf("nptv6 %s int_prefix %s ", cfg->name, abuf);
-	inet_ntop(AF_INET6, &cfg->external, abuf, sizeof(abuf));
-	printf("ext_prefix %s prefixlen %u\n", abuf, cfg->plen);
+	if (cfg->flags & NPTV6_DYNAMIC_PREFIX)
+		printf("ext_if %s ", cfg->if_name);
+	else {
+		inet_ntop(AF_INET6, &cfg->external, abuf, sizeof(abuf));
+		printf("ext_prefix %s ", abuf);
+	}
+	printf("prefixlen %u\n", cfg->plen);
 	return (0);
 }
 

Modified: head/sys/netinet6/ip_fw_nptv6.h
==============================================================================
--- head/sys/netinet6/ip_fw_nptv6.h	Mon Nov 12 07:14:34 2018	(r340359)
+++ head/sys/netinet6/ip_fw_nptv6.h	Mon Nov 12 11:20:59 2018	(r340360)
@@ -40,11 +40,15 @@ struct ipfw_nptv6_stats {
 typedef struct _ipfw_nptv6_cfg {
 	char		name[64];	/* NPTv6 instance name */
 	struct in6_addr	internal;	/* NPTv6 internal prefix */
-	struct in6_addr	external;	/* NPTv6 external prefix */
+	union {
+		struct in6_addr	external; /* NPTv6 external prefix */
+		char	if_name[IF_NAMESIZE];
+	};
 	uint8_t		plen;		/* Prefix length */
 	uint8_t		set;		/* Named instance set [0..31] */
 	uint8_t		spare[2];
 	uint32_t	flags;
+#define	NPTV6_DYNAMIC_PREFIX	1	/* Use dynamic external prefix */
 } ipfw_nptv6_cfg;
 
 #endif /* _NETINET6_IP_FW_NPTV6_H_ */

Modified: head/sys/netpfil/ipfw/nptv6/nptv6.c
==============================================================================
--- head/sys/netpfil/ipfw/nptv6/nptv6.c	Mon Nov 12 07:14:34 2018	(r340359)
+++ head/sys/netpfil/ipfw/nptv6/nptv6.c	Mon Nov 12 11:20:59 2018	(r340360)
@@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/counter.h>
+#include <sys/eventhandler.h>
 #include <sys/errno.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
@@ -65,6 +66,8 @@ VNET_DEFINE_STATIC(uint16_t, nptv6_eid) = 0;
 #define	V_nptv6_eid	VNET(nptv6_eid)
 #define	IPFW_TLV_NPTV6_NAME	IPFW_TLV_EACTION_NAME(V_nptv6_eid)
 
+static eventhandler_tag nptv6_ifaddr_event;
+
 static struct nptv6_cfg *nptv6_alloc_config(const char *name, uint8_t set);
 static void nptv6_free_config(struct nptv6_cfg *cfg);
 static struct nptv6_cfg *nptv6_find(struct namedobj_instance *ni,
@@ -357,7 +360,8 @@ ipfw_nptv6(struct ip_fw_chain *chain, struct ip_fw_arg
 	if (cmd->opcode != O_EXTERNAL_ACTION ||
 	    cmd->arg1 != V_nptv6_eid ||
 	    icmd->opcode != O_EXTERNAL_INSTANCE ||
-	    (cfg = NPTV6_LOOKUP(chain, icmd)) == NULL)
+	    (cfg = NPTV6_LOOKUP(chain, icmd)) == NULL ||
+	    (cfg->flags & NPTV6_READY) == 0)
 		return (ret);
 	/*
 	 * We need act as router, so when forwarding is disabled -
@@ -442,7 +446,10 @@ nptv6_export_config(struct ip_fw_chain *ch, struct npt
 {
 
 	uc->internal = cfg->internal;
-	uc->external = cfg->external;
+	if (cfg->flags & NPTV6_DYNAMIC_PREFIX)
+		memcpy(uc->if_name, cfg->if_name, IF_NAMESIZE);
+	else
+		uc->external = cfg->external;
 	uc->plen = cfg->plen;
 	uc->flags = cfg->flags & NPTV6_FLAGSMASK;
 	uc->set = cfg->no.set;
@@ -497,7 +504,141 @@ nptv6_calculate_adjustment(struct nptv6_cfg *cfg)
 	cfg->adjustment = cksum_add(~e, i);
 }
 
+static int
+nptv6_check_prefix(const struct in6_addr *addr)
+{
+
+	if (IN6_IS_ADDR_MULTICAST(addr) ||
+	    IN6_IS_ADDR_LINKLOCAL(addr) ||
+	    IN6_IS_ADDR_LOOPBACK(addr) ||
+	    IN6_IS_ADDR_UNSPECIFIED(addr))
+		return (EINVAL);
+	return (0);
+}
+
+static void
+nptv6_set_external(struct nptv6_cfg *cfg, struct in6_addr *addr)
+{
+
+	cfg->external = *addr;
+	IN6_MASK_ADDR(&cfg->external, &cfg->mask);
+	nptv6_calculate_adjustment(cfg);
+	cfg->flags |= NPTV6_READY;
+}
+
 /*
+ * Try to determine what prefix to use as external for
+ * configured interface name.
+ */
+static void
+nptv6_find_prefix(struct ip_fw_chain *ch, struct nptv6_cfg *cfg,
+    struct ifnet *ifp)
+{
+	struct ifaddr *ifa;
+	struct in6_ifaddr *ia;
+
+	MPASS(cfg->flags & NPTV6_DYNAMIC_PREFIX);
+	IPFW_UH_WLOCK_ASSERT(ch);
+
+	if (ifp == NULL) {
+		ifp = ifunit_ref(cfg->if_name);
+		if (ifp == NULL)
+			return;
+	}
+	if_addr_rlock(ifp);
+	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
+		if (ifa->ifa_addr->sa_family != AF_INET6)
+			continue;
+		ia = (struct in6_ifaddr *)ifa;
+		if (nptv6_check_prefix(&ia->ia_addr.sin6_addr) ||
+		    IN6_ARE_MASKED_ADDR_EQUAL(&ia->ia_addr.sin6_addr,
+		    &cfg->internal, &cfg->mask))
+			continue;
+		/* Suitable address is found. */
+		nptv6_set_external(cfg, &ia->ia_addr.sin6_addr);
+		break;
+	}
+	if_addr_runlock(ifp);
+	if_rele(ifp);
+}
+
+struct ifaddr_event_args {
+	struct ifnet *ifp;
+	const struct in6_addr *addr;
+	int event;
+};
+
+static int
+ifaddr_cb(struct namedobj_instance *ni, struct named_object *no,
+    void *arg)
+{
+	struct ifaddr_event_args *args;
+	struct ip_fw_chain *ch;
+	struct nptv6_cfg *cfg;
+
+	ch = &V_layer3_chain;
+	cfg = (struct nptv6_cfg *)SRV_OBJECT(ch, no->kidx);
+	if ((cfg->flags & NPTV6_DYNAMIC_PREFIX) == 0)
+		return (0);
+
+	args = arg;
+	/* If interface name doesn't match, ignore */
+	if (strncmp(args->ifp->if_xname, cfg->if_name, IF_NAMESIZE))
+		return (0);
+	if (args->ifp->if_flags & IFF_DYING) { /* XXX: is it possible? */
+		cfg->flags &= ~NPTV6_READY;
+		return (0);
+	}
+	if (args->event == IFADDR_EVENT_DEL) {
+		/* If instance is not ready, ignore */
+		if ((cfg->flags & NPTV6_READY) == 0)
+			return (0);
+		/* If address does not match the external prefix, ignore */
+		if (IN6_ARE_MASKED_ADDR_EQUAL(&cfg->external, args->addr,
+		    &cfg->mask) != 0)
+			return (0);
+		/* Otherwise clear READY flag */
+		cfg->flags &= ~NPTV6_READY;
+	} else {/* IFADDR_EVENT_ADD */
+		/* If instance is already ready, ignore */
+		if (cfg->flags & NPTV6_READY)
+			return (0);
+		/* If address is not suitable for prefix, ignore */
+		if (nptv6_check_prefix(args->addr) ||
+		    IN6_ARE_MASKED_ADDR_EQUAL(args->addr, &cfg->internal,
+		    &cfg->mask))
+			return (0);
+		/* FALLTHROUGH */
+	}
+	MPASS(!(cfg->flags & NPTV6_READY));
+	/* Try to determine the prefix */
+	if_ref(args->ifp);
+	nptv6_find_prefix(ch, cfg, args->ifp);
+	return (0);
+}
+
+static void
+nptv6_ifaddrevent_handler(void *arg __unused, struct ifnet *ifp,
+    struct ifaddr *ifa, int event)
+{
+	struct ifaddr_event_args args;
+	struct ip_fw_chain *ch;
+
+	if (ifa->ifa_addr->sa_family != AF_INET6)
+		return;
+
+	args.ifp = ifp;
+	args.addr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
+	args.event = event;
+
+	ch = &V_layer3_chain;
+	IPFW_UH_WLOCK(ch);
+	ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), ifaddr_cb, &args,
+	    IPFW_TLV_NPTV6_NAME);
+	IPFW_UH_WUNLOCK(ch);
+}
+
+/*
  * Creates new NPTv6 instance.
  * Data layout (v0)(current):
  * Request: [ ipfw_obj_lheader ipfw_nptv6_cfg ]
@@ -523,15 +664,12 @@ nptv6_create(struct ip_fw_chain *ch, ip_fw3_opheader *
 		return (EINVAL);
 	if (uc->plen < 8 || uc->plen > 64 || uc->set >= IPFW_MAX_SETS)
 		return (EINVAL);
-	if (IN6_IS_ADDR_MULTICAST(&uc->internal) ||
-	    IN6_IS_ADDR_MULTICAST(&uc->external) ||
-	    IN6_IS_ADDR_UNSPECIFIED(&uc->internal) ||
-	    IN6_IS_ADDR_UNSPECIFIED(&uc->external) ||
-	    IN6_IS_ADDR_LINKLOCAL(&uc->internal) ||
-	    IN6_IS_ADDR_LINKLOCAL(&uc->external))
+	if (nptv6_check_prefix(&uc->internal))
 		return (EINVAL);
 	in6_prefixlen2mask(&mask, uc->plen);
-	if (IN6_ARE_MASKED_ADDR_EQUAL(&uc->internal, &uc->external, &mask))
+	if ((uc->flags & NPTV6_DYNAMIC_PREFIX) == 0 && (
+	    nptv6_check_prefix(&uc->external) ||
+	    IN6_ARE_MASKED_ADDR_EQUAL(&uc->external, &uc->internal, &mask)))
 		return (EINVAL);
 
 	ni = CHAIN_TO_SRV(ch);
@@ -544,15 +682,23 @@ nptv6_create(struct ip_fw_chain *ch, ip_fw3_opheader *
 
 	cfg = nptv6_alloc_config(uc->name, uc->set);
 	cfg->plen = uc->plen;
+	cfg->flags = uc->flags & NPTV6_FLAGSMASK;
 	if (cfg->plen <= 48)
 		cfg->flags |= NPTV6_48PLEN;
-	cfg->internal = uc->internal;
-	cfg->external = uc->external;
 	cfg->mask = mask;
+	cfg->internal = uc->internal;
 	IN6_MASK_ADDR(&cfg->internal, &mask);
-	IN6_MASK_ADDR(&cfg->external, &mask);
-	nptv6_calculate_adjustment(cfg);
+	if (cfg->flags & NPTV6_DYNAMIC_PREFIX)
+		memcpy(cfg->if_name, uc->if_name, IF_NAMESIZE);
+	else
+		nptv6_set_external(cfg, &uc->external);
 
+	if ((uc->flags & NPTV6_DYNAMIC_PREFIX) != 0 &&
+	    nptv6_ifaddr_event == NULL)
+		nptv6_ifaddr_event = EVENTHANDLER_REGISTER(
+		    ifaddr_event_ext, nptv6_ifaddrevent_handler, NULL,
+		    EVENTHANDLER_PRI_ANY);
+
 	IPFW_UH_WLOCK(ch);
 	if (ipfw_objhash_alloc_idx(ni, &cfg->no.kidx) != 0) {
 		IPFW_UH_WUNLOCK(ch);
@@ -561,7 +707,10 @@ nptv6_create(struct ip_fw_chain *ch, ip_fw3_opheader *
 	}
 	ipfw_objhash_add(ni, &cfg->no);
 	SRV_OBJECT(ch, cfg->no.kidx) = cfg;
+	if (cfg->flags & NPTV6_DYNAMIC_PREFIX)
+		nptv6_find_prefix(ch, cfg, NULL);
 	IPFW_UH_WUNLOCK(ch);
+
 	return (0);
 }
 
@@ -870,6 +1019,8 @@ void
 nptv6_uninit(struct ip_fw_chain *ch, int last)
 {
 
+	if (last && nptv6_ifaddr_event != NULL)
+		EVENTHANDLER_DEREGISTER(ifaddr_event_ext, nptv6_ifaddr_event);
 	IPFW_DEL_OBJ_REWRITER(last, opcodes);
 	IPFW_DEL_SOPT_HANDLER(last, scodes);
 	ipfw_del_eaction(ch, V_nptv6_eid);

Modified: head/sys/netpfil/ipfw/nptv6/nptv6.h
==============================================================================
--- head/sys/netpfil/ipfw/nptv6/nptv6.h	Mon Nov 12 07:14:34 2018	(r340359)
+++ head/sys/netpfil/ipfw/nptv6/nptv6.h	Mon Nov 12 11:20:59 2018	(r340360)
@@ -51,11 +51,14 @@ struct nptv6_cfg {
 	uint16_t		adjustment; /* Checksum adjustment value */
 	uint8_t			plen;	    /* Prefix length */
 	uint8_t			flags;	    /* Flags for internal use */
-#define	NPTV6_48PLEN		0x0001
+#define	NPTV6_READY		0x80
+#define	NPTV6_48PLEN		0x40
+
+	char			if_name[IF_NAMESIZE];
 	char			name[64];   /* Instance name */
 	counter_u64_t		stats[NPTV6STATS]; /* Statistics counters */
 };
-#define	NPTV6_FLAGSMASK		0
+#define	NPTV6_FLAGSMASK		(NPTV6_DYNAMIC_PREFIX)
 
 int nptv6_init(struct ip_fw_chain *ch, int first);
 void nptv6_uninit(struct ip_fw_chain *ch, int last);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201811121120.wACBKxMt061432>