Page MenuHomeFreeBSD

D17765.id50312.diff
No OneTemporary

D17765.id50312.diff

Index: head/sbin/ipfw/ipfw.8
===================================================================
--- head/sbin/ipfw/ipfw.8
+++ head/sbin/ipfw/ipfw.8
@@ -1,7 +1,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd October 21, 2018
+.Dd November 12, 2018
.Dt IPFW 8
.Os
.Sh NAME
@@ -3495,6 +3495,15 @@
.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
Index: head/sbin/ipfw/ipfw2.h
===================================================================
--- head/sbin/ipfw/ipfw2.h
+++ head/sbin/ipfw/ipfw2.h
@@ -294,6 +294,7 @@
TOK_INTPREFIX,
TOK_EXTPREFIX,
TOK_PREFIXLEN,
+ TOK_EXTIF,
TOK_TCPSETMSS,
Index: head/sbin/ipfw/nptv6.c
===================================================================
--- head/sbin/ipfw/nptv6.c
+++ head/sbin/ipfw/nptv6.c
@@ -152,6 +152,7 @@
{ "int_prefix", TOK_INTPREFIX },
{ "ext_prefix", TOK_EXTPREFIX },
{ "prefixlen", TOK_PREFIXLEN },
+ { "ext_if", TOK_EXTIF },
{ NULL, 0 }
};
@@ -214,6 +215,9 @@
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 @@
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 @@
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 @@
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);
}
Index: head/sys/netinet6/ip_fw_nptv6.h
===================================================================
--- head/sys/netinet6/ip_fw_nptv6.h
+++ head/sys/netinet6/ip_fw_nptv6.h
@@ -40,11 +40,15 @@
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_ */
Index: head/sys/netpfil/ipfw/nptv6/nptv6.h
===================================================================
--- head/sys/netpfil/ipfw/nptv6/nptv6.h
+++ head/sys/netpfil/ipfw/nptv6/nptv6.h
@@ -51,11 +51,14 @@
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);
Index: head/sys/netpfil/ipfw/nptv6/nptv6.c
===================================================================
--- head/sys/netpfil/ipfw/nptv6/nptv6.c
+++ head/sys/netpfil/ipfw/nptv6/nptv6.c
@@ -31,6 +31,7 @@
#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 @@
#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 @@
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 @@
{
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 @@
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 @@
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 @@
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 @@
}
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 @@
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);

File Metadata

Mime Type
text/plain
Expires
Tue, Mar 24, 9:14 PM (21 h, 14 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
30272118
Default Alt Text
D17765.id50312.diff (11 KB)

Event Timeline