summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netinet6/in6.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/netinet6/in6.c')
-rw-r--r--freebsd/sys/netinet6/in6.c726
1 files changed, 375 insertions, 351 deletions
diff --git a/freebsd/sys/netinet6/in6.c b/freebsd/sys/netinet6/in6.c
index 5d9f85ba..a57f3975 100644
--- a/freebsd/sys/netinet6/in6.c
+++ b/freebsd/sys/netinet6/in6.c
@@ -200,6 +200,11 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
switch (cmd) {
case SIOCGETSGCNT_IN6:
case SIOCGETMIFCNT_IN6:
+ /*
+ * XXX mrt_ioctl has a 3rd, unused, FIB argument in route.c.
+ * We cannot see how that would be needed, so do not adjust the
+ * KPI blindly; more likely should clean up the IPv4 variant.
+ */
return (mrt6_ioctl ? mrt6_ioctl(cmd, data) : EOPNOTSUPP);
}
@@ -341,6 +346,18 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
case SIOCGIFSTAT_ICMP6:
sa6 = &ifr->ifr_addr;
break;
+ case SIOCSIFADDR:
+ case SIOCSIFBRDADDR:
+ case SIOCSIFDSTADDR:
+ case SIOCSIFNETMASK:
+ /*
+ * Although we should pass any non-INET6 ioctl requests
+ * down to driver, we filter some legacy INET requests.
+ * Drivers trust SIOCSIFADDR et al to come from an already
+ * privileged layer, and do not perform any credentials
+ * checks or input validation.
+ */
+ return (EINVAL);
default:
sa6 = NULL;
break;
@@ -698,6 +715,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 = in6_rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL, RT_DEFAULT_FIB);
+ 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 = in6_rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)&mltmask, RTF_UP,
+ (struct rtentry **)0, RT_DEFAULT_FIB);
+ 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 = in6_rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL, RT_DEFAULT_FIB);
+ 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 = in6_rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)&mltmask, RTF_UP,
+ (struct rtentry **)0, RT_DEFAULT_FIB);
+ 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().
@@ -710,9 +890,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];
@@ -851,9 +1029,9 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask;
ia->ia_ifp = ifp;
ifa_ref(&ia->ia_ifa); /* if_addrhead */
- IF_ADDR_LOCK(ifp);
+ IF_ADDR_WLOCK(ifp);
TAILQ_INSERT_TAIL(&ifp->if_addrhead, &ia->ia_ifa, ifa_link);
- IF_ADDR_UNLOCK(ifp);
+ IF_ADDR_WUNLOCK(ifp);
ifa_ref(&ia->ia_ifa); /* in6_ifaddrhead */
IN6_IFADDR_WLOCK();
@@ -956,178 +1134,17 @@ 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");
+ error = in6_update_ifa_join_mc(ifp, ifra, ia, flags, &in6m_sol);
+ if (error)
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.
- * [draft-ietf-ipv6-rfc2462bis-02.txt]
- */
- 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));
- goto cleanup;
- }
- LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain);
-#undef MLTMASK_LEN
}
/*
* Perform DAD, if needed.
- * XXX It may be of use, if we can administratively
- * disable DAD.
+ * XXX It may be of use, if we can administratively disable DAD.
*/
if (in6if_do_dad(ifp) && ((ifra->ifra_flags & IN6_IFF_NODAD) == 0) &&
(ia->ia6_flags & IN6_IFF_TENTATIVE))
@@ -1185,87 +1202,29 @@ 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;
- struct rtentry rt0;
- struct sockaddr_dl gateway;
- struct sockaddr_in6 mask, addr;
- int plen, error;
+ struct in6_multi_mship *imm;
struct rtentry *rt;
- struct ifaddr *ifa0, *nifa;
-
- /*
- * find another IPv6 address as the gateway for the
- * link-local and node-local all-nodes multicast
- * address routes
- */
- IF_ADDR_LOCK(ifp);
- TAILQ_FOREACH_SAFE(ifa0, &ifp->if_addrhead, ifa_link, nifa) {
- 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_UNLOCK(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);
-
- IF_AFDATA_LOCK(ifp);
- lla_lookup(LLTABLE6(ifp), (LLE_DELETE | LLE_IFADDR),
- (struct sockaddr *)&ia->ia_addr);
- IF_AFDATA_UNLOCK(ifp);
-
- /*
- * initialize for rtmsg generation
- */
- bzero(&gateway, sizeof(gateway));
- gateway.sdl_len = sizeof(gateway);
- gateway.sdl_family = AF_LINK;
- gateway.sdl_nlen = 0;
- gateway.sdl_alen = ifp->if_addrlen;
- /* */
- bzero(&rt0, sizeof(rt0));
- rt0.rt_gateway = (struct sockaddr *)&gateway;
- memcpy(&mask, &ia->ia_prefixmask, sizeof(ia->ia_prefixmask));
- memcpy(&addr, &ia->ia_addr, sizeof(ia->ia_addr));
- rt_mask(&rt0) = (struct sockaddr *)&mask;
- rt_key(&rt0) = (struct sockaddr *)&addr;
- rt0.rt_flags = RTF_HOST | RTF_STATIC;
- rt_newaddrmsg(RTM_DELETE, ifa, 0, &rt0);
+ struct sockaddr_in6 sin6;
+ int error;
/*
- * leave from multicast groups we have joined for the interface
+ * Leave from multicast groups we have joined for the interface.
*/
- while ((imm = ia->ia6_memberships.lh_first) != NULL) {
+ while ((imm = LIST_FIRST(&ia->ia6_memberships)) != NULL) {
LIST_REMOVE(imm, i6mm_chain);
in6_leavegroup(imm);
}
/*
- * 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);
@@ -1277,43 +1236,51 @@ in6_purgeaddr(struct ifaddr *ifa)
mltaddr.sin6_family = AF_INET6;
mltaddr.sin6_addr = in6addr_linklocal_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);
+ /*
+ * As for the mltaddr above, proactively prepare the sin6 to avoid
+ * rtentry un- and re-locking.
+ */
+ if (ifa0 != NULL) {
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_family = AF_INET6;
+ memcpy(&sin6.sin6_addr, &satosin6(ifa0->ifa_addr)->sin6_addr,
+ sizeof(sin6.sin6_addr));
+ error = in6_setscope(&sin6.sin6_addr, ifa0->ifa_ifp, NULL);
+ if (error != 0)
+ return (error);
+ }
+
+ rt = in6_rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL, RT_DEFAULT_FIB);
if (rt != NULL && rt->rt_gateway != NULL &&
(memcmp(&satosin6(rt->rt_gateway)->sin6_addr,
&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 = in6_rtrequest(RTM_DELETE,
+ (struct sockaddr *)&mltaddr,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)&mltmask, RTF_UP,
+ (struct rtentry **)0, RT_DEFAULT_FIB);
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;
-
- bzero(&sa, sizeof(sa));
- sa.sin6_len = sizeof(struct sockaddr_in6);
- sa.sin6_family = AF_INET6;
- memcpy(&sa.sin6_addr, &satosin6(ifa0->ifa_addr)->sin6_addr,
- sizeof(sa.sin6_addr));
- in6_setscope(&sa.sin6_addr, ifa0->ifa_ifp, NULL);
- memcpy(rt->rt_gateway, &sa, sizeof(sa));
+ memcpy(rt->rt_gateway, &sin6, sizeof(sin6));
RTFREE_LOCKED(rt);
}
} else {
@@ -1322,48 +1289,40 @@ 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);
+ rt = in6_rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL, RT_DEFAULT_FIB);
if (rt != NULL && rt->rt_gateway != NULL &&
(memcmp(&satosin6(rt->rt_gateway)->sin6_addr,
&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 = in6_rtrequest(RTM_DELETE,
+ (struct sockaddr *)&mltaddr,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)&mltmask, RTF_UP,
+ (struct rtentry **)0, RT_DEFAULT_FIB);
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;
-
- bzero(&sa, sizeof(sa));
- sa.sin6_len = sizeof(struct sockaddr_in6);
- sa.sin6_family = AF_INET6;
- memcpy(&sa.sin6_addr, &satosin6(ifa0->ifa_addr)->sin6_addr,
- sizeof(sa.sin6_addr));
- in6_setscope(&sa.sin6_addr, ifa0->ifa_ifp, NULL);
- memcpy(rt->rt_gateway, &sa, sizeof(sa));
+ memcpy(rt->rt_gateway, &sin6, sizeof(sin6));
RTFREE_LOCKED(rt);
}
} else {
@@ -1371,31 +1330,91 @@ 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;
+ struct sockaddr_dl gateway;
+ struct sockaddr_in6 mask, addr;
+ struct rtentry rt0;
+ int plen, error;
+ struct ifaddr *ifa0;
+
+ /*
+ * 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);
+
+ /* Remove local address entry from lltable. */
+ IF_AFDATA_LOCK(ifp);
+ lla_lookup(LLTABLE6(ifp), (LLE_DELETE | LLE_IFADDR),
+ (struct sockaddr *)&ia->ia_addr);
+ IF_AFDATA_UNLOCK(ifp);
+
+ /*
+ * initialize for rtmsg generation
+ */
+ bzero(&gateway, sizeof(gateway));
+ gateway.sdl_len = sizeof(gateway);
+ gateway.sdl_family = AF_LINK;
+ gateway.sdl_nlen = 0;
+ gateway.sdl_alen = ifp->if_addrlen;
+ /* */
+ bzero(&rt0, sizeof(rt0));
+ rt0.rt_gateway = (struct sockaddr *)&gateway;
+ memcpy(&mask, &ia->ia_prefixmask, sizeof(ia->ia_prefixmask));
+ memcpy(&addr, &ia->ia_addr, sizeof(ia->ia_addr));
+ rt_mask(&rt0) = (struct sockaddr *)&mask;
+ rt_key(&rt0) = (struct sockaddr *)&addr;
+ rt0.rt_flags = RTF_HOST | RTF_STATIC;
+ rt_newaddrmsg(RTM_DELETE, ifa, 0, &rt0);
+
+ /* Leave multicast groups. */
+ error = in6_purgeaddr_mc(ifp, ia, ifa0);
+
if (ifa0 != NULL)
ifa_free(ifa0);
plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */
if ((ia->ia_flags & IFA_ROUTE) && plen == 128) {
- int error;
- struct sockaddr *dstaddr;
-
- /*
- * use the interface address if configuring an
- * interface address with a /128 prefix len
- */
- if (ia->ia_dstaddr.sin6_family == AF_INET6)
- dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
- else
- dstaddr = (struct sockaddr *)&ia->ia_addr;
-
- error = rtrequest(RTM_DELETE,
- (struct sockaddr *)dstaddr,
- (struct sockaddr *)&ia->ia_addr,
- (struct sockaddr *)&ia->ia_prefixmask,
- ia->ia_flags | RTF_HOST, NULL);
+ error = rtinit(&(ia->ia_ifa), RTM_DELETE, ia->ia_flags |
+ (ia->ia_dstaddr.sin6_family == AF_INET6) ? RTF_HOST : 0);
if (error != 0)
- return;
+ log(LOG_INFO, "%s: err=%d, destination address delete "
+ "failed\n", __func__, error);
ia->ia_flags &= ~IFA_ROUTE;
}
@@ -1407,9 +1426,9 @@ in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp)
{
int s = splnet();
- IF_ADDR_LOCK(ifp);
+ IF_ADDR_WLOCK(ifp);
TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link);
- IF_ADDR_UNLOCK(ifp);
+ IF_ADDR_WUNLOCK(ifp);
ifa_free(&ia->ia_ifa); /* if_addrhead */
/*
@@ -1641,7 +1660,7 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data,
}
}
- IF_ADDR_LOCK(ifp);
+ IF_ADDR_RLOCK(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
@@ -1662,7 +1681,9 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data,
if (IN6_ARE_ADDR_EQUAL(&candidate, &match))
break;
}
- IF_ADDR_UNLOCK(ifp);
+ if (ifa != NULL)
+ ifa_ref(ifa);
+ IF_ADDR_RUNLOCK(ifp);
if (!ifa)
return EADDRNOTAVAIL;
ia = ifa2ia6(ifa);
@@ -1674,16 +1695,20 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data,
bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin6_len);
error = sa6_recoverscope(
(struct sockaddr_in6 *)&iflr->addr);
- if (error != 0)
+ if (error != 0) {
+ ifa_free(ifa);
return (error);
+ }
if ((ifp->if_flags & IFF_POINTOPOINT) != 0) {
bcopy(&ia->ia_dstaddr, &iflr->dstaddr,
ia->ia_dstaddr.sin6_len);
error = sa6_recoverscope(
(struct sockaddr_in6 *)&iflr->dstaddr);
- if (error != 0)
+ if (error != 0) {
+ ifa_free(ifa);
return (error);
+ }
} else
bzero(&iflr->dstaddr, sizeof(iflr->dstaddr));
@@ -1691,6 +1716,7 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data,
in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL);
iflr->flags = ia->ia6_flags; /* XXX */
+ ifa_free(ifa);
return 0;
} else {
@@ -1714,6 +1740,7 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data,
ia->ia_prefixmask.sin6_len);
ifra.ifra_flags = ia->ia6_flags;
+ ifa_free(ifa);
return in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra,
ifp, td);
}
@@ -1724,8 +1751,7 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data,
}
/*
- * Initialize an interface's intetnet6 address
- * and routing table entry.
+ * Initialize an interface's IPv6 address and routing table entry.
*/
static int
in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia,
@@ -1740,13 +1766,13 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia,
* if this is its first address,
* and to validate the address if necessary.
*/
- IF_ADDR_LOCK(ifp);
+ IF_ADDR_RLOCK(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
ifacount++;
}
- IF_ADDR_UNLOCK(ifp);
+ IF_ADDR_RUNLOCK(ifp);
ia->ia_addr = *sin6;
@@ -1775,30 +1801,28 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia,
if (!(ia->ia_flags & IFA_ROUTE) && plen == 128 &&
ia->ia_dstaddr.sin6_family == AF_INET6) {
int rtflags = RTF_UP | RTF_HOST;
-
- error = rtrequest(RTM_ADD,
- (struct sockaddr *)&ia->ia_dstaddr,
- (struct sockaddr *)&ia->ia_addr,
- (struct sockaddr *)&ia->ia_prefixmask,
- ia->ia_flags | rtflags, NULL);
- if (error != 0)
+ error = rtinit(&ia->ia_ifa, RTM_ADD, ia->ia_flags | rtflags);
+ if (error)
return (error);
ia->ia_flags |= IFA_ROUTE;
+ /*
+ * Handle the case for ::1 .
+ */
+ if (ifp->if_flags & IFF_LOOPBACK)
+ ia->ia_flags |= IFA_RTSELF;
}
/*
* add a loopback route to self
*/
- if (!(ia->ia_flags & IFA_ROUTE)
- && (V_nd6_useloopback
- || (ifp->if_flags & IFF_LOOPBACK))) {
+ if (!(ia->ia_flags & IFA_RTSELF) && V_nd6_useloopback) {
error = ifa_add_loopback_route((struct ifaddr *)ia,
(struct sockaddr *)&ia->ia_addr);
if (error == 0)
ia->ia_flags |= IFA_RTSELF;
}
- /* Add ownaddr as loopback rtentry, if necessary (ex. on p2p link). */
+ /* Add local address to lltable, if necessary (ex. on p2p link). */
if (newhost) {
struct llentry *ln;
struct rtentry rt;
@@ -1806,11 +1830,7 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia,
struct sockaddr_in6 mask, addr;
IF_AFDATA_LOCK(ifp);
- ia->ia_ifa.ifa_rtrequest = NULL;
-
- /* XXX QL
- * we need to report rt_newaddrmsg
- */
+ ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
ln = lla_lookup(LLTABLE6(ifp), (LLE_CREATE | LLE_IFADDR | LLE_EXCLUSIVE),
(struct sockaddr *)&ia->ia_addr);
IF_AFDATA_UNLOCK(ifp);
@@ -1837,6 +1857,7 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia,
rt_mask(&rt) = (struct sockaddr *)&mask;
rt_key(&rt) = (struct sockaddr *)&addr;
rt.rt_flags = RTF_UP | RTF_HOST | RTF_STATIC;
+ /* Announce arrival of local address to all FIBs. */
rt_newaddrmsg(RTM_ADD, &ia->ia_ifa, 0, &rt);
}
@@ -1852,7 +1873,7 @@ in6ifa_ifpforlinklocal(struct ifnet *ifp, int ignoreflags)
{
struct ifaddr *ifa;
- IF_ADDR_LOCK(ifp);
+ IF_ADDR_RLOCK(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
@@ -1864,7 +1885,7 @@ in6ifa_ifpforlinklocal(struct ifnet *ifp, int ignoreflags)
break;
}
}
- IF_ADDR_UNLOCK(ifp);
+ IF_ADDR_RUNLOCK(ifp);
return ((struct in6_ifaddr *)ifa);
}
@@ -1879,7 +1900,7 @@ in6ifa_ifpwithaddr(struct ifnet *ifp, struct in6_addr *addr)
{
struct ifaddr *ifa;
- IF_ADDR_LOCK(ifp);
+ IF_ADDR_RLOCK(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
@@ -1888,7 +1909,7 @@ in6ifa_ifpwithaddr(struct ifnet *ifp, struct in6_addr *addr)
break;
}
}
- IF_ADDR_UNLOCK(ifp);
+ IF_ADDR_RUNLOCK(ifp);
return ((struct in6_ifaddr *)ifa);
}
@@ -2091,7 +2112,7 @@ 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.
*/
- IF_ADDR_LOCK(ifp);
+ IF_ADDR_RLOCK(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
@@ -2125,12 +2146,10 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst)
}
if (besta) {
ifa_ref(&besta->ia_ifa);
- IF_ADDR_UNLOCK(ifp);
+ IF_ADDR_RUNLOCK(ifp);
return (besta);
}
- IF_ADDR_UNLOCK(ifp);
- IN6_IFADDR_RLOCK();
TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
@@ -2148,23 +2167,23 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst)
if (ifa != NULL)
ifa_ref(ifa);
- IN6_IFADDR_RUNLOCK();
+ IF_ADDR_RUNLOCK(ifp);
return (struct in6_ifaddr *)ifa;
}
/* use the last-resort values, that are, deprecated addresses */
if (dep[0]) {
ifa_ref((struct ifaddr *)dep[0]);
- IN6_IFADDR_RUNLOCK();
+ IF_ADDR_RUNLOCK(ifp);
return dep[0];
}
if (dep[1]) {
ifa_ref((struct ifaddr *)dep[1]);
- IN6_IFADDR_RUNLOCK();
+ IF_ADDR_RUNLOCK(ifp);
return dep[1];
}
- IN6_IFADDR_RUNLOCK();
+ IF_ADDR_RUNLOCK(ifp);
return NULL;
}
@@ -2177,7 +2196,7 @@ in6_if_up(struct ifnet *ifp)
struct ifaddr *ifa;
struct in6_ifaddr *ia;
- IF_ADDR_LOCK(ifp);
+ IF_ADDR_RLOCK(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
@@ -2193,7 +2212,7 @@ in6_if_up(struct ifnet *ifp)
arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz));
}
}
- IF_ADDR_UNLOCK(ifp);
+ IF_ADDR_RUNLOCK(ifp);
/*
* special cases, like 6to4, are handled in in6_ifattach
@@ -2248,8 +2267,7 @@ in6_setmaxmtu(void)
struct ifnet *ifp;
IFNET_RLOCK_NOSLEEP();
- for (ifp = TAILQ_FIRST(&V_ifnet); ifp;
- ifp = TAILQ_NEXT(ifp, if_list)) {
+ TAILQ_FOREACH(ifp, &V_ifnet, if_list) {
/* this function can be called during ifnet initialization */
if (!ifp->if_afdata[AF_INET6])
continue;
@@ -2364,19 +2382,25 @@ in6_lltable_free(struct lltable *llt, struct llentry *lle)
static void
in6_lltable_prefix_free(struct lltable *llt,
const struct sockaddr *prefix,
- const struct sockaddr *mask)
+ const struct sockaddr *mask,
+ u_int flags)
{
const struct sockaddr_in6 *pfx = (const struct sockaddr_in6 *)prefix;
const struct sockaddr_in6 *msk = (const struct sockaddr_in6 *)mask;
struct llentry *lle, *next;
register int i;
+ /*
+ * (flags & LLE_STATIC) means deleting all entries
+ * including static ND6 entries
+ */
for (i=0; i < LLTBL_HASHTBL_SIZE; i++) {
LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) {
if (IN6_ARE_MASKED_ADDR_EQUAL(
&((struct sockaddr_in6 *)L3_ADDR(lle))->sin6_addr,
&pfx->sin6_addr,
- &msk->sin6_addr)) {
+ &msk->sin6_addr) &&
+ ((flags & LLE_STATIC) || !(lle->la_flags & LLE_STATIC))) {
int canceled;
canceled = callout_drain(&lle->la_timer);
@@ -2400,8 +2424,10 @@ in6_lltable_rtcheck(struct ifnet *ifp,
KASSERT(l3addr->sa_family == AF_INET6,
("sin_family %d", l3addr->sa_family));
+ /* Our local addresses are always only installed on the default FIB. */
/* XXX rtalloc1 should take a const param */
- rt = rtalloc1(__DECONST(struct sockaddr *, l3addr), 0, 0);
+ rt = in6_rtalloc1(__DECONST(struct sockaddr *, l3addr), 0, 0,
+ RT_DEFAULT_FIB);
if (rt == NULL || (rt->rt_flags & RTF_GATEWAY) || rt->rt_ifp != ifp) {
struct ifaddr *ifa;
/*
@@ -2594,10 +2620,8 @@ in6_domifattach(struct ifnet *ifp)
ext->scope6_id = scope6_ifattach(ifp);
ext->lltable = lltable_init(ifp, AF_INET6);
if (ext->lltable != NULL) {
- ext->lltable->llt_new = in6_lltable_new;
ext->lltable->llt_free = in6_lltable_free;
ext->lltable->llt_prefix_free = in6_lltable_prefix_free;
- ext->lltable->llt_rtcheck = in6_lltable_rtcheck;
ext->lltable->llt_lookup = in6_lltable_lookup;
ext->lltable->llt_dump = in6_lltable_dump;
}