On 18/11/13(Mon) 11:43, Martin Pieuchot wrote: > Diff below changes the way protocol multicast addresses are linked to > an interface. > > Right now they are added to a list attached to the first protocol > address of an interface. That makes this address descriptor and > its position in the global list special. Plus in the IPv6 case, > a special kludge is used to move multicast records from one > address to another. > > So this diff reuse the design of the protocol agnostic "struct ifaddr" > and adds a new list of multicast addresses, "struct ifmaddr", to the > interface descriptor. It solves the problems described previously and > as a bonus properly free the IPv4 multicast records of an interface > when it is detached, thus plugging some more leaks. > > I tested it with a carp setup, I appreciate any multicast related > tests.
Here's an updated diff after recent header changes. Did anybody tested it? Does it break one of your use cases? Comments? Index: net/if.c =================================================================== RCS file: /cvs/src/sys/net/if.c,v retrieving revision 1.277 diff -u -p -r1.277 if.c --- net/if.c 19 Nov 2013 09:00:43 -0000 1.277 +++ net/if.c 22 Nov 2013 08:17:11 -0000 @@ -437,8 +437,9 @@ if_attach(struct ifnet *ifp) void if_attach_common(struct ifnet *ifp) { - TAILQ_INIT(&ifp->if_addrlist); + TAILQ_INIT(&ifp->if_maddrlist); + ifp->if_addrhooks = malloc(sizeof(*ifp->if_addrhooks), M_TEMP, M_WAITOK); TAILQ_INIT(ifp->if_addrhooks); Index: net/if_var.h =================================================================== RCS file: /cvs/src/sys/net/if_var.h,v retrieving revision 1.1 diff -u -p -r1.1 if_var.h --- net/if_var.h 21 Nov 2013 17:32:12 -0000 1.1 +++ net/if_var.h 22 Nov 2013 08:17:11 -0000 @@ -123,6 +123,7 @@ struct ifnet { /* and the entries */ TAILQ_ENTRY(ifnet) if_list; /* all struct ifnets are chained */ TAILQ_ENTRY(ifnet) if_txlist; /* list of ifnets ready to tx */ TAILQ_HEAD(, ifaddr) if_addrlist; /* linked list of addresses per if */ + TAILQ_HEAD(, ifmaddr) if_maddrlist; /* list of multicast records */ TAILQ_HEAD(, ifg_list) if_groups; /* linked list of groups per if */ struct hook_desc_head *if_addrhooks; /* address change callbacks */ struct hook_desc_head *if_linkstatehooks; /* link change callbacks */ @@ -302,6 +303,16 @@ struct ifaddr_item { struct ifaddr *ifai_ifa; struct ifaddr_item *ifai_next; u_int ifai_rdomain; +}; + +/* + * Interface multicast address. + */ +struct ifmaddr { + struct sockaddr *ifma_addr; /* Protocol address */ + struct ifnet *ifma_ifp; /* Back pointer to ifnet */ + unsigned int ifma_refcnt; /* Count of references */ + TAILQ_ENTRY(ifmaddr) ifma_list; /* Per-interface list */ }; /* Index: netinet/igmp.c =================================================================== RCS file: /cvs/src/sys/netinet/igmp.c,v retrieving revision 1.35 diff -u -p -r1.35 igmp.c --- netinet/igmp.c 18 Oct 2013 09:04:02 -0000 1.35 +++ netinet/igmp.c 22 Nov 2013 08:17:12 -0000 @@ -83,6 +83,7 @@ #include <sys/sysctl.h> #include <net/if.h> +#include <net/if_var.h> #include <net/route.h> #include <netinet/in.h> @@ -193,6 +194,7 @@ igmp_input(struct mbuf *m, ...) struct igmp *igmp; int igmplen; int minlen; + struct ifmaddr *ifma; struct in_multi *inm; struct router_info *rti; struct in_ifaddr *ia; @@ -266,7 +268,10 @@ igmp_input(struct mbuf *m, ...) * except those that are already running and those * that belong to a "local" group (224.0.0.X). */ - IN_FOREACH_MULTI(ia, ifp, inm) { + TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { + if (ifma->ifma_addr->sa_family != AF_INET) + continue; + inm = ifmatoinm(ifma); if (inm->inm_timer == 0 && !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) { inm->inm_state = IGMP_DELAYING_MEMBER; @@ -294,7 +299,10 @@ igmp_input(struct mbuf *m, ...) * timers already running, check if they need to be * reset. */ - IN_FOREACH_MULTI(ia, ifp, inm) { + TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { + if (ifma->ifma_addr->sa_family != AF_INET) + continue; + inm = ifmatoinm(ifma); if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP || ip->ip_dst.s_addr == inm->inm_addr.s_addr)) { @@ -479,6 +487,8 @@ void igmp_leavegroup(struct in_multi *inm) { + int s = splsoftnet(); + switch (inm->inm_state) { case IGMP_DELAYING_MEMBER: case IGMP_IDLE_MEMBER: @@ -494,6 +504,7 @@ igmp_leavegroup(struct in_multi *inm) case IGMP_SLEEPING_MEMBER: break; } + splx(s); } void @@ -521,11 +532,14 @@ void igmp_checktimer(struct ifnet *ifp) { struct in_multi *inm; - struct in_ifaddr *ia; + struct ifmaddr *ifma; splsoftassert(IPL_SOFTNET); - IN_FOREACH_MULTI(ia, ifp, inm) { + TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { + if (ifma->ifma_addr->sa_family != AF_INET) + continue; + inm = ifmatoinm(ifma); if (inm->inm_timer == 0) { /* do nothing */ } else if (--inm->inm_timer == 0) { Index: netinet/in.c =================================================================== RCS file: /cvs/src/sys/netinet/in.c,v retrieving revision 1.88 diff -u -p -r1.88 in.c --- netinet/in.c 23 Oct 2013 13:39:35 -0000 1.88 +++ netinet/in.c 22 Nov 2013 08:17:12 -0000 @@ -68,6 +68,7 @@ #include <sys/socketvar.h> #include <net/if.h> +#include <net/if_var.h> #include <net/route.h> #include "carp.h" @@ -258,7 +259,6 @@ in_control(struct socket *so, u_long cmd ia->ia_broadaddr.sin_family = AF_INET; } ia->ia_ifp = ifp; - LIST_INIT(&ia->ia_multiaddrs); newifaddr = 1; } else @@ -922,8 +922,7 @@ in_addmulti(struct in_addr *ap, struct i { struct in_multi *inm; struct ifreq ifr; - struct in_ifaddr *ia; - int s = splsoftnet(); + int s; /* * See if address already in list. @@ -933,50 +932,47 @@ in_addmulti(struct in_addr *ap, struct i /* * Found it; just increment the reference count. */ - ++inm->inm_refcount; + ++inm->inm_refcnt; } else { + if (ifp->if_ioctl == NULL) + return (NULL); + /* * New address; allocate a new multicast record * and link it into the interface's multicast list. */ - inm = (struct in_multi *)malloc(sizeof(*inm), - M_IPMADDR, M_NOWAIT); - if (inm == NULL) { - splx(s); - return (NULL); - } - inm->inm_addr = *ap; - inm->inm_refcount = 1; - IFP_TO_IA(ifp, ia); - if (ia == NULL) { - free(inm, M_IPMADDR); - splx(s); + inm = malloc(sizeof(*inm), M_IPMADDR, M_NOWAIT); + if (inm == NULL) return (NULL); - } - inm->inm_ia = ia; - ia->ia_ifa.ifa_refcnt++; - LIST_INSERT_HEAD(&ia->ia_multiaddrs, inm, inm_list); + + inm->inm_sin.sin_len = sizeof(struct sockaddr_in); + inm->inm_sin.sin_family = AF_INET; + inm->inm_sin.sin_addr = *ap; + inm->inm_refcnt = 1; + inm->inm_ifp = ifp; + inm->inm_ifma.ifma_addr = sintosa(&inm->inm_sin); + /* * Ask the network driver to update its multicast reception * filter appropriately for the new address. */ - satosin(&ifr.ifr_addr)->sin_len = sizeof(struct sockaddr_in); - satosin(&ifr.ifr_addr)->sin_family = AF_INET; - satosin(&ifr.ifr_addr)->sin_addr = *ap; - if ((ifp->if_ioctl == NULL) || - (*ifp->if_ioctl)(ifp, SIOCADDMULTI,(caddr_t)&ifr) != 0) { - LIST_REMOVE(inm, inm_list); - ifafree(&inm->inm_ia->ia_ifa); + memcpy(&ifr.ifr_addr, &inm->inm_sin, sizeof(inm->inm_sin)); + if ((*ifp->if_ioctl)(ifp, SIOCADDMULTI,(caddr_t)&ifr) != 0) { free(inm, M_IPMADDR); - splx(s); return (NULL); } + + s = splsoftnet(); + TAILQ_INSERT_HEAD(&ifp->if_maddrlist, &inm->inm_ifma, + ifma_list); + splx(s); + /* * Let IGMP know that we have joined a new IP multicast group. */ igmp_joingroup(inm); } - splx(s); + return (inm); } @@ -988,33 +984,31 @@ in_delmulti(struct in_multi *inm) { struct ifreq ifr; struct ifnet *ifp; - int s = splsoftnet(); + int s; - if (--inm->inm_refcount == 0) { + if (--inm->inm_refcnt == 0) { /* * No remaining claims to this record; let IGMP know that * we are leaving the multicast group. */ igmp_leavegroup(inm); + ifp = inm->inm_ifp; + /* - * Unlink from list. + * Notify the network driver to update its multicast + * reception filter. */ - LIST_REMOVE(inm, inm_list); - ifp = inm->inm_ifp; - ifafree(&inm->inm_ia->ia_ifa); + satosin(&ifr.ifr_addr)->sin_len = sizeof(struct sockaddr_in); + satosin(&ifr.ifr_addr)->sin_family = AF_INET; + satosin(&ifr.ifr_addr)->sin_addr = inm->inm_addr; + (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr); + + s = splsoftnet(); + TAILQ_REMOVE(&ifp->if_maddrlist, &inm->inm_ifma, ifma_list); + splx(s); - if (ifp) { - /* - * Notify the network driver to update its multicast - * reception filter. - */ - satosin(&ifr.ifr_addr)->sin_family = AF_INET; - satosin(&ifr.ifr_addr)->sin_addr = inm->inm_addr; - (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr); - } free(inm, M_IPMADDR); } - splx(s); } #endif @@ -1023,11 +1017,20 @@ void in_ifdetach(struct ifnet *ifp) { struct ifaddr *ifa, *next; + struct ifmaddr *ifma, *mnext; /* nuke any of IPv4 addresses we have */ TAILQ_FOREACH_SAFE(ifa, &ifp->if_addrlist, ifa_list, next) { if (ifa->ifa_addr->sa_family != AF_INET) continue; in_purgeaddr(ifa); + } + + TAILQ_FOREACH_SAFE(ifma, &ifp->if_maddrlist, ifma_list, mnext) { + if (ifma->ifma_addr->sa_family != AF_INET) + continue; + + ifma->ifma_refcnt = 1; + in_delmulti(ifmatoinm(ifma)); } } Index: netinet/in_var.h =================================================================== RCS file: /cvs/src/sys/netinet/in_var.h,v retrieving revision 1.28 diff -u -p -r1.28 in_var.h --- netinet/in_var.h 21 Nov 2013 16:34:33 -0000 1.28 +++ netinet/in_var.h 22 Nov 2013 08:17:12 -0000 @@ -56,7 +56,6 @@ struct in_ifaddr { struct sockaddr_in ia_dstaddr; /* reserve space for broadcast addr */ #define ia_broadaddr ia_dstaddr struct sockaddr_in ia_sockmask; /* reserve space for general netmask */ - LIST_HEAD(, in_multi) ia_multiaddrs; /* list of multicast addresses */ struct in_multi *ia_allhosts; /* multicast address record for the allhosts multicast group */ }; @@ -125,32 +124,29 @@ struct router_info { /* * Internet multicast address structure. There is one of these for each IP * multicast group to which this host belongs on a given network interface. - * They are kept in a linked list, rooted in the interface's in_ifaddr - * structure. */ struct in_multi { - struct in_addr inm_addr; /* IP multicast address */ - struct in_ifaddr *inm_ia; /* back pointer to in_ifaddr */ -#define inm_ifp inm_ia->ia_ifp - u_int inm_refcount; /* no. membership claims by sockets */ - u_int inm_timer; /* IGMP membership report timer */ - LIST_ENTRY(in_multi) inm_list; /* list of multicast addresses */ - u_int inm_state; /* state of membership */ - struct router_info *inm_rti; /* router version info */ + struct ifmaddr inm_ifma; /* Protocol-independent info */ +#define inm_refcnt inm_ifma.ifma_refcnt +#define inm_ifp inm_ifma.ifma_ifp + + struct sockaddr_in inm_sin; /* IPv4 multicast address */ +#define inm_addr inm_sin.sin_addr + + u_int inm_state; /* state of membership */ + u_int inm_timer; /* IGMP membership report timer */ + + struct router_info *inm_rti; /* router version info */ }; + #ifdef _KERNEL -/* - * Macro for iterating over all the in_multi records linked to a given - * interface. - */ -#define IN_FOREACH_MULTI(ia, ifp, inm) \ - /* struct in_ifaddr *ia; */ \ - /* struct ifnet *ifp; */ \ - /* struct in_multi *inm; */ \ - IFP_TO_IA((ifp), ia); \ - if (ia != NULL) \ - LIST_FOREACH((inm), &ia->ia_multiaddrs, inm_list) \ + +static __inline struct in_multi * +ifmatoinm(struct ifmaddr *ifma) +{ + return ((struct in_multi *)(ifma)); +} /* * Macro for looking up the in_multi record for a given IP multicast @@ -162,12 +158,15 @@ struct in_multi { /* struct ifnet *ifp; */ \ /* struct in_multi *inm; */ \ do { \ - struct in_ifaddr *ia; \ + struct ifmaddr *ifma; \ \ (inm) = NULL; \ - IN_FOREACH_MULTI(ia, ifp, inm) \ - if ((inm)->inm_addr.s_addr == (addr).s_addr) \ + TAILQ_FOREACH(ifma, &(ifp)->if_maddrlist, ifma_list) \ + if (ifma->ifma_addr->sa_family == AF_INET && \ + ifmatoinm(ifma)->inm_addr.s_addr == (addr).s_addr) {\ + (inm) = ifmatoinm(ifma); \ break; \ + } \ } while (/* CONSTCOND */ 0) int in_ifinit(struct ifnet *, Index: netinet6/in6.c =================================================================== RCS file: /cvs/src/sys/netinet6/in6.c,v retrieving revision 1.125 diff -u -p -r1.125 in6.c --- netinet6/in6.c 22 Nov 2013 07:59:09 -0000 1.125 +++ netinet6/in6.c 22 Nov 2013 08:17:12 -0000 @@ -129,18 +129,6 @@ const struct sockaddr_in6 sa6_any = { }; /* - * This structure is used to keep track of in6_multi chains which belong to - * deleted interface addresses. - */ -static LIST_HEAD(, multi6_kludge) in6_mk; /* XXX BSS initialization */ - -struct multi6_kludge { - LIST_ENTRY(multi6_kludge) mk_entry; - struct ifnet *mk_ifp; - struct in6_multihead mk_head; -}; - -/* * Subroutine for in6_ifaddloop() and in6_ifremloop(). * This routine does actual work. */ @@ -1244,10 +1232,6 @@ in6_unlink_ifa(struct in6_ifaddr *ia, st TAILQ_REMOVE(&in6_ifaddr, ia, ia_list); - if (!LIST_EMPTY(&ia->ia6_multiaddrs)) { - in6_savemkludge(ia); - } - /* Release the reference to the base prefix. */ if (ia->ia6_ndpr == NULL) { char addr[INET6_ADDRSTRLEN]; @@ -1567,141 +1551,19 @@ in6_ifinit(struct ifnet *ifp, struct in6 in6_ifaddloop(&(ia->ia_ifa)); } - if (ifp->if_flags & IFF_MULTICAST) - in6_restoremkludge(ia, ifp); - return (error); } /* - * Multicast address kludge: - * If there were any multicast addresses attached to this interface address, - * either move them to another address on this interface, or save them until - * such time as this interface is reconfigured for IPv6. - */ -void -in6_savemkludge(struct in6_ifaddr *oia) -{ - struct in6_ifaddr *ia; - struct in6_multi *in6m, *next; - - IFP_TO_IA6(oia->ia_ifp, ia); - if (ia) { /* there is another address */ - for (in6m = LIST_FIRST(&oia->ia6_multiaddrs); - in6m != NULL; in6m = next) { - next = LIST_NEXT(in6m, in6m_entry); - ifafree(&in6m->in6m_ia->ia_ifa); - ia->ia_ifa.ifa_refcnt++; - in6m->in6m_ia = ia; - LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry); - } - } else { /* last address on this if deleted, save */ - struct multi6_kludge *mk; - - LIST_FOREACH(mk, &in6_mk, mk_entry) { - if (mk->mk_ifp == oia->ia_ifp) - break; - } - if (mk == NULL) /* this should not happen! */ - panic("in6_savemkludge: no kludge space"); - - for (in6m = LIST_FIRST(&oia->ia6_multiaddrs); - in6m != NULL; in6m = next) { - next = LIST_NEXT(in6m, in6m_entry); - ifafree(&in6m->in6m_ia->ia_ifa); /* release reference */ - in6m->in6m_ia = NULL; - LIST_INSERT_HEAD(&mk->mk_head, in6m, in6m_entry); - } - } -} - -/* - * Continuation of multicast address hack: - * If there was a multicast group list previously saved for this interface, - * then we re-attach it to the first address configured on the i/f. - */ -void -in6_restoremkludge(struct in6_ifaddr *ia, struct ifnet *ifp) -{ - struct multi6_kludge *mk; - - LIST_FOREACH(mk, &in6_mk, mk_entry) { - if (mk->mk_ifp == ifp) { - struct in6_multi *in6m, *next; - - for (in6m = LIST_FIRST(&mk->mk_head); in6m != NULL; - in6m = next) { - next = LIST_NEXT(in6m, in6m_entry); - in6m->in6m_ia = ia; - ia->ia_ifa.ifa_refcnt++; - LIST_INSERT_HEAD(&ia->ia6_multiaddrs, - in6m, in6m_entry); - } - LIST_INIT(&mk->mk_head); - break; - } - } -} - -/* - * Allocate space for the kludge at interface initialization time. - * Formerly, we dynamically allocated the space in in6_savemkludge() with - * malloc(M_WAITOK). However, it was wrong since the function could be called - * under an interrupt context (software timer on address lifetime expiration). - * Also, we cannot just give up allocating the strucutre, since the group - * membership structure is very complex and we need to keep it anyway. - * Of course, this function MUST NOT be called under an interrupt context. - * Specifically, it is expected to be called only from in6_ifattach(), though - * it is a global function. - */ -void -in6_createmkludge(struct ifnet *ifp) -{ - struct multi6_kludge *mk; - - LIST_FOREACH(mk, &in6_mk, mk_entry) { - /* If we've already had one, do not allocate. */ - if (mk->mk_ifp == ifp) - return; - } - - mk = malloc(sizeof(*mk), M_IPMADDR, M_WAITOK | M_ZERO); - - LIST_INIT(&mk->mk_head); - mk->mk_ifp = ifp; - LIST_INSERT_HEAD(&in6_mk, mk, mk_entry); -} - -void -in6_purgemkludge(struct ifnet *ifp) -{ - struct multi6_kludge *mk; - struct in6_multi *in6m; - - LIST_FOREACH(mk, &in6_mk, mk_entry) { - if (mk->mk_ifp != ifp) - continue; - - /* leave from all multicast groups joined */ - while ((in6m = LIST_FIRST(&mk->mk_head)) != NULL) - in6_delmulti(in6m); - LIST_REMOVE(mk, mk_entry); - free(mk, M_IPMADDR); - break; - } -} - -/* * Add an address to the list of IP6 multicast addresses for a * given interface. */ struct in6_multi * in6_addmulti(struct in6_addr *maddr6, struct ifnet *ifp, int *errorp) { - struct in6_ifaddr *ia; struct in6_ifreq ifr; struct in6_multi *in6m; - int s = splsoftnet(); + int s; *errorp = 0; /* @@ -1712,60 +1574,53 @@ in6_addmulti(struct in6_addr *maddr6, st /* * Found it; just increment the refrence count. */ - in6m->in6m_refcount++; + in6m->in6m_refcnt++; } else { + if (ifp->if_ioctl == NULL) { + *errorp = ENXIO; /* XXX: appropriate? */ + return (NULL); + } + /* * New address; allocate a new multicast record * and link it into the interface's multicast list. */ - in6m = (struct in6_multi *) - malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT); + in6m = malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT); if (in6m == NULL) { - splx(s); *errorp = ENOBUFS; return (NULL); } - in6m->in6m_addr = *maddr6; + + in6m->in6m_sin.sin6_len = sizeof(struct sockaddr_in6); + in6m->in6m_sin.sin6_family = AF_INET6; + in6m->in6m_sin.sin6_addr = *maddr6; + in6m->in6m_refcnt = 1; in6m->in6m_ifp = ifp; - in6m->in6m_refcount = 1; - IFP_TO_IA6(ifp, ia); - if (ia == NULL) { - free(in6m, M_IPMADDR); - splx(s); - *errorp = EADDRNOTAVAIL; /* appropriate? */ - return (NULL); - } - in6m->in6m_ia = ia; - ia->ia_ifa.ifa_refcnt++; /* gain a reference */ - LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry); + in6m->in6m_ifma.ifma_addr = sin6tosa(&in6m->in6m_sin); /* * Ask the network driver to update its multicast reception * filter appropriately for the new address. */ - bzero(&ifr.ifr_addr, sizeof(struct sockaddr_in6)); - ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6); - ifr.ifr_addr.sin6_family = AF_INET6; - ifr.ifr_addr.sin6_addr = *maddr6; - if (ifp->if_ioctl == NULL) - *errorp = ENXIO; /* XXX: appropriate? */ - else - *errorp = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, - (caddr_t)&ifr); + memcpy(&ifr.ifr_addr, &in6m->in6m_sin, sizeof(in6m->in6m_sin)); + *errorp = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)&ifr); if (*errorp) { - LIST_REMOVE(in6m, in6m_entry); free(in6m, M_IPMADDR); - ifafree(&ia->ia_ifa); - splx(s); return (NULL); } + + s = splsoftnet(); + TAILQ_INSERT_HEAD(&ifp->if_maddrlist, &in6m->in6m_ifma, + ifma_list); + splx(s); + /* * Let MLD6 know that we have joined a new IP6 multicast * group. */ mld6_start_listening(in6m); } - splx(s); + return (in6m); } @@ -1776,22 +1631,16 @@ void in6_delmulti(struct in6_multi *in6m) { struct in6_ifreq ifr; - int s = splsoftnet(); + struct ifnet *ifp; + int s; - if (--in6m->in6m_refcount == 0) { + if (--in6m->in6m_refcnt == 0) { /* * No remaining claims to this record; let MLD6 know * that we are leaving the multicast group. */ mld6_stop_listening(in6m); - - /* - * Unlink from list. - */ - LIST_REMOVE(in6m, in6m_entry); - if (in6m->in6m_ia) { - ifafree(&in6m->in6m_ia->ia_ifa); /* release reference */ - } + ifp = in6m->in6m_ifp; /* * Notify the network driver to update its multicast @@ -1801,11 +1650,14 @@ in6_delmulti(struct in6_multi *in6m) ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6); ifr.ifr_addr.sin6_family = AF_INET6; ifr.ifr_addr.sin6_addr = in6m->in6m_addr; - (*in6m->in6m_ifp->if_ioctl)(in6m->in6m_ifp, - SIOCDELMULTI, (caddr_t)&ifr); + (*ifp->if_ioctl)(in6m->in6m_ifp, SIOCDELMULTI, (caddr_t)&ifr); + + s = splsoftnet(); + TAILQ_REMOVE(&ifp->if_maddrlist, &in6m->in6m_ifma, ifma_list); + splx(s); + free(in6m, M_IPMADDR); } - splx(s); } struct in6_multi_mship * Index: netinet6/in6_ifattach.c =================================================================== RCS file: /cvs/src/sys/netinet6/in6_ifattach.c,v retrieving revision 1.63 diff -u -p -r1.63 in6_ifattach.c --- netinet6/in6_ifattach.c 19 Nov 2013 09:00:43 -0000 1.63 +++ netinet6/in6_ifattach.c 22 Nov 2013 08:17:12 -0000 @@ -41,6 +41,7 @@ #include <crypto/md5.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_dl.h> #include <net/if_types.h> #include <net/route.h> @@ -594,9 +595,6 @@ in6_ifattach(struct ifnet *ifp, struct i return; } - /* create a multicast kludge storage (if we have not had one) */ - in6_createmkludge(ifp); - /* * quirks based on interface type */ @@ -652,6 +650,7 @@ void in6_ifdetach(struct ifnet *ifp) { struct ifaddr *ifa, *next; + struct ifmaddr *ifma, *mnext; struct rtentry *rt; struct sockaddr_in6 sin6; @@ -670,8 +669,14 @@ in6_ifdetach(struct ifnet *ifp) in6_purgeaddr(ifa); } - /* cleanup multicast address kludge table, if there is any */ - in6_purgemkludge(ifp); + + TAILQ_FOREACH_SAFE(ifma, &ifp->if_maddrlist, ifma_list, mnext) { + if (ifma->ifma_addr->sa_family != AF_INET6) + continue; + + ifma->ifma_refcnt = 1; + in6_delmulti(ifmatoin6m(ifma)); + } /* * remove neighbor management table. we call it twice just to make Index: netinet6/in6_var.h =================================================================== RCS file: /cvs/src/sys/netinet6/in6_var.h,v retrieving revision 1.45 diff -u -p -r1.45 in6_var.h --- netinet6/in6_var.h 22 Nov 2013 07:59:09 -0000 1.45 +++ netinet6/in6_var.h 22 Nov 2013 08:17:12 -0000 @@ -106,8 +106,6 @@ struct in6_ifaddr { struct sockaddr_in6 ia_dstaddr; /* space for destination addr */ struct sockaddr_in6 ia_prefixmask; /* prefix mask */ TAILQ_ENTRY(in6_ifaddr) ia_list; /* list of IP6 addresses */ - LIST_HEAD(in6_multihead, in6_multi) ia6_multiaddrs; - /* list of multicast addresses */ int ia6_flags; struct in6_addrlifetime ia6_lifetime; @@ -458,24 +456,6 @@ do { \ } while (0) /* - * Macro for finding the internet address structure (in6_ifaddr) corresponding - * to a given interface (ifnet structure). - */ -#define IFP_TO_IA6(ifp, ia) \ -/* struct ifnet *ifp; */ \ -/* struct in6_ifaddr *ia; */ \ -do { \ - struct ifaddr *ifa; \ - TAILQ_FOREACH(ifa, &(ifp)->if_addrlist, ifa_list) { \ - if (!ifa->ifa_addr) \ - continue; \ - if (ifa->ifa_addr->sa_family == AF_INET6) \ - break; \ - } \ - (ia) = (struct in6_ifaddr *)ifa; \ -} while (0) - -/* * Multi-cast membership entry. One for each group/ifp that a PCB * belongs to. */ @@ -484,27 +464,23 @@ struct in6_multi_mship { LIST_ENTRY(in6_multi_mship) i6mm_chain; /* multicast options chain */ }; -struct in6_multi { - LIST_ENTRY(in6_multi) in6m_entry; /* list glue */ - struct in6_addr in6m_addr; /* IP6 multicast address */ - struct ifnet *in6m_ifp; /* back pointer to ifnet */ - struct in6_ifaddr *in6m_ia; /* back pointer to in6_ifaddr */ - u_int in6m_refcount; /* # membership claims by sockets */ - u_int in6m_state; /* state of the membership */ - u_int in6m_timer; /* MLD6 listener report timer */ -}; - -/* - * Macro for iterating over all the in6_multi records linked to a given - * interface. - */ -#define IN6_FOREACH_MULTI(ia, ifp, in6m) \ - /* struct in6_ifaddr *ia; */ \ - /* struct ifnet *ifp; */ \ - /* struct in6_multi *in6m; */ \ - IFP_TO_IA6((ifp), ia); \ - if (ia != NULL) \ - LIST_FOREACH((in6m), &ia->ia6_multiaddrs, in6m_entry) \ +struct in6_multi { + struct ifmaddr in6m_ifma; /* Protocol-independent info */ +#define in6m_refcnt in6m_ifma.ifma_refcnt +#define in6m_ifp in6m_ifma.ifma_ifp + + struct sockaddr_in6 in6m_sin; /* IPv6 multicast address */ +#define in6m_addr in6m_sin.sin6_addr + + u_int in6m_state; /* state of membership */ + u_int in6m_timer; /* MLD6 membership report timer */ +}; + +static __inline struct in6_multi * +ifmatoin6m(struct ifmaddr *ifma) +{ + return ((struct in6_multi *)(ifma)); +} /* * Macros for looking up the in6_multi record for a given IP6 multicast @@ -516,12 +492,16 @@ struct in6_multi { /* struct ifnet *ifp; */ \ /* struct in6_multi *in6m; */ \ do { \ - struct in6_ifaddr *ia; \ + struct ifmaddr *ifma; \ \ (in6m) = NULL; \ - IN6_FOREACH_MULTI(ia, ifp, in6m) \ - if (IN6_ARE_ADDR_EQUAL(&(in6m)->in6m_addr, &(addr))) \ + TAILQ_FOREACH(ifma, &(ifp)->if_maddrlist, ifma_list) \ + if (ifma->ifma_addr->sa_family == AF_INET6 && \ + IN6_ARE_ADDR_EQUAL(&ifmatoin6m(ifma)->in6m_addr, \ + &(addr))) { \ + (in6m) = ifmatoin6m(ifma); \ break; \ + } \ } while (/* CONSTCOND */ 0) struct in6_multi *in6_addmulti(struct in6_addr *, struct ifnet *, int *); @@ -533,13 +513,9 @@ int in6_update_ifa(struct ifnet *, struc struct in6_ifaddr *); void in6_purgeaddr(struct ifaddr *); int in6if_do_dad(struct ifnet *); -void in6_savemkludge(struct in6_ifaddr *); void in6_setmaxmtu(void); void *in6_domifattach(struct ifnet *); void in6_domifdetach(struct ifnet *, void *); -void in6_restoremkludge(struct in6_ifaddr *, struct ifnet *); -void in6_createmkludge(struct ifnet *); -void in6_purgemkludge(struct ifnet *); struct in6_ifaddr *in6ifa_ifpforlinklocal(struct ifnet *, int); struct in6_ifaddr *in6ifa_ifpwithaddr(struct ifnet *, struct in6_addr *); int in6_ifpprefix(const struct ifnet *, const struct in6_addr *); Index: netinet6/mld6.c =================================================================== RCS file: /cvs/src/sys/netinet6/mld6.c,v retrieving revision 1.33 diff -u -p -r1.33 mld6.c --- netinet6/mld6.c 14 Nov 2013 23:30:23 -0000 1.33 +++ netinet6/mld6.c 22 Nov 2013 08:17:12 -0000 @@ -74,6 +74,7 @@ #include <dev/rndvar.h> #include <net/if.h> +#include <net/if_var.h> #include <netinet/in.h> #include <netinet6/in6_var.h> @@ -146,6 +147,8 @@ mld6_start_listening(struct in6_multi *i void mld6_stop_listening(struct in6_multi *in6m) { + int s = splsoftnet(); + mld_all_nodes_linklocal.s6_addr16[1] = htons(in6m->in6m_ifp->if_index); /* XXX */ mld_all_routers_linklocal.s6_addr16[1] = @@ -156,6 +159,7 @@ mld6_stop_listening(struct in6_multi *in __IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) > __IPV6_ADDR_SCOPE_INTFACELOCAL) mld6_sendpkt(in6m, MLD_LISTENER_DONE, &mld_all_routers_linklocal); + splx(s); } void @@ -165,7 +169,7 @@ mld6_input(struct mbuf *m, int off) struct mld_hdr *mldh; struct ifnet *ifp = m->m_pkthdr.rcvif; struct in6_multi *in6m; - struct in6_ifaddr *ia; + struct ifmaddr *ifma; int timer; /* timer value in the MLD query header */ IP6_EXTHDR_GET(mldh, struct mld_hdr *, m, off, sizeof(*mldh)); @@ -227,9 +231,6 @@ mld6_input(struct mbuf *m, int off) * - Use the value specified in the query message as * the maximum timeout. */ - IFP_TO_IA6(ifp, ia); - if (ia == NULL) - break; /* * XXX: System timer resolution is too low to handle Max @@ -243,7 +244,10 @@ mld6_input(struct mbuf *m, int off) mld_all_nodes_linklocal.s6_addr16[1] = htons(ifp->if_index); /* XXX */ - LIST_FOREACH(in6m, &ia->ia6_multiaddrs, in6m_entry) { + TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { + if (ifma->ifma_addr->sa_family != AF_INET6) + continue; + in6m = ifmatoin6m(ifma); if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld_all_nodes_linklocal) || __IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < @@ -343,11 +347,14 @@ void mld6_checktimer(struct ifnet *ifp) { struct in6_multi *in6m; - struct in6_ifaddr *ia; + struct ifmaddr *ifma; splsoftassert(IPL_SOFTNET); - IN6_FOREACH_MULTI(ia, ifp, in6m) { + TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { + if (ifma->ifma_addr->sa_family != AF_INET6) + continue; + in6m = ifmatoin6m(ifma); if (in6m->in6m_timer == 0) { /* do nothing */ } else if (--in6m->in6m_timer == 0) {