summaryrefslogtreecommitdiffstats
path: root/sys/netinet6
diff options
context:
space:
mode:
authorqingli <qingli@FreeBSD.org>2009-05-18 02:25:45 +0000
committerqingli <qingli@FreeBSD.org>2009-05-18 02:25:45 +0000
commita501d9a071f1dfe8471bed22e728be03188465b2 (patch)
treee307bdd708038b68c2599a105c304b03dc00feaf /sys/netinet6
parent8dd08ce26523a86f2f91de35a2bad2d04bd3204b (diff)
downloadFreeBSD-src-a501d9a071f1dfe8471bed22e728be03188465b2.zip
FreeBSD-src-a501d9a071f1dfe8471bed22e728be03188465b2.tar.gz
This patch resolves the following issues:
-- A routing socket message is not generated when an IPv6 address is either inserted or deleted from an interface. The missing routing message problem was discovered by Randall Stewart and Michael Tuxen during SCTP testing. -- Previously when an IPv6 address is configured on an interface, if the prefix length is /128, then a host route is instaleld in the kernel for this address. But this host route is not deleted when that IPv6 address is removed from the interface. -- Routes to the link-local all-nodes multicast address and the interface-local all-nodes multicast address are not removed when the last IPv6 address is removed from an interface. Reviewed by: bz, gnn
Diffstat (limited to 'sys/netinet6')
-rw-r--r--sys/netinet6/in6.c197
1 files changed, 196 insertions, 1 deletions
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
index df57bf6..a2321ff 100644
--- a/sys/netinet6/in6.c
+++ b/sys/netinet6/in6.c
@@ -1151,6 +1151,28 @@ in6_purgeaddr(struct ifaddr *ifa)
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 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
+ */
+ 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;
+ }
/* stop DAD processing */
nd6_dad_stop(ifa);
@@ -1159,7 +1181,25 @@ in6_purgeaddr(struct ifaddr *ifa)
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 from multicast groups we have joined for the interface
*/
@@ -1168,6 +1208,139 @@ in6_purgeaddr(struct ifaddr *ifa)
in6_leavegroup(imm);
}
+ /*
+ * remove the link-local all-nodes address
+ */
+ bzero(&mltmask, sizeof(mltmask));
+ mltmask.sin6_len = sizeof(struct sockaddr_in6);
+ mltmask.sin6_family = AF_INET6;
+ mltmask.sin6_addr = in6mask32;
+
+ 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;
+
+ rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL);
+ 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 (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);
+ if (error)
+ log(LOG_INFO, "in6_purgeaddr: link-local all-nodes"
+ "multicast address deletion error\n");
+ } else {
+ /*
+ * 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));
+ RTFREE_LOCKED(rt);
+ }
+ } else {
+ if (rt != NULL)
+ RTFREE_LOCKED(rt);
+ }
+
+ /*
+ * 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;
+
+ rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL);
+ 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 (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);
+
+ if (error)
+ log(LOG_INFO, "in6_purgeaddr: node-local all-nodes"
+ "multicast address deletion error\n");
+ } else {
+ /*
+ * 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));
+ RTFREE_LOCKED(rt);
+ }
+ } else {
+ if (rt != NULL)
+ RTFREE_LOCKED(rt);
+ }
+
+cleanup:
+
+ 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);
+ if (error != 0)
+ return;
+ ia->ia_flags &= ~IFA_ROUTE;
+ }
+
in6_unlink_ifa(ia, ifp);
}
@@ -1571,6 +1744,9 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia,
/* Add ownaddr as loopback rtentry, if necessary (ex. on p2p link). */
if (newhost) {
struct llentry *ln;
+ struct rtentry rt;
+ struct sockaddr_dl gateway;
+ struct sockaddr_in6 mask, addr;
IF_AFDATA_LOCK(ifp);
ia->ia_ifa.ifa_rtrequest = NULL;
@@ -1584,8 +1760,27 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia,
if (ln != NULL) {
ln->la_expire = 0; /* for IPv6 this means permanent */
ln->ln_state = ND6_LLINFO_REACHABLE;
+ /*
+ * 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 = 6;
+ memcpy(gateway.sdl_data, &ln->ll_addr.mac_aligned, sizeof(ln->ll_addr));
+ /* */
LLE_WUNLOCK(ln);
}
+
+ bzero(&rt, sizeof(rt));
+ rt.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(&rt) = (struct sockaddr *)&mask;
+ rt_key(&rt) = (struct sockaddr *)&addr;
+ rt.rt_flags = RTF_UP | RTF_HOST | RTF_STATIC;
+ rt_newaddrmsg(RTM_ADD, &ia->ia_ifa, 0, &rt);
}
return (error);
OpenPOWER on IntegriCloud