etc/services - sync with NetBSD-8
[minix.git] / minix / net / lwip / mcast.c
blob832dce1a42bae87b0676e5ac8401c318fa1d4e4b
1 /* LWIP service - mcast.c - per-socket multicast membership tracking */
2 /*
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.
28 #include "lwip.h"
29 #include "mcast.h"
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.
54 void
55 mcast_init(void)
57 unsigned int slot;
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.
73 void
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.
89 int
90 mcast_join(struct mcast_head * mcast_head, const ip_addr_t * group,
91 struct ifdev * ifdev)
93 struct mcast_member *mm;
94 struct netif *netif;
95 unsigned int count;
96 err_t err;
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))
106 return EINVAL;
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.
112 if (ifdev == NULL) {
113 netif = ip_route(IP46_ADDR_ANY(IP_GET_TYPE(group)), group);
115 if (netif == NULL)
116 return EHOSTUNREACH;
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.
134 count = 0;
136 LIST_FOREACH(mm, &mcast_head->mh_list, mm_next) {
137 if (mm->mm_ifdev == ifdev && ip_addr_cmp(&mm->mm_group, group))
138 return EEXIST;
140 count++;
143 if (count >= MAX_GROUPS_PER_SOCKET)
144 return ENOBUFS;
146 /* Do we have a free membership structure available? */
147 if (LIST_EMPTY(&mcast_freelist))
148 return ENOBUFS;
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
153 * the driver end.
155 netif = ifdev_get_netif(ifdev);
157 if (IP_IS_V6(group))
158 err = mld6_joingroup_netif(netif, ip_2_ip6(group));
159 else
160 err = igmp_joingroup_netif(netif, ip_2_ip4(group));
162 if (err != ERR_OK)
163 return util_convert_err(err);
166 * Success. Allocate, initialize, and attach a membership structure to
167 * the socket.
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);
178 return OK;
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.
186 static void
187 mcast_free(struct mcast_member * mm, int leave_group)
189 struct netif *netif;
190 err_t err;
192 assert(mm->mm_ifdev != NULL);
194 if (leave_group) {
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));
200 else
201 err = igmp_leavegroup_netif(netif,
202 ip_2_ip4(&mm->mm_group));
204 if (err != ERR_OK)
205 panic("lwIP multicast membership desynchronization");
208 LIST_REMOVE(mm, mm_next);
210 mm->mm_ifdev = NULL;
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))
240 break;
243 if (mm == NULL)
244 return ESRCH;
246 mcast_free(mm, TRUE /*leave_group*/);
248 return OK;
252 * Remove all per-socket multicast membership associations of the given socket.
253 * This function is called when the socket is closed.
255 void
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).
272 void
273 mcast_clear(struct ifdev * ifdev)
275 unsigned int slot;
277 for (slot = 0; slot < __arraycount(mcast_array); slot++) {
278 if (mcast_array[slot].mm_ifdev != ifdev)
279 continue;
281 mcast_free(&mcast_array[slot], FALSE /*leave_group*/);