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);
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to