Merge branches 'master' and 'suser_to_priv'
[dragonfly.git] / sys / netinet6 / in6.c
index 4f5508e..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.23 2006/10/24 06:18:42 hsu 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>
@@ -231,7 +232,7 @@ done:
  * rely on the cloning mechanism from the corresponding interface route
  * any more.
  */
-static void
+void
 in6_ifaddloop(struct ifaddr *ifa)
 {
        struct rtentry *rt;
@@ -249,7 +250,7 @@ in6_ifaddloop(struct ifaddr *ifa)
  * Remove loopback rtentry of ownaddr generated by in6_ifaddloop(),
  * if it exists.
  */
-static void
+void
 in6_ifremloop(struct ifaddr *ifa)
 {
        struct in6_ifaddr *ia;
@@ -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))
@@ -1229,7 +1246,7 @@ in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp)
                        ia->ia_next = oia->ia_next;
                else {
                        /* search failed */
-                       printf("Couldn't unlink in6_ifaddr from in6_ifaddr\n");
+                       kprintf("Couldn't unlink in6_ifaddr from in6_ifaddr\n");
                }
        }
 
@@ -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 &&
@@ -1614,6 +1631,35 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia, struct sockaddr_in6 *sin6,
        return (error);
 }
 
+struct in6_multi_mship *
+in6_joingroup(struct ifnet *ifp, struct in6_addr *addr, int *errorp)
+{
+       struct in6_multi_mship *imm;
+
+       imm = kmalloc(sizeof(*imm), M_IPMADDR, M_NOWAIT);
+       if (!imm) {
+               *errorp = ENOBUFS;
+               return NULL;
+       }
+       imm->i6mm_maddr = in6_addmulti(addr, ifp, errorp);
+       if (!imm->i6mm_maddr) {
+               /* *errorp is alrady set */
+               kfree(imm, M_IPMADDR);
+               return NULL;
+       }
+       return imm;
+}
+
+int
+in6_leavegroup(struct in6_multi_mship *imm)
+{
+
+       if (imm->i6mm_maddr)
+               in6_delmulti(imm->i6mm_maddr);
+       kfree(imm,  M_IPMADDR);
+       return 0;
+}
+
 /*
  * Add an address to the list of IP6 multicast addresses for a
  * given interface.
@@ -1708,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)
@@ -1723,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);
 }
 
 
@@ -1734,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)
@@ -1745,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);
+}
+
+/*
+ * 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 ((struct in6_ifaddr *)ifa);
+       return NULL;
 }
 
 /*
@@ -1913,13 +1993,12 @@ 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;
 
        if (oifp == NULL) {
 #if 0
-               printf("in6_ifawithscope: output interface is not specified\n");
+               kprintf("in6_ifawithscope: output interface is not specified\n");
 #endif
                return (NULL);
        }
@@ -1931,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.
@@ -1938,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;
@@ -2189,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 */
 
@@ -2201,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)
@@ -2236,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)
@@ -2270,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 */
 
@@ -2280,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;