Fix a bug in IPv4 address configuration exposed by refcounting.

* Join the IPv4 all-hosts multicast group 224.0.0.1 once only;
   that is, when an IPv4 address is first configured on an interface.
 * Do not join it for subsequent IPv4 addresses as this violates IGMP.
 * Be sure to leave the group when all IPv4 addresses have been removed
   from the interface.
 * Add two DIAGNOSTIC printfs related to the issue.

Further care and attention is needed in this area; it is suggested that
netinet's attachment to the ifnet structure be compartmentalized and
non-implicit.

Bug found by:	andre
MFC after:	1 month
This commit is contained in:
Bruce M Simpson 2007-03-29 21:39:22 +00:00
parent 99469fe270
commit f7e083af90

View File

@ -224,13 +224,17 @@ in_control(so, cmd, data, ifp, td)
register struct ifreq *ifr = (struct ifreq *)data;
register struct in_ifaddr *ia = 0, *iap;
register struct ifaddr *ifa;
struct in_addr allhosts_addr;
struct in_addr dst;
struct in_ifaddr *oia;
struct in_aliasreq *ifra = (struct in_aliasreq *)data;
struct sockaddr_in oldaddr;
int error, hostIsNew, iaIsNew, maskIsNew, s;
int iaIsFirst;
iaIsFirst = 0;
iaIsNew = 0;
allhosts_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
switch (cmd) {
case SIOCALIFADDR:
@ -281,6 +285,8 @@ in_control(so, cmd, data, ifp, td)
break;
}
}
if (ia == NULL)
iaIsFirst = 1;
}
switch (cmd) {
@ -422,8 +428,11 @@ in_control(so, cmd, data, ifp, td)
(struct sockaddr_in *) &ifr->ifr_addr, 1);
if (error != 0 && iaIsNew)
break;
if (error == 0)
if (error == 0) {
if (iaIsFirst && (ifp->if_flags & IFF_MULTICAST) != 0)
in_addmulti(&allhosts_addr, ifp);
EVENTHANDLER_INVOKE(ifaddr_event, ifp);
}
return (0);
case SIOCSIFNETMASK:
@ -466,8 +475,11 @@ in_control(so, cmd, data, ifp, td)
if ((ifp->if_flags & IFF_BROADCAST) &&
(ifra->ifra_broadaddr.sin_family == AF_INET))
ia->ia_broadaddr = ifra->ifra_broadaddr;
if (error == 0)
if (error == 0) {
if (iaIsFirst && (ifp->if_flags & IFF_MULTICAST) != 0)
in_addmulti(&allhosts_addr, ifp);
EVENTHANDLER_INVOKE(ifaddr_event, ifp);
}
return (error);
case SIOCDIFADDR:
@ -502,8 +514,27 @@ in_control(so, cmd, data, ifp, td)
s = splnet();
TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link);
TAILQ_REMOVE(&in_ifaddrhead, ia, ia_link);
if (ia->ia_addr.sin_family == AF_INET)
if (ia->ia_addr.sin_family == AF_INET) {
LIST_REMOVE(ia, ia_hash);
/*
* If this is the last IPv4 address configured on this
* interface, leave the all-hosts group.
* XXX: This is quite ugly because of locking and structure.
*/
oia = NULL;
IFP_TO_IA(ifp, oia);
if (oia == NULL) {
struct in_multi *inm;
IFF_LOCKGIANT(ifp);
IN_MULTI_LOCK();
IN_LOOKUP_MULTI(allhosts_addr, ifp, inm);
if (inm != NULL)
in_delmulti_locked(inm);
IN_MULTI_UNLOCK();
IFF_UNLOCKGIANT(ifp);
}
}
IFAFREE(&ia->ia_ifa);
splx(s);
@ -792,16 +823,6 @@ in_ifinit(ifp, ia, sin, scrub)
if ((error = in_addprefix(ia, flags)) != 0)
return (error);
/*
* If the interface supports multicast, join the "all hosts"
* multicast group on that interface.
*/
if (ifp->if_flags & IFF_MULTICAST) {
struct in_addr addr;
addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
in_addmulti(&addr, ifp);
}
return (error);
}
@ -1113,6 +1134,9 @@ in_delmulti_locked(struct in_multi *inm)
igmp_leavegroup(inm);
ifma = inm->inm_ifma;
#ifdef DIAGNOSTIC
printf("%s: purging ifma %p\n", __func__, ifma);
#endif
KASSERT(ifma->ifma_protospec == inm,
("%s: ifma_protospec != inm", __func__));
ifma->ifma_protospec = NULL;
@ -1134,6 +1158,9 @@ in_purgemaddrs(struct ifnet *ifp)
struct in_multi *inm;
struct in_multi *oinm;
#ifdef DIAGNOSTIC
printf("%s: purging ifp %p\n", __func__, ifp);
#endif
IFF_LOCKGIANT(ifp);
IN_MULTI_LOCK();
LIST_FOREACH_SAFE(inm, &in_multihead, inm_link, oinm) {