diff options
Diffstat (limited to 'sys/netinet/in.c')
-rw-r--r-- | sys/netinet/in.c | 110 |
1 files changed, 74 insertions, 36 deletions
diff --git a/sys/netinet/in.c b/sys/netinet/in.c index bf1ebae..7c890b1 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$"); #include <netinet/in_pcb.h> #include <netinet/ip_var.h> #include <netinet/vinet.h> +#include <netinet/igmp_var.h> static int in_mask2len(struct in_addr *); static void in_len2mask(struct in_addr *, int); @@ -215,12 +216,14 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, struct in_addr allhosts_addr; struct in_addr dst; struct in_ifaddr *oia; + struct in_ifinfo *ii; struct in_aliasreq *ifra = (struct in_aliasreq *)data; struct sockaddr_in oldaddr; int error, hostIsNew, iaIsNew, maskIsNew, s; int iaIsFirst; ia = NULL; + ii = ((struct in_ifinfo *)ifp->if_afdata[AF_INET]); iaIsFirst = 0; iaIsNew = 0; allhosts_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); @@ -425,8 +428,11 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, if (error != 0 && iaIsNew) break; if (error == 0) { - if (iaIsFirst && (ifp->if_flags & IFF_MULTICAST) != 0) - in_addmulti(&allhosts_addr, ifp); + if (iaIsFirst && + (ifp->if_flags & IFF_MULTICAST) != 0) { + error = in_joingroup(ifp, &allhosts_addr, + NULL, &ii->ii_allhosts); + } EVENTHANDLER_INVOKE(ifaddr_event, ifp); } return (0); @@ -472,8 +478,11 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, (ifra->ifra_broadaddr.sin_family == AF_INET)) ia->ia_broadaddr = ifra->ifra_broadaddr; if (error == 0) { - if (iaIsFirst && (ifp->if_flags & IFF_MULTICAST) != 0) - in_addmulti(&allhosts_addr, ifp); + if (iaIsFirst && + (ifp->if_flags & IFF_MULTICAST) != 0) { + error = in_joingroup(ifp, &allhosts_addr, + NULL, &ii->ii_allhosts); + } EVENTHANDLER_INVOKE(ifaddr_event, ifp); } return (error); @@ -515,18 +524,18 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, /* * 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. + * No state-change report need be transmitted. */ 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); + if (ii->ii_allhosts) { + (void)in_leavegroup_locked(ii->ii_allhosts, + NULL); + ii->ii_allhosts = NULL; + } IN_MULTI_UNLOCK(); IFF_UNLOCKGIANT(ifp); } @@ -993,37 +1002,56 @@ in_broadcast(struct in_addr in, struct ifnet *ifp) } /* + * On interface removal, clean up IPv4 data structures hung off of the ifnet. + */ +void +in_ifdetach(struct ifnet *ifp) +{ + INIT_VNET_INET(ifp->if_vnet); + + in_pcbpurgeif0(&V_ripcbinfo, ifp); + in_pcbpurgeif0(&V_udbinfo, ifp); + in_purgemaddrs(ifp); +} + +/* * Delete all IPv4 multicast address records, and associated link-layer * multicast address records, associated with ifp. + * XXX It looks like domifdetach runs AFTER the link layer cleanup. */ static void in_purgemaddrs(struct ifnet *ifp) { INIT_VNET_INET(ifp->if_vnet); - struct in_multi *inm; - struct in_multi *oinm; + LIST_HEAD(,in_multi) purgeinms; + struct in_multi *inm, *tinm; + struct ifmultiaddr *ifma; - IFF_LOCKGIANT(ifp); + LIST_INIT(&purgeinms); IN_MULTI_LOCK(); - LIST_FOREACH_SAFE(inm, &V_in_multihead, inm_link, oinm) { - if (inm->inm_ifp == ifp) - in_delmulti_locked(inm); + + /* + * Extract list of in_multi associated with the detaching ifp + * which the PF_INET layer is about to release. + * We need to do this as IF_ADDR_LOCK() may be re-acquired + * by code further down. + */ + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_INET) + continue; + inm = (struct in_multi *)ifma->ifma_protospec; + LIST_INSERT_HEAD(&purgeinms, inm, inm_link); } - IN_MULTI_UNLOCK(); - IFF_UNLOCKGIANT(ifp); -} + IF_ADDR_UNLOCK(ifp); -/* - * On interface removal, clean up IPv4 data structures hung off of the ifnet. - */ -void -in_ifdetach(struct ifnet *ifp) -{ - INIT_VNET_INET(ifp->if_vnet); + LIST_FOREACH_SAFE(inm, &purgeinms, inm_link, tinm) { + inm_release_locked(inm); + LIST_REMOVE(inm, inm_link); + } + igmp_ifdetach(ifp); - in_pcbpurgeif0(&V_ripcbinfo, ifp); - in_pcbpurgeif0(&V_udbinfo, ifp); - in_purgemaddrs(ifp); + IN_MULTI_UNLOCK(); } #include <sys/syslog.h> @@ -1250,9 +1278,13 @@ in_lltable_dump(struct lltable *llt, struct sysctl_req *wr) void * in_domifattach(struct ifnet *ifp) -{ - struct lltable *llt = lltable_init(ifp, AF_INET); - +{ + struct in_ifinfo *ii; + struct lltable *llt; + + ii = malloc(sizeof(struct in_ifinfo), M_IFADDR, M_WAITOK|M_ZERO); + + llt = lltable_init(ifp, AF_INET); if (llt != NULL) { llt->llt_new = in_lltable_new; llt->llt_free = in_lltable_free; @@ -1260,13 +1292,19 @@ in_domifattach(struct ifnet *ifp) llt->llt_lookup = in_lltable_lookup; llt->llt_dump = in_lltable_dump; } - return (llt); + ii->ii_llt = llt; + + ii->ii_igmp = igmp_domifattach(ifp); + + return ii; } void -in_domifdetach(struct ifnet *ifp __unused, void *aux) +in_domifdetach(struct ifnet *ifp, void *aux) { - struct lltable *llt = (struct lltable *)aux; + struct in_ifinfo *ii = (struct in_ifinfo *)aux; - lltable_free(llt); + igmp_domifdetach(ifp); + lltable_free(ii->ii_llt); + free(ii, M_IFADDR); } |