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) {

Reply via email to