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
This commit is contained in:
Qing Li 2009-05-18 02:25:45 +00:00
parent ab7b2d927c
commit 511e8a5343

View file

@ -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);