Hi,

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.

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.

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.

Index: share/man/man4/ip.4
===================================================================
RCS file: /cvs/src/share/man/man4/ip.4,v
retrieving revision 1.41
diff -u -p -r1.41 ip.4
--- share/man/man4/ip.4 18 Aug 2016 11:45:18 -0000      1.41
+++ share/man/man4/ip.4 10 Dec 2016 10:31:07 -0000
@@ -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
Index: sys/net/if_pfsync.c
===================================================================
RCS file: /cvs/src/sys/net/if_pfsync.c,v
retrieving revision 1.238
diff -u -p -r1.238 if_pfsync.c
--- sys/net/if_pfsync.c 22 Nov 2016 19:29:54 -0000      1.238
+++ sys/net/if_pfsync.c 10 Dec 2016 10:31:08 -0000
@@ -313,10 +313,7 @@ pfsync_clone_create(struct if_clone *ifc
        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,9 +1315,11 @@ pfsyncioctl(struct ifnet *ifp, u_long cm
                                    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]);
+                       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);
@@ -1345,13 +1343,17 @@ pfsyncioctl(struct ifnet *ifp, u_long cm
                            sc->sc_lhcookie);
                sc->sc_sync_if = sifp;
 
-               if (imo->imo_num_memberships > 0) {
-                       
in_delmulti(imo->imo_membership[--imo->imo_num_memberships]);
+               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 +1364,15 @@ pfsyncioctl(struct ifnet *ifp, u_long cm
 
                        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;
Index: sys/net/if_vxlan.c
===================================================================
RCS file: /cvs/src/sys/net/if_vxlan.c,v
retrieving revision 1.53
diff -u -p -r1.53 if_vxlan.c
--- sys/net/if_vxlan.c  2 Dec 2016 11:16:04 -0000       1.53
+++ sys/net/if_vxlan.c  10 Dec 2016 10:31:08 -0000
@@ -129,10 +129,7 @@ vxlan_clone_create(struct if_clone *ifc,
            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,8 +219,11 @@ vxlan_multicast_cleanup(struct ifnet *if
                if_put(mifp);
        }
 
-       if (imo->imo_num_memberships > 0) {
-               in_delmulti(imo->imo_membership[--imo->imo_num_memberships]);
+       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;
        }
 }
@@ -241,6 +240,7 @@ vxlan_multicast_join(struct ifnet *ifp, 
 #endif /* INET6 */
        struct ifaddr           *ifa;
        struct ifnet            *mifp;
+       struct in_multi_mship   *imm;
 
        switch (dst->sa_family) {
        case AF_INET:
@@ -272,17 +272,16 @@ vxlan_multicast_join(struct ifnet *ifp, 
            (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
Index: sys/netinet/igmp.c
===================================================================
RCS file: /cvs/src/sys/netinet/igmp.c,v
retrieving revision 1.56
diff -u -p -r1.56 igmp.c
--- sys/netinet/igmp.c  5 Dec 2016 15:31:43 -0000       1.56
+++ sys/netinet/igmp.c  10 Dec 2016 10:31:08 -0000
@@ -664,6 +664,7 @@ igmp_sendpkt(struct in_multi *inm, int t
 #else
        imo.imo_loop = 0;
 #endif /* MROUTING */
+       LIST_INIT(&imo.imo_memberships);
 
        ip_output(m, router_alert, NULL, IP_MULTICASTOPTS, &imo, NULL, 0);
 
Index: sys/netinet/in.c
===================================================================
RCS file: /cvs/src/sys/netinet/in.c,v
retrieving revision 1.130
diff -u -p -r1.130 in.c
--- sys/netinet/in.c    5 Dec 2016 15:31:43 -0000       1.130
+++ sys/netinet/in.c    10 Dec 2016 10:31:08 -0000
@@ -906,6 +906,32 @@ in_hasmulti(struct in_addr *ap, struct i
        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)
 {
Index: sys/netinet/in.h
===================================================================
RCS file: /cvs/src/sys/netinet/in.h,v
retrieving revision 1.119
diff -u -p -r1.119 in.h
--- sys/netinet/in.h    4 Sep 2016 17:05:24 -0000       1.119
+++ sys/netinet/in.h    10 Dec 2016 10:31:08 -0000
@@ -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.
Index: sys/netinet/in_proto.c
===================================================================
RCS file: /cvs/src/sys/netinet/in_proto.c,v
retrieving revision 1.70
diff -u -p -r1.70 in_proto.c
--- sys/netinet/in_proto.c      3 Dec 2015 21:57:59 -0000       1.70
+++ sys/netinet/in_proto.c      10 Dec 2016 10:31:08 -0000
@@ -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>
Index: sys/netinet/in_var.h
===================================================================
RCS file: /cvs/src/sys/netinet/in_var.h,v
retrieving revision 1.39
diff -u -p -r1.39 in_var.h
--- sys/netinet/in_var.h        15 Jun 2016 19:39:34 -0000      1.39
+++ sys/netinet/in_var.h        10 Dec 2016 10:31:08 -0000
@@ -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);
Index: sys/netinet/ip_carp.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_carp.c,v
retrieving revision 1.296
diff -u -p -r1.296 ip_carp.c
--- sys/netinet/ip_carp.c       20 Nov 2016 11:40:58 -0000      1.296
+++ sys/netinet/ip_carp.c       10 Dec 2016 10:31:08 -0000
@@ -787,10 +787,7 @@ carp_clone_create(struct if_clone *ifc, 
 #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;
@@ -867,7 +864,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);
 }
@@ -1625,16 +1621,13 @@ carp_multicast_cleanup(struct carp_softc
 #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
@@ -1821,21 +1814,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;
@@ -1873,21 +1855,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);
 }
 
Index: sys/netinet/ip_mroute.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_mroute.c,v
retrieving revision 1.93
diff -u -p -r1.93 ip_mroute.c
--- sys/netinet/ip_mroute.c     29 Nov 2016 15:52:12 -0000      1.93
+++ sys/netinet/ip_mroute.c     10 Dec 2016 10:31:08 -0000
@@ -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>
@@ -1580,6 +1581,7 @@ send_packet(struct vif *vifp, struct mbu
        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);
Index: sys/netinet/ip_output.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_output.c,v
retrieving revision 1.331
diff -u -p -r1.331 ip_output.c
--- sys/netinet/ip_output.c     28 Nov 2016 10:14:00 -0000      1.331
+++ sys/netinet/ip_output.c     10 Dec 2016 10:31:08 -0000
@@ -1340,10 +1340,10 @@ ip_setmoptions(int optname, struct ip_mo
        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) {
@@ -1352,16 +1352,11 @@ ip_setmoptions(int optname, struct ip_mo
                 * 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) {
@@ -1480,65 +1475,28 @@ ip_setmoptions(int optname, struct ip_mo
                        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,15 +1534,12 @@ ip_setmoptions(int optname, struct ip_mo
                /*
                 * Find the membership in the membership array.
                 */
-               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;
                }
@@ -1592,13 +1547,8 @@ ip_setmoptions(int optname, struct ip_mo
                 * 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:
@@ -1612,8 +1562,7 @@ ip_setmoptions(int optname, struct ip_mo
        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;
        }
@@ -1675,14 +1624,17 @@ ip_getmoptions(int optname, struct ip_mo
 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));
 }
 
 /*
Index: sys/netinet/ip_var.h
===================================================================
RCS file: /cvs/src/sys/netinet/ip_var.h,v
retrieving revision 1.64
diff -u -p -r1.64 ip_var.h
--- sys/netinet/ip_var.h        28 Nov 2016 23:15:31 -0000      1.64
+++ sys/netinet/ip_var.h        10 Dec 2016 10:31:08 -0000
@@ -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>

Reply via email to