1 /* LWIP service - mcast.c - per-socket multicast membership tracking */
3 * Each socket has a linked list of multicast groups of which it is a member.
4 * The linked list consists of 'mcast_member' elements. There is both a global
5 * limit (the number of elements in 'mcast_array') and a per-socket limit on
6 * group membership. Since multiple sockets may join the same multicast
7 * groups, there is not a one-to-one relationship between our membership
8 * structures and the lwIP IGMP/MLD membership structures. Moreover, linking
9 * to the latter structures directly is not intended by lwIP, so we have to
10 * keep our own tracking independent, which in particular means that we have to
11 * make a copy of the multicast group address.
13 * We currently put no effort into saving memory on storing that group address.
14 * Optimization is complicated by the fact that we have to be able to remove
15 * membership structures when their corresponding interface disappears, which
16 * currently involves removal without knowing about the corresponding socket,
17 * and therefore the socket's address family. All of this can be changed.
19 * There is no function to test whether a particular socket is a member of a
20 * multicast group. The pktsock module currently makes the assumption that if
21 * a socket has been joined to any multicast groups, or set any multicast
22 * options, the application is multicast aware and therefore able to figure out
23 * whether it is interested in particular packets, and so we do not filter
24 * incoming packets against the receiving socket's multicast list. This should
25 * be more or less in line with what W. Richard Stevens say that the BSDs do.
31 #include "lwip/igmp.h"
32 #include "lwip/mld6.h"
35 * The per-socket limit on group membership. In theory, the limit should be
36 * high enough that a single socket can join a particular multicast group on
37 * all interfaces that support multicast. In practice, we set it a bit lower
38 * to prevent one socket from using up half of the entries per address family.
39 * Setting it to IP_MAX_MEMBERSHIPS is definitely excessive right now..
41 #define MAX_GROUPS_PER_SOCKET 8
43 static struct mcast_member
{
44 LIST_ENTRY(mcast_member
) mm_next
; /* next in socket, free list */
45 struct ifdev
* mm_ifdev
; /* interface (NULL: free) */
46 ip_addr_t mm_group
; /* group address */
47 } mcast_array
[NR_IPV4_MCAST_GROUP
+ NR_IPV6_MCAST_GROUP
];
49 static LIST_HEAD(, mcast_member
) mcast_freelist
;
52 * Initialize the per-socket multicast membership module.
59 /* Initialize the list of free multicast membership entries. */
60 LIST_INIT(&mcast_freelist
);
62 for (slot
= 0; slot
< __arraycount(mcast_array
); slot
++) {
63 mcast_array
[slot
].mm_ifdev
= NULL
;
65 LIST_INSERT_HEAD(&mcast_freelist
, &mcast_array
[slot
], mm_next
);
70 * Reset the multicast head for a socket. The socket must not have any
71 * previous multicast group memberships.
74 mcast_reset(struct mcast_head
* mcast_head
)
77 LIST_INIT(&mcast_head
->mh_list
);
81 * Attempt to add a per-socket multicast membership association. The given
82 * 'mcast_head' pointer is part of a socket. The 'group' parameter is the
83 * multicast group to join. It is a properly zoned address, but has not been
84 * checked in any other way. If 'ifdev' is not NULL, it is the interface for
85 * the membership; if it is NULL, an interface will be selected using routing.
86 * Return OK if the membership has been successfully removed, or a negative
87 * error code otherwise.
90 mcast_join(struct mcast_head
* mcast_head
, const ip_addr_t
* group
,
93 struct mcast_member
*mm
;
99 * The callers of this function perform only checks that depend on the
100 * address family. We check everything else here.
102 if (!ip_addr_ismulticast(group
))
103 return EADDRNOTAVAIL
;
105 if (!addr_is_valid_multicast(group
))
109 * If no interface was specified, pick one with a routing query. Note
110 * that scoped IPv6 addresses do require an interface to be specified.
113 netif
= ip_route(IP46_ADDR_ANY(IP_GET_TYPE(group
)), group
);
118 ifdev
= netif_get_ifdev(netif
);
121 assert(ifdev
!= NULL
);
122 assert(!IP_IS_V6(group
) ||
123 !ip6_addr_lacks_zone(ip_2_ip6(group
), IP6_MULTICAST
));
125 /* The interface must support multicast. */
126 if (!(ifdev_get_ifflags(ifdev
) & IFF_MULTICAST
))
127 return EADDRNOTAVAIL
;
130 * First see if this socket is already joined to the given group, which
131 * is an error. While looking, also count the number of groups the
132 * socket has joined already, to enforce the per-socket limit.
136 LIST_FOREACH(mm
, &mcast_head
->mh_list
, mm_next
) {
137 if (mm
->mm_ifdev
== ifdev
&& ip_addr_cmp(&mm
->mm_group
, group
))
143 if (count
>= MAX_GROUPS_PER_SOCKET
)
146 /* Do we have a free membership structure available? */
147 if (LIST_EMPTY(&mcast_freelist
))
151 * Nothing can go wrong as far as we are concerned. Ask lwIP to join
152 * the multicast group. This may result in a multicast list update at
155 netif
= ifdev_get_netif(ifdev
);
158 err
= mld6_joingroup_netif(netif
, ip_2_ip6(group
));
160 err
= igmp_joingroup_netif(netif
, ip_2_ip4(group
));
163 return util_convert_err(err
);
166 * Success. Allocate, initialize, and attach a membership structure to
169 mm
= LIST_FIRST(&mcast_freelist
);
171 LIST_REMOVE(mm
, mm_next
);
173 mm
->mm_ifdev
= ifdev
;
174 mm
->mm_group
= *group
;
176 LIST_INSERT_HEAD(&mcast_head
->mh_list
, mm
, mm_next
);
182 * Free the given per-socket multicast membership structure, which must
183 * previously have been associated with a socket. If 'leave_group' is set,
184 * also tell lwIP to leave the corresponding multicast group.
187 mcast_free(struct mcast_member
* mm
, int leave_group
)
192 assert(mm
->mm_ifdev
!= NULL
);
195 netif
= ifdev_get_netif(mm
->mm_ifdev
);
197 if (IP_IS_V6(&mm
->mm_group
))
198 err
= mld6_leavegroup_netif(netif
,
199 ip_2_ip6(&mm
->mm_group
));
201 err
= igmp_leavegroup_netif(netif
,
202 ip_2_ip4(&mm
->mm_group
));
205 panic("lwIP multicast membership desynchronization");
208 LIST_REMOVE(mm
, mm_next
);
212 LIST_INSERT_HEAD(&mcast_freelist
, mm
, mm_next
);
216 * Attempt to remove a per-socket multicast membership association. The given
217 * 'mcast_head' pointer is part of a socket. The 'group' parameter is the
218 * multicast group to leave. It is a properly zoned address, but has not been
219 * checked in any other way. If 'ifdev' is not NULL, it is the interface of
220 * the membership; if it is NULL, a membership matching the address on any
221 * interface will suffice. As such, the parameter requirements mirror those of
222 * mcast_join(). Return OK if the membership has been successfully removed, or
223 * a negative error code otherwise.
226 mcast_leave(struct mcast_head
* mcast_head
, const ip_addr_t
* group
,
227 struct ifdev
* ifdev
)
229 struct mcast_member
*mm
;
232 * Look up a matching entry. The fact that we must find a match for
233 * the given address and interface, keeps us from having to perform
234 * various other checks, such as whether the given address is a
235 * multicast address at all. The exact error codes are not specified.
237 LIST_FOREACH(mm
, &mcast_head
->mh_list
, mm_next
) {
238 if ((ifdev
== NULL
|| mm
->mm_ifdev
== ifdev
) &&
239 ip_addr_cmp(&mm
->mm_group
, group
))
246 mcast_free(mm
, TRUE
/*leave_group*/);
252 * Remove all per-socket multicast membership associations of the given socket.
253 * This function is called when the socket is closed.
256 mcast_leave_all(struct mcast_head
* mcast_head
)
258 struct mcast_member
*mm
;
260 while (!LIST_EMPTY(&mcast_head
->mh_list
)) {
261 mm
= LIST_FIRST(&mcast_head
->mh_list
);
263 mcast_free(mm
, TRUE
/*leave_group*/);
268 * The given interface is about to disappear. Remove and free any per-socket
269 * multicast membership structures associated with the interface, without
270 * leaving the multicast group itself (as that will happen a bit later anyway).
273 mcast_clear(struct ifdev
* ifdev
)
277 for (slot
= 0; slot
< __arraycount(mcast_array
); slot
++) {
278 if (mcast_array
[slot
].mm_ifdev
!= ifdev
)
281 mcast_free(&mcast_array
[slot
], FALSE
/*leave_group*/);