X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/blobdiff_plain/89aa6ac1656ce8537ec94fb21c165fd09d053c42..8a27f1c965140ec72dd069582960c64ba9ecf534:/sys/netinet6/in6.c diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index ae76439acb..ef148f6fe7 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -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.24 2006/12/22 23:57:53 swildner 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 #include #include +#include #include #include #include @@ -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)) @@ -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,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; @@ -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;