On Tue, Dec 13, 2016 at 12:35:07PM +0100, Martin Pieuchot wrote:
> Hello,
> 
> On 10/12/16(Sat) 11:08, Dimitris Papastamos wrote:
> > While I was playing around with the ip4 multicast code, I thought
> > I would attempt to make the membership data structure similar to that
> > of ip6.  This means changing from a dynamic array to a linked list.
> 
> I like this a lot.  Coherency is good.
> 
> > The max membership limit has been lifted (I did not see a similar limit
> > in the ip6 code).  I am not sure if this is appropriate for ip4 though.
> > I can add the limit back if necessary.
> 
> I can't think of a reason why it was there in the first place.
> 
> > My system seems to work with this patch but I have not explicitly
> > tested all the code paths I have modified.  I am working on some small
> > tests to prove that it works.
> 
> carp(4), pfsync(4) and vxlan(4) as well as ports using multicast like
> avahi-daemon should be tested.

Updated diff based on your comments below.  I am still testing this though.
If anyone can help with the testing, it would be great!

diff --git a/share/man/man4/ip.4 b/share/man/man4/ip.4
index 82ad06c..607c500 100644
--- a/share/man/man4/ip.4
+++ b/share/man/man4/ip.4
@@ -431,10 +431,6 @@ the host is multihomed.
 Membership is associated with a single interface;
 programs running on multihomed hosts may need to
 join the same group on more than one interface.
-Up to
-.Dv IP_MAX_MEMBERSHIPS
-(currently 4095) memberships may be added on a
-single socket.
 .Pp
 To drop a membership, use:
 .Bd -literal -offset indent
diff --git a/sys/net/if_pfsync.c b/sys/net/if_pfsync.c
index 117d03f..079a593 100644
--- a/sys/net/if_pfsync.c
+++ b/sys/net/if_pfsync.c
@@ -313,10 +313,7 @@ pfsync_clone_create(struct if_clone *ifc, int unit)
        sc->sc_len = PFSYNC_MINPKT;
        sc->sc_maxupdates = 128;
 
-       sc->sc_imo.imo_membership = (struct in_multi **)malloc(
-           (sizeof(struct in_multi *) * IP_MIN_MEMBERSHIPS), M_IPMOPTS,
-           M_WAITOK | M_ZERO);
-       sc->sc_imo.imo_max_memberships = IP_MIN_MEMBERSHIPS;
+       LIST_INIT(&sc->sc_imo.imo_memberships);
 
        ifp = &sc->sc_if;
        snprintf(ifp->if_xname, sizeof ifp->if_xname, "pfsync%d", unit);
@@ -378,7 +375,6 @@ pfsync_clone_destroy(struct ifnet *ifp)
        }
 
        pool_destroy(&sc->sc_pool);
-       free(sc->sc_imo.imo_membership, M_IPMOPTS, 0);
        free(sc, M_DEVBUF, sizeof(*sc));
 
        pfsyncif = NULL;
@@ -1319,11 +1315,14 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
                                    sc->sc_sync_if->if_linkstatehooks,
                                    sc->sc_lhcookie);
                        sc->sc_sync_if = NULL;
-                       if (imo->imo_num_memberships > 0) {
-                               in_delmulti(imo->imo_membership[
-                                   --imo->imo_num_memberships]);
-                               imo->imo_ifidx = 0;
+                       if (!LIST_EMPTY(&imo->imo_memberships)) {
+                               struct in_multi_mship *imm =
+                                   LIST_FIRST(&imo->imo_memberships);
+
+                               LIST_REMOVE(imm, imm_chain);
+                               in_leavegroup(imm);
                        }
+                       imo->imo_ifidx = 0;
                        splx(s);
                        break;
                }
@@ -1345,13 +1344,18 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
                            sc->sc_lhcookie);
                sc->sc_sync_if = sifp;
 
