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>