Add the management part of address selection policy described in RFC
authorPeter Avalos <pavalos@theshell.com>
Fri, 26 Dec 2008 03:37:08 +0000 (22:37 -0500)
committerPeter Avalos <pavalos@theshell.com>
Sun, 28 Dec 2008 04:17:06 +0000 (23:17 -0500)
3484.

Obtained-from: KAME via FreeBSD
Reviewed-by: sephe & hasso
sys/netinet6/in6.c
sys/netinet6/in6.h
sys/netinet6/in6_src.c
sys/netinet6/in6_var.h
sys/netinet6/ip6_input.c

index 4e249f9..e227c06 100644 (file)
@@ -394,6 +394,14 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
                return (mrt6_ioctl(cmd, data));
        }
 
+       switch(cmd) {
+       case SIOCAADDRCTL_POLICY:
+       case SIOCDADDRCTL_POLICY:
+               if (!privileged)
+                       return (EPERM);
+               return (in6_src_ioctl(cmd, data));
+       }
+
        if (ifp == NULL)
                return (EOPNOTSUPP);
 
index a741aee..eef8da0 100644 (file)
@@ -613,9 +613,11 @@ struct ip6_mtuinfo {
 #define IPV6CTL_AUTO_LINKLOCAL 35      /* automatic link-local addr assign */
 #define IPV6CTL_RIP6STATS      36      /* raw_ip6 stats */
 
+#define IPV6CTL_ADDRCTLPOLICY  38      /* get/set address selection policy */
+
 /* New entries should be added here from current IPV6CTL_MAXID value. */
 /* to define items, should talk with KAME guys first, for *BSD compatibility */
-#define IPV6CTL_MAXID          37
+#define IPV6CTL_MAXID          42
 
 #endif /* !_XOPEN_SOURCE */
 
@@ -646,6 +648,7 @@ void        in6_sin6_2_sin (struct sockaddr_in *sin, struct sockaddr_in6 *sin6);
 void   in6_sin_2_v4mapsin6(struct sockaddr_in *sin, struct sockaddr_in6 *sin6);
 void   in6_sin6_2_sin_in_sock (struct sockaddr *nam);
 void   in6_sin_2_v4mapsin6_in_sock (struct sockaddr **nam);
+void   addrsel_policy_init (void);
 
 #define        satosin6(sa)    ((struct sockaddr_in6 *)(sa))
 #define        sin6tosa(sin6)  ((struct sockaddr *)(sin6))
index a6e0904..eff45ba 100644 (file)
@@ -77,6 +77,8 @@
 #include <sys/protosw.h>
 #include <sys/socket.h>
 #include <sys/socketvar.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
 #include <sys/errno.h>
 #include <sys/time.h>
 #include <sys/proc.h>
 
 #include "use_loop.h"
 
+#define ADDR_LABEL_NOTAPP (-1)
+struct in6_addrpolicy defaultaddrpolicy;
+
+static void    init_policy_queue(void);
+static int     add_addrsel_policyent(struct in6_addrpolicy *);
+static int     delete_addrsel_policyent(struct in6_addrpolicy *);
+static int     walk_addrsel_policy(int (*)(struct in6_addrpolicy *, void *),
+                                   void *);
+static int     dump_addrsel_policyent(struct in6_addrpolicy *, void *);
+
+
 /*
  * Return an IPv6 address, which is the most appropriate for a given
  * destination and user specified options.
@@ -610,3 +623,169 @@ in6_clearscope(struct in6_addr *addr)
        if (IN6_IS_SCOPE_LINKLOCAL(addr))
                addr->s6_addr16[1] = 0;
 }
+
+void
+addrsel_policy_init(void)
+{
+
+       init_policy_queue();
+
+       /* initialize the "last resort" policy */
+       bzero(&defaultaddrpolicy, sizeof(defaultaddrpolicy));
+       defaultaddrpolicy.label = ADDR_LABEL_NOTAPP;
+}
+
+/*
+ * Subroutines to manage the address selection policy table via sysctl.
+ */
+struct walkarg {
+       struct sysctl_req *w_req;
+};
+
+static int in6_src_sysctl(SYSCTL_HANDLER_ARGS);
+SYSCTL_DECL(_net_inet6_ip6);
+SYSCTL_NODE(_net_inet6_ip6, IPV6CTL_ADDRCTLPOLICY, addrctlpolicy,
+       CTLFLAG_RD, in6_src_sysctl, "");
+
+static int
+in6_src_sysctl(SYSCTL_HANDLER_ARGS)
+{
+       struct walkarg w;
+
+       if (req->newptr)
+               return EPERM;
+
+       bzero(&w, sizeof(w));
+       w.w_req = req;
+
+       return (walk_addrsel_policy(dump_addrsel_policyent, &w));
+}
+
+int
+in6_src_ioctl(u_long cmd, caddr_t data)
+{
+       int i;
+       struct in6_addrpolicy ent0;
+
+       if (cmd != SIOCAADDRCTL_POLICY && cmd != SIOCDADDRCTL_POLICY)
+               return (EOPNOTSUPP); /* check for safety */
+
+       ent0 = *(struct in6_addrpolicy *)data;
+
+       if (ent0.label == ADDR_LABEL_NOTAPP)
+               return (EINVAL);
+       /* check if the prefix mask is consecutive. */
+       if (in6_mask2len(&ent0.addrmask.sin6_addr, NULL) < 0)
+               return (EINVAL);
+       /* clear trailing garbages (if any) of the prefix address. */
+       for (i = 0; i < 4; i++) {
+               ent0.addr.sin6_addr.s6_addr32[i] &=
+                       ent0.addrmask.sin6_addr.s6_addr32[i];
+       }
+       ent0.use = 0;
+
+       switch (cmd) {
+       case SIOCAADDRCTL_POLICY:
+               return (add_addrsel_policyent(&ent0));
+       case SIOCDADDRCTL_POLICY:
+               return (delete_addrsel_policyent(&ent0));
+       }
+
+       return (0);             /* XXX: compromise compilers */
+}
+
+/*
+ * The followings are implementation of the policy table using a
+ * simple tail queue.
+ * XXX such details should be hidden.
+ * XXX implementation using binary tree should be more efficient.
+ */
+struct addrsel_policyent {
+       TAILQ_ENTRY(addrsel_policyent) ape_entry;
+       struct in6_addrpolicy ape_policy;
+};
+
+TAILQ_HEAD(addrsel_policyhead, addrsel_policyent);
+
+struct addrsel_policyhead addrsel_policytab;
+
+static void
+init_policy_queue(void)
+{
+       TAILQ_INIT(&addrsel_policytab);
+}
+
+static int
+add_addrsel_policyent(struct in6_addrpolicy *newpolicy)
+{
+       struct addrsel_policyent *new, *pol;
+
+       /* duplication check */
+       for (pol = TAILQ_FIRST(&addrsel_policytab); pol;
+            pol = TAILQ_NEXT(pol, ape_entry)) {
+               if (SA6_ARE_ADDR_EQUAL(&newpolicy->addr,
+                                      &pol->ape_policy.addr) &&
+                   SA6_ARE_ADDR_EQUAL(&newpolicy->addrmask,
+                                      &pol->ape_policy.addrmask)) {
+                       return (EEXIST);        /* or override it? */
+               }
+       }
+
+       new = kmalloc(sizeof(*new), M_IFADDR, M_WAITOK | M_ZERO);
+
+       /* XXX: should validate entry */
+       new->ape_policy = *newpolicy;
+
+       TAILQ_INSERT_TAIL(&addrsel_policytab, new, ape_entry);
+
+       return (0);
+}
+
+static int
+delete_addrsel_policyent(struct in6_addrpolicy *key)
+{
+       struct addrsel_policyent *pol;
+
+       /* search for the entry in the table */
+       for (pol = TAILQ_FIRST(&addrsel_policytab); pol;
+            pol = TAILQ_NEXT(pol, ape_entry)) {
+               if (SA6_ARE_ADDR_EQUAL(&key->addr, &pol->ape_policy.addr) &&
+                   SA6_ARE_ADDR_EQUAL(&key->addrmask,
+                                      &pol->ape_policy.addrmask)) {
+                       break;
+               }
+       }
+       if (pol == NULL)
+               return (ESRCH);
+
+       TAILQ_REMOVE(&addrsel_policytab, pol, ape_entry);
+       kfree(pol, M_IFADDR);
+
+       return (0);
+}
+
+static int
+walk_addrsel_policy(int(*callback)(struct in6_addrpolicy *, void *), void *w)
+{
+       struct addrsel_policyent *pol;
+       int error = 0;
+
+       for (pol = TAILQ_FIRST(&addrsel_policytab); pol;
+            pol = TAILQ_NEXT(pol, ape_entry)) {
+               if ((error = (*callback)(&pol->ape_policy, w)) != 0)
+                       return (error);
+       }
+
+       return (error);
+}
+
+static int
+dump_addrsel_policyent(struct in6_addrpolicy *pol, void *arg)
+{
+       int error = 0;
+       struct walkarg *w = arg;
+
+       error = SYSCTL_OUT(w->w_req, pol, sizeof(*pol));
+
+       return (error);
+}
index 68ca18d..91342b3 100644 (file)
@@ -134,6 +134,15 @@ struct     in6_ifaddr {
                                     */
 };
 
