diff options
author | Bjoern A. Zeeb <bz@FreeBSD.org> | 2012-02-03 08:50:19 +0000 |
---|---|---|
committer | Bjoern A. Zeeb <bz@FreeBSD.org> | 2012-02-03 08:50:19 +0000 |
commit | 5490110cf5ab9ba51b15bc26434ad14fbfacd619 (patch) | |
tree | 84273652c8d673c86573ea865051ec5571f96846 /sys/netinet6/in6.c | |
parent | 1616ed91c291fd7331f92355984623663f266428 (diff) | |
download | src-test-5490110cf5ab9ba51b15bc26434ad14fbfacd619.tar.gz src-test-5490110cf5ab9ba51b15bc26434ad14fbfacd619.zip |
Notes
Diffstat (limited to 'sys/netinet6/in6.c')
-rw-r--r-- | sys/netinet6/in6.c | 493 |
1 files changed, 255 insertions, 238 deletions
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index cf5eb35fa4612..4bbc4ee4e5b8b 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -821,6 +821,169 @@ out: } /* + * Join necessary multicast groups. Factored out from in6_update_ifa(). + * This entire work should only be done once, for the default FIB. + */ +static int +in6_update_ifa_join_mc(struct ifnet *ifp, struct in6_aliasreq *ifra, + struct in6_ifaddr *ia, int flags, struct in6_multi **in6m_sol) +{ + char ip6buf[INET6_ADDRSTRLEN]; + struct sockaddr_in6 mltaddr, mltmask; + struct in6_addr llsol; + struct in6_multi_mship *imm; + struct rtentry *rt; + int delay, error; + + KASSERT(in6m_sol != NULL, ("%s: in6m_sol is NULL", __func__)); + + /* Join solicited multicast addr for new host id. */ + bzero(&llsol, sizeof(struct in6_addr)); + llsol.s6_addr32[0] = IPV6_ADDR_INT32_MLL; + llsol.s6_addr32[1] = 0; + llsol.s6_addr32[2] = htonl(1); + llsol.s6_addr32[3] = ifra->ifra_addr.sin6_addr.s6_addr32[3]; + llsol.s6_addr8[12] = 0xff; + if ((error = in6_setscope(&llsol, ifp, NULL)) != 0) { + /* XXX: should not happen */ + log(LOG_ERR, "%s: in6_setscope failed\n", __func__); + goto cleanup; + } + delay = 0; + if ((flags & IN6_IFAUPDATE_DADDELAY)) { + /* + * We need a random delay for DAD on the address being + * configured. It also means delaying transmission of the + * corresponding MLD report to avoid report collision. + * [RFC 4861, Section 6.3.7] + */ + delay = arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz); + } + imm = in6_joingroup(ifp, &llsol, &error, delay); + if (imm == NULL) { + nd6log((LOG_WARNING, "%s: addmulti failed for %s on %s " + "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, &llsol), + if_name(ifp), error)); + goto cleanup; + } + LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); + *in6m_sol = imm->i6mm_maddr; + + bzero(&mltmask, sizeof(mltmask)); + mltmask.sin6_len = sizeof(struct sockaddr_in6); + mltmask.sin6_family = AF_INET6; + mltmask.sin6_addr = in6mask32; +#define MLTMASK_LEN 4 /* mltmask's masklen (=32bit=4octet) */ + + /* + * Join link-local all-nodes address. + */ + bzero(&mltaddr, sizeof(mltaddr)); + mltaddr.sin6_len = sizeof(struct sockaddr_in6); + mltaddr.sin6_family = AF_INET6; + mltaddr.sin6_addr = in6addr_linklocal_allnodes; + if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != 0) + goto cleanup; /* XXX: should not fail */ + + /* + * XXX: do we really need this automatic routes? We should probably + * reconsider this stuff. Most applications actually do not need the + * routes, since they usually specify the outgoing interface. + */ + rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL); + if (rt != NULL) { + /* XXX: only works in !SCOPEDROUTING case. */ + if (memcmp(&mltaddr.sin6_addr, + &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, + MLTMASK_LEN)) { + RTFREE_LOCKED(rt); + rt = NULL; + } + } + if (rt == NULL) { + error = rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&mltmask, RTF_UP, + (struct rtentry **)0); + if (error) + goto cleanup; + } else + RTFREE_LOCKED(rt); + + imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, 0); + if (imm == NULL) { + nd6log((LOG_WARNING, "%s: addmulti failed for %s on %s " + "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, + &mltaddr.sin6_addr), if_name(ifp), error)); + goto cleanup; + } + LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); + + /* + * Join node information group address. + */ + delay = 0; + if ((flags & IN6_IFAUPDATE_DADDELAY)) { + /* + * The spec does not say anything about delay for this group, + * but the same logic should apply. + */ + delay = arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz); + } + if (in6_nigroup(ifp, NULL, -1, &mltaddr.sin6_addr) == 0) { + /* XXX jinmei */ + imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, delay); + if (imm == NULL) + nd6log((LOG_WARNING, "%s: addmulti failed for %s on %s " + "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, + &mltaddr.sin6_addr), if_name(ifp), error)); + /* XXX not very fatal, go on... */ + else + LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); + } + + /* + * Join interface-local all-nodes address. + * (ff01::1%ifN, and ff01::%ifN/32) + */ + mltaddr.sin6_addr = in6addr_nodelocal_allnodes; + if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != 0) + goto cleanup; /* XXX: should not fail */ + /* XXX: again, do we really need the route? */ + rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL); + if (rt != NULL) { + if (memcmp(&mltaddr.sin6_addr, + &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, + MLTMASK_LEN)) { + RTFREE_LOCKED(rt); + rt = NULL; + } + } + if (rt == NULL) { + error = rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&mltmask, RTF_UP, + (struct rtentry **)0); + if (error) + goto cleanup; + } else + RTFREE_LOCKED(rt); + + imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, 0); + if (imm == NULL) { + nd6log((LOG_WARNING, "%s: addmulti failed for %s on %s " + "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, + &mltaddr.sin6_addr), if_name(ifp), error)); + goto cleanup; + } + LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); +#undef MLTMASK_LEN + +cleanup: + return (error); +} + +/* * Update parameters of an IPv6 interface address. * If necessary, a new entry is created and linked into address chains. * This function is separated from in6_control(). @@ -833,9 +996,7 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, int error = 0, hostIsNew = 0, plen = -1; struct sockaddr_in6 dst6; struct in6_addrlifetime *lt; - struct in6_multi_mship *imm; struct in6_multi *in6m_sol; - struct rtentry *rt; int delay; char ip6buf[INET6_ADDRSTRLEN]; @@ -1083,172 +1244,12 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, * not just go to unlink. */ - /* Join necessary multicast groups */ + /* Join necessary multicast groups. */ in6m_sol = NULL; if ((ifp->if_flags & IFF_MULTICAST) != 0) { - struct sockaddr_in6 mltaddr, mltmask; - struct in6_addr llsol; - - /* join solicited multicast addr for new host id */ - bzero(&llsol, sizeof(struct in6_addr)); - llsol.s6_addr32[0] = IPV6_ADDR_INT32_MLL; - llsol.s6_addr32[1] = 0; - llsol.s6_addr32[2] = htonl(1); - llsol.s6_addr32[3] = ifra->ifra_addr.sin6_addr.s6_addr32[3]; - llsol.s6_addr8[12] = 0xff; - if ((error = in6_setscope(&llsol, ifp, NULL)) != 0) { - /* XXX: should not happen */ - log(LOG_ERR, "in6_update_ifa: " - "in6_setscope failed\n"); - goto cleanup; - } - delay = 0; - if ((flags & IN6_IFAUPDATE_DADDELAY)) { - /* - * We need a random delay for DAD on the address - * being configured. It also means delaying - * transmission of the corresponding MLD report to - * avoid report collision. - * [RFC 4861, Section 6.3.7] - */ - delay = arc4random() % - (MAX_RTR_SOLICITATION_DELAY * hz); - } - imm = in6_joingroup(ifp, &llsol, &error, delay); - if (imm == NULL) { - nd6log((LOG_WARNING, - "in6_update_ifa: addmulti failed for " - "%s on %s (errno=%d)\n", - ip6_sprintf(ip6buf, &llsol), if_name(ifp), - error)); - goto cleanup; - } - LIST_INSERT_HEAD(&ia->ia6_memberships, - imm, i6mm_chain); - in6m_sol = imm->i6mm_maddr; - - bzero(&mltmask, sizeof(mltmask)); - mltmask.sin6_len = sizeof(struct sockaddr_in6); - mltmask.sin6_family = AF_INET6; - mltmask.sin6_addr = in6mask32; -#define MLTMASK_LEN 4 /* mltmask's masklen (=32bit=4octet) */ - - /* - * join link-local all-nodes address - */ - bzero(&mltaddr, sizeof(mltaddr)); - mltaddr.sin6_len = sizeof(struct sockaddr_in6); - mltaddr.sin6_family = AF_INET6; - mltaddr.sin6_addr = in6addr_linklocal_allnodes; - if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != - 0) - goto cleanup; /* XXX: should not fail */ - - /* - * XXX: do we really need this automatic routes? - * We should probably reconsider this stuff. Most applications - * actually do not need the routes, since they usually specify - * the outgoing interface. - */ - rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL); - if (rt) { - /* XXX: only works in !SCOPEDROUTING case. */ - if (memcmp(&mltaddr.sin6_addr, - &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, - MLTMASK_LEN)) { - RTFREE_LOCKED(rt); - rt = NULL; - } - } - if (!rt) { - error = rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&mltmask, RTF_UP, - (struct rtentry **)0); - if (error) - goto cleanup; - } else { - RTFREE_LOCKED(rt); - } - - imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, 0); - if (!imm) { - nd6log((LOG_WARNING, - "in6_update_ifa: addmulti failed for " - "%s on %s (errno=%d)\n", - ip6_sprintf(ip6buf, &mltaddr.sin6_addr), - if_name(ifp), error)); - goto cleanup; - } - LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); - - /* - * join node information group address - */ - delay = 0; - if ((flags & IN6_IFAUPDATE_DADDELAY)) { - /* - * The spec doesn't say anything about delay for this - * group, but the same logic should apply. - */ - delay = arc4random() % - (MAX_RTR_SOLICITATION_DELAY * hz); - } - if (in6_nigroup(ifp, NULL, -1, &mltaddr.sin6_addr) == 0) { - imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, - delay); /* XXX jinmei */ - if (!imm) { - nd6log((LOG_WARNING, "in6_update_ifa: " - "addmulti failed for %s on %s " - "(errno=%d)\n", - ip6_sprintf(ip6buf, &mltaddr.sin6_addr), - if_name(ifp), error)); - /* XXX not very fatal, go on... */ - } else { - LIST_INSERT_HEAD(&ia->ia6_memberships, - imm, i6mm_chain); - } - } - - /* - * join interface-local all-nodes address. - * (ff01::1%ifN, and ff01::%ifN/32) - */ - mltaddr.sin6_addr = in6addr_nodelocal_allnodes; - if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) - != 0) - goto cleanup; /* XXX: should not fail */ - /* XXX: again, do we really need the route? */ - rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL); - if (rt) { - if (memcmp(&mltaddr.sin6_addr, - &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, - MLTMASK_LEN)) { - RTFREE_LOCKED(rt); - rt = NULL; - } - } - if (!rt) { - error = rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&mltmask, RTF_UP, - (struct rtentry **)0); - if (error) - goto cleanup; - } else - RTFREE_LOCKED(rt); - - imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, 0); - if (!imm) { - nd6log((LOG_WARNING, "in6_update_ifa: " - "addmulti failed for %s on %s " - "(errno=%d)\n", - ip6_sprintf(ip6buf, &mltaddr.sin6_addr), - if_name(ifp), error)); + error = in6_update_ifa_join_mc(ifp, ifra, ia, flags, &in6m_sol); + if (error) goto cleanup; - } - LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); -#undef MLTMASK_LEN } /* @@ -1312,58 +1313,20 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, return error; } -void -in6_purgeaddr(struct ifaddr *ifa) +/* + * Leave multicast groups. Factored out from in6_purgeaddr(). + * This entire work should only be done once, for the default FIB. + */ +static int +in6_purgeaddr_mc(struct ifnet *ifp, struct in6_ifaddr *ia, struct ifaddr *ifa0) { - struct ifnet *ifp = ifa->ifa_ifp; - struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa; - struct in6_multi_mship *imm; struct sockaddr_in6 mltaddr, mltmask; - int plen, error; + struct in6_multi_mship *imm; struct rtentry *rt; - struct ifaddr *ifa0; - - if (ifa->ifa_carp) - (*carp_detach_p)(ifa); - - /* - * find another IPv6 address as the gateway for the - * link-local and node-local all-nodes multicast - * address routes - */ - IF_ADDR_RLOCK(ifp); - TAILQ_FOREACH(ifa0, &ifp->if_addrhead, ifa_link) { - if ((ifa0->ifa_addr->sa_family != AF_INET6) || - memcmp(&satosin6(ifa0->ifa_addr)->sin6_addr, - &ia->ia_addr.sin6_addr, - sizeof(struct in6_addr)) == 0) - continue; - else - break; - } - if (ifa0 != NULL) - ifa_ref(ifa0); - IF_ADDR_RUNLOCK(ifp); - - /* - * Remove the loopback route to the interface address. - * The check for the current setting of "nd6_useloopback" - * is not needed. - */ - if (ia->ia_flags & IFA_RTSELF) { - error = ifa_del_loopback_route((struct ifaddr *)ia, - (struct sockaddr *)&ia->ia_addr); - if (error == 0) - ia->ia_flags &= ~IFA_RTSELF; - } - - /* stop DAD processing */ - nd6_dad_stop(ifa); - - in6_ifremloop(ifa); + int error; /* - * leave from multicast groups we have joined for the interface + * Leave from multicast groups we have joined for the interface. */ while ((imm = LIST_FIRST(&ia->ia6_memberships)) != NULL) { LIST_REMOVE(imm, i6mm_chain); @@ -1371,7 +1334,7 @@ in6_purgeaddr(struct ifaddr *ifa) } /* - * remove the link-local all-nodes address + * Remove the link-local all-nodes address. */ bzero(&mltmask, sizeof(mltmask)); mltmask.sin6_len = sizeof(struct sockaddr_in6); @@ -1384,7 +1347,7 @@ in6_purgeaddr(struct ifaddr *ifa) mltaddr.sin6_addr = in6addr_linklocal_allnodes; if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != 0) - goto cleanup; + return (error); rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL); if (rt != NULL && rt->rt_gateway != NULL && @@ -1392,23 +1355,25 @@ in6_purgeaddr(struct ifaddr *ifa) &ia->ia_addr.sin6_addr, sizeof(ia->ia_addr.sin6_addr)) == 0)) { /* - * if no more IPv6 address exists on this interface - * then remove the multicast address route + * If no more IPv6 address exists on this interface then + * remove the multicast address route. */ if (ifa0 == NULL) { memcpy(&mltaddr.sin6_addr, &satosin6(rt_key(rt))->sin6_addr, sizeof(mltaddr.sin6_addr)); RTFREE_LOCKED(rt); - error = rtrequest(RTM_DELETE, (struct sockaddr *)&mltaddr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&mltmask, RTF_UP, - (struct rtentry **)0); + error = rtrequest(RTM_DELETE, + (struct sockaddr *)&mltaddr, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&mltmask, RTF_UP, + (struct rtentry **)0); if (error) - log(LOG_INFO, "in6_purgeaddr: link-local all-nodes" - "multicast address deletion error\n"); + log(LOG_INFO, "%s: link-local all-nodes " + "multicast address deletion error\n", + __func__); } else { /* - * replace the gateway of the route + * Replace the gateway of the route. */ struct sockaddr_in6 sa; @@ -1427,12 +1392,11 @@ in6_purgeaddr(struct ifaddr *ifa) } /* - * remove the node-local all-nodes address + * Remove the node-local all-nodes address. */ mltaddr.sin6_addr = in6addr_nodelocal_allnodes; - if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != - 0) - goto cleanup; + if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != 0) + return (error); rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL); if (rt != NULL && rt->rt_gateway != NULL && @@ -1440,25 +1404,26 @@ in6_purgeaddr(struct ifaddr *ifa) &ia->ia_addr.sin6_addr, sizeof(ia->ia_addr.sin6_addr)) == 0)) { /* - * if no more IPv6 address exists on this interface - * then remove the multicast address route + * If no more IPv6 address exists on this interface then + * remove the multicast address route. */ if (ifa0 == NULL) { memcpy(&mltaddr.sin6_addr, &satosin6(rt_key(rt))->sin6_addr, sizeof(mltaddr.sin6_addr)); RTFREE_LOCKED(rt); - error = rtrequest(RTM_DELETE, (struct sockaddr *)&mltaddr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&mltmask, RTF_UP, - (struct rtentry **)0); - + error = rtrequest(RTM_DELETE, + (struct sockaddr *)&mltaddr, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&mltmask, RTF_UP, + (struct rtentry **)0); if (error) - log(LOG_INFO, "in6_purgeaddr: node-local all-nodes" - "multicast address deletion error\n"); + log(LOG_INFO, "%s: node-local all-nodes" + "multicast address deletion error\n", + __func__); } else { /* - * replace the gateway of the route + * Replace the gateway of the route. */ struct sockaddr_in6 sa; @@ -1476,7 +1441,59 @@ in6_purgeaddr(struct ifaddr *ifa) RTFREE_LOCKED(rt); } -cleanup: + return (0); +} + +void +in6_purgeaddr(struct ifaddr *ifa) +{ + struct ifnet *ifp = ifa->ifa_ifp; + struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa; + int plen, error; + struct ifaddr *ifa0; + + if (ifa->ifa_carp) + (*carp_detach_p)(ifa); + + /* + * find another IPv6 address as the gateway for the + * link-local and node-local all-nodes multicast + * address routes + */ + IF_ADDR_RLOCK(ifp); + TAILQ_FOREACH(ifa0, &ifp->if_addrhead, ifa_link) { + if ((ifa0->ifa_addr->sa_family != AF_INET6) || + memcmp(&satosin6(ifa0->ifa_addr)->sin6_addr, + &ia->ia_addr.sin6_addr, + sizeof(struct in6_addr)) == 0) + continue; + else + break; + } + if (ifa0 != NULL) + ifa_ref(ifa0); + IF_ADDR_RUNLOCK(ifp); + + /* + * Remove the loopback route to the interface address. + * The check for the current setting of "nd6_useloopback" + * is not needed. + */ + if (ia->ia_flags & IFA_RTSELF) { + error = ifa_del_loopback_route((struct ifaddr *)ia, + (struct sockaddr *)&ia->ia_addr); + if (error == 0) + ia->ia_flags &= ~IFA_RTSELF; + } + + /* stop DAD processing */ + nd6_dad_stop(ifa); + + in6_ifremloop(ifa); + + /* Leave multicast groups. */ + error = in6_purgeaddr_mc(ifp, ia, ifa0); + if (ifa0 != NULL) ifa_free(ifa0); |