Merge branches 'master' and 'suser_to_priv'
[dragonfly.git] / sys / netinet6 / in6_src.c
index 857edf7..409febb 100644 (file)
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/jail.h>
+#include <sys/kernel.h>
 #include <sys/malloc.h>
 #include <sys/mbuf.h>
 #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.
@@ -611,3 +625,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);
+}