Merge branches 'master' and 'suser_to_priv'
[dragonfly.git] / sys / netinet6 / in6.c
index 5dde8dc..ef148f6 100644 (file)
@@ -1,5 +1,5 @@
 /*     $FreeBSD: src/sys/netinet6/in6.c,v 1.7.2.9 2002/04/28 05:40:26 suz Exp $        */
-/*     $DragonFly: src/sys/netinet6/in6.c,v 1.25 2007/08/16 20:03:58 dillon Exp $      */
+/*     $DragonFly: src/sys/netinet6/in6.c,v 1.30 2008/10/03 07:59:20 hasso Exp $       */
 /*     $KAME: in6.c,v 1.259 2002/01/21 11:37:50 keiichi Exp $  */
 
 /*
@@ -77,6 +77,7 @@
 #include <sys/sockio.h>
 #include <sys/systm.h>
 #include <sys/proc.h>
+#include <sys/priv.h>
 #include <sys/time.h>
 #include <sys/kernel.h>
 #include <sys/syslog.h>
@@ -302,15 +303,17 @@ int
 in6_ifindex2scopeid(int idx)
 {
        struct ifnet *ifp;
-       struct ifaddr *ifa;
        struct sockaddr_in6 *sin6;
+       struct ifaddr_container *ifac;
 
        if (idx < 0 || if_index < idx)
                return -1;
        ifp = ifindex2ifnet[idx];
 
-       TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
+       TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link)
        {
+               struct ifaddr *ifa = ifac->ifa;
+
                if (ifa->ifa_addr->sa_family != AF_INET6)
                        continue;
                sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
@@ -383,7 +386,7 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
        int error;
 
        privileged = 0;
-       if (suser(td) == 0)
+       if (priv_check(td, PRIV_ROOT) == 0)
                privileged++;
 
        switch (cmd) {
@@ -392,6 +395,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);
 
@@ -609,9 +620,14 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
 
        case SIOCAIFADDR_IN6:
        {
-               int i, error = 0;
+               int i, error = 0, iaIsNew;
                struct nd_prefix pr0, *pr;
 
+               if (ia != NULL)
+                       iaIsNew = 0;
+               else
+                       iaIsNew = 1;
+
                /*
                 * first, make or update the interface address structure,
                 * and link it to the list.
@@ -665,7 +681,7 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
                        if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0)
                                return (error);
                        if (pr == NULL) {
-                               log(LOG_ERR, "nd6_prelist_add succedded but "
+                               log(LOG_ERR, "nd6_prelist_add succeeded but "
                                    "no prefix\n");
                                return (EINVAL); /* XXX panic here? */
                        }
@@ -706,8 +722,11 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
                         */
                        pfxlist_onlink_check();
                }
-               if (error == 0 && ia)
-                       EVENTHANDLER_INVOKE(ifaddr_event, ifp);
+               if (error == 0 && ia) {
+                       EVENTHANDLER_INVOKE(ifaddr_event, ifp,
+                       iaIsNew ? IFADDR_EVENT_ADD : IFADDR_EVENT_CHANGE,
+                       &ia->ia_ifa);
+               }
                break;
        }
 
@@ -753,9 +772,10 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
                        pr->ndpr_expire = 1; /* XXX: just for expiration */
                }
 
-         purgeaddr:
+purgeaddr:
+               EVENTHANDLER_INVOKE(ifaddr_event, ifp, IFADDR_EVENT_DELETE,
+                                   &ia->ia_ifa);
                in6_purgeaddr(&ia->ia_ifa);
-               EVENTHANDLER_INVOKE(ifaddr_event, ifp);
                break;
        }
 
@@ -903,11 +923,9 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
                 * RA, it is called under splnet().  So, we should call malloc
                 * with M_NOWAIT.
                 */
-               ia = (struct in6_ifaddr *)
-                       kmalloc(sizeof(*ia), M_IFADDR, M_NOWAIT);
+               ia = ifa_create(sizeof(*ia), M_NOWAIT);
                if (ia == NULL)
                        return (ENOBUFS);
-               bzero((caddr_t)ia, sizeof(*ia));
                /* Initialize the address and masks */
                ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
                ia->ia_addr.sin6_family = AF_INET6;
@@ -933,8 +951,7 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
                } else
                        in6_ifaddr = ia;
 
-               TAILQ_INSERT_TAIL(&ifp->if_addrlist, &ia->ia_ifa,
-                                 ifa_list);
+               ifa_iflink(&ia->ia_ifa, ifp, 1);
        }
 
        /* set prefix mask */
@@ -1217,7 +1234,7 @@ in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp)
 
        crit_enter();
 
-       TAILQ_REMOVE(&ifp->if_addrlist, &ia->ia_ifa, ifa_list);
+       ifa_ifunlink(&ia->ia_ifa, ifp);
 
        oia = ia;
        if (oia == (ia = in6_ifaddr))
@@ -1262,7 +1279,7 @@ in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp)
         * release another refcnt for the link from in6_ifaddr.
         * Note that we should decrement the refcnt at least once for all *BSD.
         */