+/* control structure to manage address selection policy */
+struct in6_addrpolicy {
+       struct sockaddr_in6 addr; /* prefix address */
+       struct sockaddr_in6 addrmask; /* prefix mask */
+       int preced;             /* precedence */
+       int label;              /* matching label */
+       u_quad_t use;           /* statistics */
+};
+
 /*
  * IPv6 interface statistics, as defined in RFC2465 Ipv6IfStatsEntry (p12).
  */
@@ -445,6 +454,9 @@ struct      in6_rrenumreq {
 #define SIOCGETMIFCNT_IN6      _IOWR('u', 107, \
                                      struct sioc_mif_req6) /* get pkt cnt per if */
 
+#define SIOCAADDRCTL_POLICY    _IOW('u', 108, struct in6_addrpolicy)
+#define SIOCDADDRCTL_POLICY    _IOW('u', 109, struct in6_addrpolicy)
+
 #define IN6_IFF_ANYCAST                0x01    /* anycast address */
 #define IN6_IFF_TENTATIVE      0x02    /* tentative address */
 #define IN6_IFF_DUPLICATED     0x04    /* DAD detected duplicate */
@@ -630,6 +642,7 @@ int in6_embedscope (struct in6_addr *, const struct sockaddr_in6 *,
 int in6_recoverscope (struct sockaddr_in6 *, const struct in6_addr *,
        struct ifnet *);
 void in6_clearscope (struct in6_addr *);
+int in6_src_ioctl (u_long, caddr_t);
 #endif /* _KERNEL */
 
 #endif /* _NETINET6_IN6_VAR_H_ */
index ab0b7fa..c253b80 100644 (file)
@@ -200,6 +200,7 @@ ip6_init(void)
        netisr_register(NETISR_IPV6, cpu0_portfn, ip6_input,
                        NETISR_FLAG_NOTMPSAFE);
        scope6_init();
+       addrsel_policy_init();
        nd6_init();
        frag6_init();
        /*