-               if (imo->imo_num_memberships > 0) {
-                       
in_delmulti(imo->imo_membership[--imo->imo_num_memberships]);
-                       imo->imo_ifidx = 0;
+               if (!LIST_EMPTY(&imo->imo_memberships)) {
+                       struct in_multi_mship *imm =
+                           LIST_FIRST(&imo->imo_memberships);
+
+                       LIST_REMOVE(imm, imm_chain);
+                       in_leavegroup(imm);
                }
+               imo->imo_ifidx = 0;
 
                if (sc->sc_sync_if &&
                    sc->sc_sync_peer.s_addr == INADDR_PFSYNC_GROUP) {
+                       struct in_multi_mship *imm;
                        struct in_addr addr;
 
                        if (!(sc->sc_sync_if->if_flags & IFF_MULTICAST)) {
@@ -1362,16 +1366,16 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
 
                        addr.s_addr = INADDR_PFSYNC_GROUP;
 
-                       if ((imo->imo_membership[0] =
-                           in_addmulti(&addr, sc->sc_sync_if)) == NULL) {
+                       if ((imm = in_joingroup(&addr,
+                           sc->sc_sync_if)) == NULL) {
                                sc->sc_sync_if = NULL;
                                splx(s);
                                return (ENOBUFS);
                        }
-                       imo->imo_num_memberships++;
                        imo->imo_ifidx = sc->sc_sync_if->if_index;
                        imo->imo_ttl = PFSYNC_DFLTTL;
                        imo->imo_loop = 0;
+                       LIST_INSERT_HEAD(&imo->imo_memberships, imm, imm_chain);
                }
 
                ip = &sc->sc_template;
diff --git a/sys/net/if_vxlan.c b/sys/net/if_vxlan.c
index e9bc1cb..b9acae1 100644
--- a/sys/net/if_vxlan.c
+++ b/sys/net/if_vxlan.c
@@ -129,10 +129,7 @@ vxlan_clone_create(struct if_clone *ifc, int unit)
            M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
                return (ENOMEM);
 
-       sc->sc_imo.imo_membership = malloc(
-           (sizeof(struct in_multi *) * IP_MIN_MEMBERSHIPS), M_IPMOPTS,
-           M_WAITOK|M_ZERO);
-       sc->sc_imo.imo_max_memberships = IP_MIN_MEMBERSHIPS;
+       LIST_INIT(&sc->sc_imo.imo_memberships);
        sc->sc_dstport = htons(VXLAN_PORT);
        sc->sc_vnetid = VXLAN_VNI_UNSET;
 
@@ -190,7 +187,6 @@ vxlan_clone_destroy(struct ifnet *ifp)
        ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
        ether_ifdetach(ifp);
        if_detach(ifp);
-       free(sc->sc_imo.imo_membership, M_IPMOPTS, 0);
        free(sc, M_DEVBUF, sizeof(*sc));
 
        return (0);
@@ -223,10 +219,14 @@ vxlan_multicast_cleanup(struct ifnet *ifp)
                if_put(mifp);
        }
 
-       if (imo->imo_num_memberships > 0) {
-               in_delmulti(imo->imo_membership[--imo->imo_num_memberships]);
-               imo->imo_ifidx = 0;
+       if (!LIST_EMPTY(&imo->imo_memberships)) {
+               struct in_multi_mship *imm =
+                   LIST_FIRST(&imo->imo_memberships);
+
+               LIST_REMOVE(imm, imm_chain);
+               in_leavegroup(imm);
        }
+       imo->imo_ifidx = 0;
 }
 
 int
@@ -241,6 +241,7 @@ vxlan_multicast_join(struct ifnet *ifp, struct sockaddr 
*src,
 #endif /* INET6 */
        struct ifaddr           *ifa;
        struct ifnet            *mifp;
+       struct in_multi_mship   *imm;
 
        switch (dst->sa_family) {
        case AF_INET:
@@ -272,17 +273,16 @@ vxlan_multicast_join(struct ifnet *ifp, struct sockaddr 
*src,
            (mifp->if_flags & IFF_MULTICAST) == 0)
                return (EADDRNOTAVAIL);
 
-       if ((imo->imo_membership[0] =
-           in_addmulti(&dst4->sin_addr, mifp)) == NULL)
+       if ((imm = in_joingroup(&dst4->sin_addr, mifp)) == NULL)
                return (ENOBUFS);
 
-       imo->imo_num_memberships++;
        imo->imo_ifidx = mifp->if_index;
        if (sc->sc_ttl > 0)
                imo->imo_ttl = sc->sc_ttl;
        else
                imo->imo_ttl = IP_DEFAULT_MULTICAST_TTL;
        imo->imo_loop = 0;
+       LIST_INSERT_HEAD(&imo->imo_memberships, imm, imm_chain);
 
        /*
         * Use interface hooks to track any changes on the interface
diff --git a/sys/netinet/igmp.c b/sys/netinet/igmp.c
index f1b142c..a5eabe9 100644
--- a/sys/netinet/igmp.c
+++ b/sys/netinet/igmp.c
@@ -668,6 +668,7 @@ igmp_sendpkt(struct ifnet *ifp, struct in_multi *inm, int 
type,
 #else
        imo.imo_loop = 0;
 #endif /* MROUTING */
+       LIST_INIT(&imo.imo_memberships);
 
        ip_output(m, router_alert, NULL, IP_MULTICASTOPTS, &imo, NULL, 0);
 
diff --git a/sys/netinet/in.c b/sys/netinet/in.c
index 2513b31..b5380f5 100644
--- a/sys/netinet/in.c
+++ b/sys/netinet/in.c
@@ -908,6 +908,32 @@ in_hasmulti(struct in_addr *ap, struct ifnet *ifp)
        return (joined);
 }
 
+struct in_multi_mship *
+in_joingroup(struct in_addr *ap, struct ifnet *ifp)
+{
+       struct in_multi_mship *imm;
+
+       imm = malloc(sizeof(*imm), M_IPMADDR, M_NOWAIT);
+       if (imm == NULL)
+               return NULL;
+
+       imm->imm_maddr = in_addmulti(ap, ifp);
+       if (imm->imm_maddr == NULL) {
+               free(imm, M_IPMADDR, sizeof(*imm));
+               return NULL;
+       }
+
+       return imm;
+}
+
+void
+in_leavegroup(struct in_multi_mship *imm)
+{
+       if (imm->imm_maddr != NULL)
+               in_delmulti(imm->imm_maddr);
+       free(imm, M_IPMADDR, sizeof(*imm));
+}
+
 void
 in_ifdetach(struct ifnet *ifp)
 {
diff --git a/sys/netinet/in.h b/sys/netinet/in.h
index 4b2b0f5..19b753e 100644
--- a/sys/netinet/in.h
+++ b/sys/netinet/in.h
@@ -342,13 +342,6 @@ struct ip_opts {
  */
 #define        IP_DEFAULT_MULTICAST_TTL  1     /* normally limit m'casts to 1 
hop  */
 #define        IP_DEFAULT_MULTICAST_LOOP 1     /* normally hear sends if a 
member  */
-/*
- * The imo_membership vector for each socket starts at IP_MIN_MEMBERSHIPS
- * and is dynamically allocated at run-time, bounded by IP_MAX_MEMBERSHIPS,
- * and is reallocated when needed, sized according to a power-of-two increment.
- */
-#define        IP_MIN_MEMBERSHIPS      15
-#define        IP_MAX_MEMBERSHIPS      4095
 
 /*
  * Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP.
diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c
index d7de540..12df2b4 100644
--- a/sys/netinet/in_proto.c
+++ b/sys/netinet/in_proto.c
@@ -109,6 +109,7 @@
 #include <net/rtable.h>
 
 #include <netinet/in.h>
+#include <netinet/in_var.h>
 #include <netinet/ip.h>
 #include <netinet/ip_var.h>
 #include <netinet/ip_icmp.h>
diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h
index a265580..b8db50b 100644
--- a/sys/netinet/in_var.h
+++ b/sys/netinet/in_var.h
@@ -123,6 +123,12 @@ struct in_multi {
        struct router_info      *inm_rti;  /* router version info */
 };
 
+/* multicast membership entry */
+struct in_multi_mship {
+       struct in_multi *imm_maddr; /* multicast address pointer */
+       LIST_ENTRY(in_multi_mship) imm_chain; /* multicast options chain */
+};
+
 static __inline struct in_multi *
 ifmatoinm(struct ifmaddr *ifma)
 {
@@ -155,6 +161,8 @@ int in_ifinit(struct ifnet *,
 struct in_multi *in_addmulti(struct in_addr *, struct ifnet *);
 void   in_delmulti(struct in_multi *);
 int    in_hasmulti(struct in_addr *, struct ifnet *);
+struct in_multi_mship *in_joingroup(struct in_addr *, struct ifnet *);
+void   in_leavegroup(struct in_multi_mship *);
 void   in_ifscrub(struct ifnet *, struct in_ifaddr *);
 int    in_control(struct socket *, u_long, caddr_t, struct ifnet *);
 int    in_ioctl(u_long, caddr_t, struct ifnet *, int);
diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c
index 8165216..926cf1e 100644
--- a/sys/netinet/ip_carp.c
+++ b/sys/netinet/ip_carp.c
@@ -789,10 +789,7 @@ carp_clone_create(struct if_clone *ifc, int unit)
 #ifdef INET6
        sc->sc_im6o.im6o_hlim = CARP_DFLTTL;
 #endif /* INET6 */
-       sc->sc_imo.imo_membership = (struct in_multi **)malloc(
-           (sizeof(struct in_multi *) * IP_MIN_MEMBERSHIPS), M_IPMOPTS,
-           M_WAITOK|M_ZERO);
-       sc->sc_imo.imo_max_memberships = IP_MIN_MEMBERSHIPS;
+       LIST_INIT(&sc->sc_imo.imo_memberships);
 
        LIST_INIT(&sc->carp_mc_listhead);
        ifp = &sc->sc_if;
@@ -869,7 +866,6 @@ carp_clone_destroy(struct ifnet *ifp)
        if_detach(ifp);
        carp_destroy_vhosts(ifp->if_softc);
        refcnt_finalize(&sc->sc_refcnt, "carpdtor");
-       free(sc->sc_imo.imo_membership, M_IPMOPTS, 0);
        free(sc, M_DEVBUF, sizeof(*sc));
        return (0);
 }
@@ -1641,16 +1637,14 @@ carp_multicast_cleanup(struct carp_softc *sc)
 #ifdef INET6
        struct ip6_moptions *im6o = &sc->sc_im6o;
 #endif
-       u_int16_t n = imo->imo_num_memberships;
 
-       /* Clean up our own multicast memberships */
-       while (n-- > 0) {
-               if (imo->imo_membership[n] != NULL) {
-                       in_delmulti(imo->imo_membership[n]);
-                       imo->imo_membership[n] = NULL;
-               }
+       while (!LIST_EMPTY(&imo->imo_memberships)) {
+               struct in_multi_mship *imm =
+                   LIST_FIRST(&imo->imo_memberships);
+
+               LIST_REMOVE(imm, imm_chain);
+               in_leavegroup(imm);
        }
-       imo->imo_num_memberships = 0;
        imo->imo_ifidx = 0;
 
 #ifdef INET6
@@ -1837,21 +1831,10 @@ carp_addr_updated(void *v)
                sc->sc_naddrs6 = new_naddrs6;
 
                /* Re-establish multicast membership removed by in_control */
-               if (IN_MULTICAST(sc->sc_peer.s_addr)) {
-                       if (!in_hasmulti(&sc->sc_peer, &sc->sc_if)) {
-                               struct in_multi **imm =
-                                   sc->sc_imo.imo_membership;
-                               u_int16_t maxmem =
-                                   sc->sc_imo.imo_max_memberships;
-
-                               memset(&sc->sc_imo, 0, sizeof(sc->sc_imo));
-                               sc->sc_imo.imo_membership = imm;
-                               sc->sc_imo.imo_max_memberships = maxmem;
-
+               if (IN_MULTICAST(sc->sc_peer.s_addr))
+                       if (!in_hasmulti(&sc->sc_peer, &sc->sc_if))
                                if (sc->sc_carpdev != NULL && sc->sc_naddrs > 0)
                                        carp_join_multicast(sc);
-                       }
-               }
 
                if (sc->sc_naddrs == 0 && sc->sc_naddrs6 == 0) {
                        sc->sc_if.if_flags &= ~IFF_UP;
@@ -1889,21 +1872,20 @@ int
 carp_join_multicast(struct carp_softc *sc)
 {
        struct ip_moptions *imo = &sc->sc_imo;
-       struct in_multi *imm;
+       struct in_multi_mship *imm;
        struct in_addr addr;
 
        if (!IN_MULTICAST(sc->sc_peer.s_addr))
                return (0);
 
        addr.s_addr = sc->sc_peer.s_addr;
-       if ((imm = in_addmulti(&addr, &sc->sc_if)) == NULL)
+       if ((imm = in_joingroup(&addr, &sc->sc_if)) == NULL)
                return (ENOBUFS);
 
-       imo->imo_membership[0] = imm;
-       imo->imo_num_memberships = 1;
        imo->imo_ifidx = sc->sc_if.if_index;
        imo->imo_ttl = CARP_DFLTTL;
        imo->imo_loop = 0;
+       LIST_INSERT_HEAD(&imo->imo_memberships, imm, imm_chain);
        return (0);
 }
 
diff --git a/sys/netinet/ip_mroute.c b/sys/netinet/ip_mroute.c
index e48ded8..abad4c7 100644
--- a/sys/netinet/ip_mroute.c
+++ b/sys/netinet/ip_mroute.c
@@ -80,6 +80,7 @@
 #include <net/route.h>
 
 #include <netinet/in.h>
+#include <netinet/in_var.h>
 #include <netinet/ip.h>
 #include <netinet/ip_var.h>
 #include <netinet/in_pcb.h>
@@ -1599,6 +1600,7 @@ send_packet(struct vif *vifp, struct mbuf *m)
        imo.imo_ifidx = vifp->v_ifp->if_index;
        imo.imo_ttl = mtod(m, struct ip *)->ip_ttl - IPTTLDEC;
        imo.imo_loop = 1;
+       LIST_INIT(&imo.imo_memberships);
 
        s = splsoftnet();
        ip_output(m, NULL, NULL, IP_FORWARDING, &imo, NULL, 0);
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c
index 10b4ada..603d45d 100644
--- a/sys/netinet/ip_output.c
+++ b/sys/netinet/ip_output.c
@@ -1342,10 +1342,10 @@ ip_setmoptions(int optname, struct ip_moptions **imop, 
struct mbuf *m,
        struct ip_mreq *mreq;
        struct ifnet *ifp = NULL;
        struct ip_moptions *imo = *imop;
-       struct in_multi **immp;
+       struct in_multi_mship *imm;
        struct rtentry *rt;
        struct sockaddr_in sin;
-       int i, error = 0;
+       int error = 0;
        u_char loop;
 
        if (imo == NULL) {
@@ -1354,16 +1354,11 @@ ip_setmoptions(int optname, struct ip_moptions **imop, 
struct mbuf *m,
                 * allocate one and initialize to default values.
                 */
                imo = malloc(sizeof(*imo), M_IPMOPTS, M_WAITOK|M_ZERO);
-               immp = (struct in_multi **)malloc(
-                   (sizeof(*immp) * IP_MIN_MEMBERSHIPS), M_IPMOPTS,
-                   M_WAITOK|M_ZERO);
                *imop = imo;
                imo->imo_ifidx = 0;
                imo->imo_ttl = IP_DEFAULT_MULTICAST_TTL;
                imo->imo_loop = IP_DEFAULT_MULTICAST_LOOP;
-               imo->imo_num_memberships = 0;
-               imo->imo_max_memberships = IP_MIN_MEMBERSHIPS;
-               imo->imo_membership = immp;
+               LIST_INIT(&imo->imo_memberships);
        }
 
        switch (optname) {
@@ -1482,65 +1477,29 @@ ip_setmoptions(int optname, struct ip_moptions **imop, 
struct mbuf *m,
                        break;
                }
                /*
-                * See if the membership already exists or if all the
-                * membership slots are full.
+                * See if the membership already exists.
                 */
-               for (i = 0; i < imo->imo_num_memberships; ++i) {
-                       if (imo->imo_membership[i]->inm_ifidx
-                                               == ifp->if_index &&
-                           imo->imo_membership[i]->inm_addr.s_addr
-                                               == mreq->imr_multiaddr.s_addr)
+               LIST_FOREACH(imm, &imo->imo_memberships, imm_chain)
+                       if (imm->imm_maddr->inm_ifidx == ifp->if_index &&
+                           imm->imm_maddr->inm_addr.s_addr ==
+                           mreq->imr_multiaddr.s_addr)
                                break;
-               }
-               if (i < imo->imo_num_memberships) {
+               if (imm != NULL) {
                        error = EADDRINUSE;
                        if_put(ifp);
                        break;
                }
-               if (imo->imo_num_memberships == imo->imo_max_memberships) {
-                       struct in_multi **nmships, **omships;
-                       size_t newmax;
-                       /*
-                        * Resize the vector to next power-of-two minus 1. If 
the
-                        * size would exceed the maximum then we know we've 
really
-                        * run out of entries. Otherwise, we reallocate the 
vector.
-                        */
-                       nmships = NULL;
-                       omships = imo->imo_membership;
-                       newmax = ((imo->imo_max_memberships + 1) * 2) - 1;
-                       if (newmax <= IP_MAX_MEMBERSHIPS) {
-                               nmships = (struct in_multi **)malloc(
-                                   sizeof(*nmships) * newmax, M_IPMOPTS,
-                                   M_NOWAIT|M_ZERO);
-                               if (nmships != NULL) {
-                                       memcpy(nmships, omships,
-                                           sizeof(*omships) *
-                                           imo->imo_max_memberships);
-                                       free(omships, M_IPMOPTS,
-                                           sizeof(*omships) *
-                                           imo->imo_max_memberships);
-                                       imo->imo_membership = nmships;
-                                       imo->imo_max_memberships = newmax;
-                               }
-                       }
-                       if (nmships == NULL) {
-                               error = ENOBUFS;
-                               if_put(ifp);
-                               break;
-                       }
-               }
                /*
                 * Everything looks good; add a new record to the multicast
                 * address list for the given interface.
                 */
-               if ((imo->imo_membership[i] =
-                   in_addmulti(&mreq->imr_multiaddr, ifp)) == NULL) {
+               if ((imm = in_joingroup(&mreq->imr_multiaddr, ifp)) == NULL) {
                        error = ENOBUFS;
                        if_put(ifp);
                        break;
                }
-               ++imo->imo_num_memberships;
                if_put(ifp);
+               LIST_INSERT_HEAD(&imo->imo_memberships, imm, imm_chain);
                break;
 
        case IP_DROP_MEMBERSHIP:
@@ -1576,17 +1535,15 @@ ip_setmoptions(int optname, struct ip_moptions **imop, 
struct mbuf *m,
                        ifp = ia->ia_ifp;
                }
                /*
-                * Find the membership in the membership array.
+                * Find the membership in the membership list.
                 */
-               for (i = 0; i < imo->imo_num_memberships; ++i) {
+               LIST_FOREACH(imm, &imo->imo_memberships, imm_chain)
                        if ((ifp == NULL ||
-                           imo->imo_membership[i]->inm_ifidx ==
-                               ifp->if_index) &&
-                            imo->imo_membership[i]->inm_addr.s_addr ==
-                            mreq->imr_multiaddr.s_addr)
+                           imm->imm_maddr->inm_ifidx == ifp->if_index) &&
+                           imm->imm_maddr->inm_addr.s_addr ==
+                           mreq->imr_multiaddr.s_addr)
                                break;
-               }
-               if (i == imo->imo_num_memberships) {
+               if (imm == NULL) {
                        error = EADDRNOTAVAIL;
                        break;
                }
@@ -1594,13 +1551,8 @@ ip_setmoptions(int optname, struct ip_moptions **imop, 
struct mbuf *m,
                 * Give up the multicast address record to which the
                 * membership points.
                 */
-               in_delmulti(imo->imo_membership[i]);
-               /*
-                * Remove the gap in the membership array.
-                */
-               for (++i; i < imo->imo_num_memberships; ++i)
-                       imo->imo_membership[i-1] = imo->imo_membership[i];
-               --imo->imo_num_memberships;
+               LIST_REMOVE(imm, imm_chain);
+               in_leavegroup(imm);
                break;
 
        default:
@@ -1614,8 +1566,7 @@ ip_setmoptions(int optname, struct ip_moptions **imop, 
struct mbuf *m,
        if (imo->imo_ifidx == 0 &&
            imo->imo_ttl == IP_DEFAULT_MULTICAST_TTL &&
            imo->imo_loop == IP_DEFAULT_MULTICAST_LOOP &&
-           imo->imo_num_memberships == 0) {
-               free(imo->imo_membership , M_IPMOPTS, 0);
+           LIST_EMPTY(&imo->imo_memberships)) {
                free(*imop, M_IPMOPTS, sizeof(**imop));
                *imop = NULL;
        }
@@ -1677,14 +1628,17 @@ ip_getmoptions(int optname, struct ip_moptions *imo, 
struct mbuf **mp)
 void
 ip_freemoptions(struct ip_moptions *imo)
 {
-       int i;
+       struct in_multi_mship *imm;
+
+       if (imo == NULL)
+               return;
 
-       if (imo != NULL) {
-               for (i = 0; i < imo->imo_num_memberships; ++i)
-                       in_delmulti(imo->imo_membership[i]);
-               free(imo->imo_membership, M_IPMOPTS, 0);
-               free(imo, M_IPMOPTS, sizeof(*imo));
+       while (!LIST_EMPTY(&imo->imo_memberships)) {
+               imm = LIST_FIRST(&imo->imo_memberships);
+               LIST_REMOVE(imm, imm_chain);
+               in_leavegroup(imm);
        }
+       free(imo, M_IPMOPTS, sizeof(*imo));
 }
 
 /*
diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h
index 7d1663d..6e1d2cc 100644
--- a/sys/netinet/ip_var.h
+++ b/sys/netinet/ip_var.h
@@ -148,12 +148,10 @@ ipstat_inc(enum ipstat_counters c)
  * passed to ip_output when IP multicast options are in use.
  */
 struct ip_moptions {
-       struct in_multi **imo_membership; /* group memberships */
+       LIST_HEAD(, in_multi_mship) imo_memberships; /* group memberships */
        unsigned short imo_ifidx;       /* ifp index for outgoing multicasts */
        u_int8_t  imo_ttl;      /* TTL for outgoing multicasts */
        u_int8_t  imo_loop;     /* 1 => hear sends if a member */
-       u_int16_t imo_num_memberships;  /* no. memberships this socket */
-       u_int16_t imo_max_memberships;  /* max memberships this socket */
 };
 
 #include <sys/queue.h>
-- 
2.9.0

Reply via email to