-       IFAFREE(&oia->ia_ifa);
+       ifa_destroy(&oia->ia_ifa);
 
        crit_exit();
 }
@@ -1270,14 +1287,13 @@ in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp)
 void
 in6_purgeif(struct ifnet *ifp)
 {
-       struct ifaddr *ifa, *nifa;
+       struct ifaddr_container *ifac, *next;
 
-       for (ifa = TAILQ_FIRST(&ifp->if_addrlist); ifa != NULL; ifa = nifa)
-       {
-               nifa = TAILQ_NEXT(ifa, ifa_list);
-               if (ifa->ifa_addr->sa_family != AF_INET6)
+       TAILQ_FOREACH_MUTABLE(ifac, &ifp->if_addrheads[mycpuid],
+                             ifa_link, next) {
+               if (ifac->ifa->ifa_addr->sa_family != AF_INET6)
                        continue;
-               in6_purgeaddr(ifa);
+               in6_purgeaddr(ifac->ifa);
        }
 
        in6_ifdetach(ifp);
@@ -1311,7 +1327,6 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data,
                  struct ifnet *ifp, struct thread *td)
 {
        struct if_laddrreq *iflr = (struct if_laddrreq *)data;
-       struct ifaddr *ifa;
        struct sockaddr *sa;
 
        /* sanity checks */
@@ -1360,6 +1375,7 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data,
                int prefixlen;
 
                if (iflr->flags & IFLR_PREFIX) {
+                       struct ifaddr *ifa;
                        struct sockaddr_in6 *sin6;
 
                        /*
@@ -1421,6 +1437,7 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data,
        case SIOCGLIFADDR:
        case SIOCDLIFADDR:
            {
+               struct ifaddr_container *ifac;
                struct in6_ifaddr *ia;
                struct in6_addr mask, candidate, match;
                struct sockaddr_in6 *sin6;
@@ -1457,8 +1474,9 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data,
                        }
                }
 
-               TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
-               {
+               TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link) {
+                       struct ifaddr *ifa = ifac->ifa;
+
                        if (ifa->ifa_addr->sa_family != AF_INET6)
                                continue;
                        if (!cmp)
@@ -1479,9 +1497,9 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data,
                        if (IN6_ARE_ADDR_EQUAL(&candidate, &match))
                                break;
                }
-               if (!ifa)
+               if (ifac == NULL)
                        return EADDRNOTAVAIL;
-               ia = ifa2ia6(ifa);
+               ia = ifa2ia6(ifac->ifa);
 
                if (cmd == SIOCGLIFADDR) {
                        struct sockaddr_in6 *s6;
@@ -1552,24 +1570,23 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia, struct sockaddr_in6 *sin6,
           int newhost)
 {
        int     error = 0, plen, ifacount = 0;
-       struct ifaddr *ifa;
-
-       lwkt_serialize_enter(ifp->if_serializer);
+       struct ifaddr_container *ifac;
 
        /*
         * Give the interface a chance to initialize
         * if this is its first address,
         * and to validate the address if necessary.
         */
-       TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
-       {
-               if (ifa->ifa_addr == NULL)
+       TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link) {
+               if (ifac->ifa->ifa_addr == NULL)
                        continue;       /* just for safety */
-               if (ifa->ifa_addr->sa_family != AF_INET6)
+               if (ifac->ifa->ifa_addr->sa_family != AF_INET6)
                        continue;
                ifacount++;
        }
 
+       lwkt_serialize_enter(ifp->if_serializer);
+
        ia->ia_addr = *sin6;
 
        if (ifacount <= 1 && ifp->if_ioctl &&
@@ -1615,10 +1632,7 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia, struct sockaddr_in6 *sin6,
 }
 
 struct in6_multi_mship *
-in6_joingroup(ifp, addr, errorp)
-       struct ifnet *ifp;
-       struct in6_addr *addr;
-       int *errorp;
+in6_joingroup(struct ifnet *ifp, struct in6_addr *addr, int *errorp)
 {
        struct in6_multi_mship *imm;
 
@@ -1637,8 +1651,7 @@ in6_joingroup(ifp, addr, errorp)
 }
 
 int
-in6_leavegroup(imm)
-       struct in6_multi_mship *imm;
+in6_leavegroup(struct in6_multi_mship *imm)
 {
 
        if (imm->i6mm_maddr)
@@ -1741,10 +1754,11 @@ in6_delmulti(struct in6_multi *in6m)
 struct in6_ifaddr *
 in6ifa_ifpforlinklocal(struct ifnet *ifp, int ignoreflags)
 {
-       struct ifaddr *ifa;
+       struct ifaddr_container *ifac;
+
+       TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link) {
+               struct ifaddr *ifa = ifac->ifa;
 
-       TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
-       {
                if (ifa->ifa_addr == NULL)
                        continue;       /* just for safety */
                if (ifa->ifa_addr->sa_family != AF_INET6)
@@ -1756,8 +1770,10 @@ in6ifa_ifpforlinklocal(struct ifnet *ifp, int ignoreflags)
                        break;
                }
        }
-
-       return ((struct in6_ifaddr *)ifa);
+       if (ifac != NULL)
+               return ((struct in6_ifaddr *)(ifac->ifa));
+       else
+               return (NULL);
 }
 
 
@@ -1767,10 +1783,11 @@ in6ifa_ifpforlinklocal(struct ifnet *ifp, int ignoreflags)
 struct in6_ifaddr *
 in6ifa_ifpwithaddr(struct ifnet *ifp, struct in6_addr *addr)
 {
-       struct ifaddr *ifa;
+       struct ifaddr_container *ifac;
+
+       TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link) {
+               struct ifaddr *ifa = ifac->ifa;
 
-       TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
-       {
                if (ifa->ifa_addr == NULL)
                        continue;       /* just for safety */
                if (ifa->ifa_addr->sa_family != AF_INET6)
@@ -1778,8 +1795,38 @@ in6ifa_ifpwithaddr(struct ifnet *ifp, struct in6_addr *addr)
                if (IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(ifa)))
                        break;
        }
+       if (ifac != NULL)
+               return ((struct in6_ifaddr *)(ifac->ifa));
+       else
+               return (NULL);
+}
 
-       return ((struct in6_ifaddr *)ifa);
+/*
+ * find the internet address on a given interface corresponding to a neighbor's
+ * address.
+ */
+struct in6_ifaddr *
+in6ifa_ifplocaladdr(const struct ifnet *ifp, const struct in6_addr *addr)
+{
+       struct ifaddr *ifa;
+       struct in6_ifaddr *ia;
+       struct ifaddr_container *ifac;
+
+       TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link) {
+               ifa = ifac->ifa;
+
+               if (ifa->ifa_addr == NULL)
+                       continue;       /* just for safety */
+               if (ifa->ifa_addr->sa_family != AF_INET6)
+                       continue;
+               ia = (struct in6_ifaddr *)ifa;
+               if (IN6_ARE_MASKED_ADDR_EQUAL(addr,
+                               &ia->ia_addr.sin6_addr,
+                               &ia->ia_prefixmask.sin6_addr))
+                       return ia;
+       }
+
+       return NULL;
 }
 
 /*
@@ -1946,7 +1993,6 @@ in6_ifawithscope(struct ifnet *oifp, struct in6_addr *dst)
 {
        int dst_scope = in6_addrscope(dst), src_scope, best_scope = 0;
        int blen = -1;
-       struct ifaddr *ifa;
        struct ifnet *ifp;
        struct in6_ifaddr *ifa_best = NULL;
 
@@ -1964,6 +2010,8 @@ in6_ifawithscope(struct ifnet *oifp, struct in6_addr *dst)
         */
        for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list))
        {
+               struct ifaddr_container *ifac;
+
                /*
                 * We can never take an address that breaks the scope zone
                 * of the destination.
@@ -1971,9 +2019,9 @@ in6_ifawithscope(struct ifnet *oifp, struct in6_addr *dst)
                if (in6_addr2scopeid(ifp, dst) != in6_addr2scopeid(oifp, dst))
                        continue;
 
-               TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
-               {
+               TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link) {
                        int tlen = -1, dscopecmp, bscopecmp, matchcmp;
+                       struct ifaddr *ifa = ifac->ifa;
 
                        if (ifa->ifa_addr->sa_family != AF_INET6)
                                continue;
@@ -2222,7 +2270,7 @@ struct in6_ifaddr *
 in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst)
 {
        int dst_scope = in6_addrscope(dst), blen = -1, tlen;
-       struct ifaddr *ifa;
+       struct ifaddr_container *ifac;
        struct in6_ifaddr *besta = 0;
        struct in6_ifaddr *dep[2];      /* last-resort: deprecated */
 
@@ -2234,8 +2282,9 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst)
         * If two or more, return one which matches the dst longest.
         * If none, return one of global addresses assigned other ifs.
         */
-       TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
-       {
+       TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link) {
+               struct ifaddr *ifa = ifac->ifa;
+
                if (ifa->ifa_addr->sa_family != AF_INET6)
                        continue;
                if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)
@@ -2269,8 +2318,9 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst)
        if (besta)
                return (besta);
 
-       TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
-       {
+       TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link) {
+               struct ifaddr *ifa = ifac->ifa;
+
                if (ifa->ifa_addr->sa_family != AF_INET6)
                        continue;
                if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)
@@ -2303,7 +2353,7 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst)
 void
 in6_if_up(struct ifnet *ifp)
 {
-       struct ifaddr *ifa;
+       struct ifaddr_container *ifac;
        struct in6_ifaddr *ia;
        int dad_delay;          /* delay ticks before DAD output */
 
@@ -2313,8 +2363,9 @@ in6_if_up(struct ifnet *ifp)
        in6_ifattach(ifp, NULL);
 
        dad_delay = 0;
-       TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
-       {
+       TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link) {
+               struct ifaddr *ifa = ifac->ifa;
+
                if (ifa->ifa_addr->sa_family != AF_INET6)
                        continue;
                ia = (struct in6_ifaddr *)ifa;