Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/netinet/in_mcast.c
39475 views
1
/*-
2
* SPDX-License-Identifier: BSD-3-Clause
3
*
4
* Copyright (c) 2007-2009 Bruce Simpson.
5
* Copyright (c) 2005 Robert N. M. Watson.
6
* All rights reserved.
7
*
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions
10
* are met:
11
* 1. Redistributions of source code must retain the above copyright
12
* notice, this list of conditions and the following disclaimer.
13
* 2. Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in the
15
* documentation and/or other materials provided with the distribution.
16
* 3. The name of the author may not be used to endorse or promote
17
* products derived from this software without specific prior written
18
* permission.
19
*
20
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
* SUCH DAMAGE.
31
*/
32
33
/*
34
* IPv4 multicast socket, group, and socket option processing module.
35
*/
36
37
#include <sys/param.h>
38
#include <sys/systm.h>
39
#include <sys/kernel.h>
40
#include <sys/lock.h>
41
#include <sys/malloc.h>
42
#include <sys/mbuf.h>
43
#include <sys/protosw.h>
44
#include <sys/socket.h>
45
#include <sys/socketvar.h>
46
#include <sys/protosw.h>
47
#include <sys/sysctl.h>
48
#include <sys/ktr.h>
49
#include <sys/taskqueue.h>
50
#include <sys/tree.h>
51
52
#include <net/if.h>
53
#include <net/if_var.h>
54
#include <net/if_dl.h>
55
#include <net/route.h>
56
#include <net/route/nhop.h>
57
#include <net/vnet.h>
58
59
#include <net/ethernet.h>
60
61
#include <netinet/in.h>
62
#include <netinet/in_systm.h>
63
#include <netinet/in_fib.h>
64
#include <netinet/in_pcb.h>
65
#include <netinet/in_var.h>
66
#include <net/if_private.h>
67
#include <netinet/ip_var.h>
68
#include <netinet/igmp_var.h>
69
70
#ifndef KTR_IGMPV3
71
#define KTR_IGMPV3 KTR_INET
72
#endif
73
74
#ifndef __SOCKUNION_DECLARED
75
union sockunion {
76
struct sockaddr_storage ss;
77
struct sockaddr sa;
78
struct sockaddr_dl sdl;
79
struct sockaddr_in sin;
80
};
81
typedef union sockunion sockunion_t;
82
#define __SOCKUNION_DECLARED
83
#endif /* __SOCKUNION_DECLARED */
84
85
static MALLOC_DEFINE(M_INMFILTER, "in_mfilter",
86
"IPv4 multicast PCB-layer source filter");
87
static MALLOC_DEFINE(M_IPMADDR, "in_multi", "IPv4 multicast group");
88
static MALLOC_DEFINE(M_IPMOPTS, "ip_moptions", "IPv4 multicast options");
89
static MALLOC_DEFINE(M_IPMSOURCE, "ip_msource",
90
"IPv4 multicast IGMP-layer source filter");
91
92
/*
93
* Locking:
94
*
95
* - Lock order is: IN_MULTI_LOCK, INP_WLOCK, IN_MULTI_LIST_LOCK, IGMP_LOCK,
96
* IF_ADDR_LOCK.
97
* - The IF_ADDR_LOCK is implicitly taken by inm_lookup() earlier, however
98
* it can be taken by code in net/if.c also.
99
* - ip_moptions and in_mfilter are covered by the INP_WLOCK.
100
*
101
* struct in_multi is covered by IN_MULTI_LIST_LOCK. There isn't strictly
102
* any need for in_multi itself to be virtualized -- it is bound to an ifp
103
* anyway no matter what happens.
104
*/
105
struct mtx in_multi_list_mtx;
106
MTX_SYSINIT(in_multi_mtx, &in_multi_list_mtx, "in_multi_list_mtx", MTX_DEF);
107
108
struct mtx in_multi_free_mtx;
109
MTX_SYSINIT(in_multi_free_mtx, &in_multi_free_mtx, "in_multi_free_mtx", MTX_DEF);
110
111
struct sx in_multi_sx;
112
SX_SYSINIT(in_multi_sx, &in_multi_sx, "in_multi_sx");
113
114
/*
115
* Functions with non-static linkage defined in this file should be
116
* declared in in_var.h:
117
* imo_multi_filter()
118
* in_joingroup()
119
* in_joingroup_locked()
120
* in_leavegroup()
121
* in_leavegroup_locked()
122
* and ip_var.h:
123
* inp_freemoptions()
124
* inp_getmoptions()
125
* inp_setmoptions()
126
*/
127
static void imf_commit(struct in_mfilter *);
128
static int imf_get_source(struct in_mfilter *imf,
129
const struct sockaddr_in *psin,
130
struct in_msource **);
131
static struct in_msource *
132
imf_graft(struct in_mfilter *, const uint8_t,
133
const struct sockaddr_in *);
134
static void imf_leave(struct in_mfilter *);
135
static int imf_prune(struct in_mfilter *, const struct sockaddr_in *);
136
static void imf_purge(struct in_mfilter *);
137
static void imf_rollback(struct in_mfilter *);
138
static void imf_reap(struct in_mfilter *);
139
static struct in_mfilter *
140
imo_match_group(const struct ip_moptions *,
141
const struct ifnet *, const struct sockaddr *);
142
static struct in_msource *
143
imo_match_source(struct in_mfilter *, const struct sockaddr *);
144
static void ims_merge(struct ip_msource *ims,
145
const struct in_msource *lims, const int rollback);
146
static int in_getmulti(struct ifnet *, const struct in_addr *,
147
struct in_multi **);
148
static int inm_get_source(struct in_multi *inm, const in_addr_t haddr,
149
const int noalloc, struct ip_msource **pims);
150
#ifdef KTR
151
static int inm_is_ifp_detached(const struct in_multi *);
152
#endif
153
static int inm_merge(struct in_multi *, /*const*/ struct in_mfilter *);
154
static void inm_purge(struct in_multi *);
155
static void inm_reap(struct in_multi *);
156
static void inm_release(struct in_multi *);
157
static struct ip_moptions *
158
inp_findmoptions(struct inpcb *);
159
static int inp_get_source_filters(struct inpcb *, struct sockopt *);
160
static int inp_join_group(struct inpcb *, struct sockopt *);
161
static int inp_leave_group(struct inpcb *, struct sockopt *);
162
static struct ifnet *
163
inp_lookup_mcast_ifp(const struct inpcb *,
164
const struct sockaddr_in *, const struct in_addr);
165
static int inp_block_unblock_source(struct inpcb *, struct sockopt *);
166
static int inp_set_multicast_if(struct inpcb *, struct sockopt *);
167
static int inp_set_source_filters(struct inpcb *, struct sockopt *);
168
static int sysctl_ip_mcast_filters(SYSCTL_HANDLER_ARGS);
169
170
static SYSCTL_NODE(_net_inet_ip, OID_AUTO, mcast,
171
CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
172
"IPv4 multicast");
173
174
static u_long in_mcast_maxgrpsrc = IP_MAX_GROUP_SRC_FILTER;
175
SYSCTL_ULONG(_net_inet_ip_mcast, OID_AUTO, maxgrpsrc,
176
CTLFLAG_RWTUN, &in_mcast_maxgrpsrc, 0,
177
"Max source filters per group");
178
179
static u_long in_mcast_maxsocksrc = IP_MAX_SOCK_SRC_FILTER;
180
SYSCTL_ULONG(_net_inet_ip_mcast, OID_AUTO, maxsocksrc,
181
CTLFLAG_RWTUN, &in_mcast_maxsocksrc, 0,
182
"Max source filters per socket");
183
184
int in_mcast_loop = IP_DEFAULT_MULTICAST_LOOP;
185
SYSCTL_INT(_net_inet_ip_mcast, OID_AUTO, loop, CTLFLAG_RWTUN,
186
&in_mcast_loop, 0, "Loopback multicast datagrams by default");
187
188
static SYSCTL_NODE(_net_inet_ip_mcast, OID_AUTO, filters,
189
CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_ip_mcast_filters,
190
"Per-interface stack-wide source filters");
191
192
#ifdef KTR
193
/*
194
* Inline function which wraps assertions for a valid ifp.
195
* The ifnet layer will set the ifma's ifp pointer to NULL if the ifp
196
* is detached.
197
*/
198
static int __inline
199
inm_is_ifp_detached(const struct in_multi *inm)
200
{
201
struct ifnet *ifp;
202
203
KASSERT(inm->inm_ifma != NULL, ("%s: no ifma", __func__));
204
ifp = inm->inm_ifma->ifma_ifp;
205
if (ifp != NULL) {
206
/*
207
* Sanity check that netinet's notion of ifp is the
208
* same as net's.
209
*/
210
KASSERT(inm->inm_ifp == ifp, ("%s: bad ifp", __func__));
211
}
212
213
return (ifp == NULL);
214
}
215
#endif
216
217
/*
218
* Interface detach can happen in a taskqueue thread context, so we must use a
219
* dedicated thread to avoid deadlocks when draining inm_release tasks.
220
*/
221
TASKQUEUE_DEFINE_THREAD(inm_free);
222
static struct in_multi_head inm_free_list = SLIST_HEAD_INITIALIZER();
223
static void inm_release_task(void *arg __unused, int pending __unused);
224
static struct task inm_free_task = TASK_INITIALIZER(0, inm_release_task, NULL);
225
226
void
227
inm_release_wait(void *arg __unused)
228
{
229
230
/*
231
* Make sure all pending multicast addresses are freed before
232
* the VNET or network device is destroyed:
233
*/
234
taskqueue_drain(taskqueue_inm_free, &inm_free_task);
235
}
236
#ifdef VIMAGE
237
/* XXX-BZ FIXME, see D24914. */
238
VNET_SYSUNINIT(inm_release_wait, SI_SUB_PROTO_DOMAIN, SI_ORDER_FIRST, inm_release_wait, NULL);
239
#endif
240
241
void
242
inm_release_list_deferred(struct in_multi_head *inmh)
243
{
244
245
if (SLIST_EMPTY(inmh))
246
return;
247
mtx_lock(&in_multi_free_mtx);
248
SLIST_CONCAT(&inm_free_list, inmh, in_multi, inm_nrele);
249
mtx_unlock(&in_multi_free_mtx);
250
taskqueue_enqueue(taskqueue_inm_free, &inm_free_task);
251
}
252
253
void
254
inm_disconnect(struct in_multi *inm)
255
{
256
struct ifnet *ifp;
257
struct ifmultiaddr *ifma, *ll_ifma;
258
259
ifp = inm->inm_ifp;
260
IF_ADDR_WLOCK_ASSERT(ifp);
261
ifma = inm->inm_ifma;
262
263
if_ref(ifp);
264
if (ifma->ifma_flags & IFMA_F_ENQUEUED) {
265
CK_STAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifmultiaddr, ifma_link);
266
ifma->ifma_flags &= ~IFMA_F_ENQUEUED;
267
}
268
MCDPRINTF("removed ifma: %p from %s\n", ifma, ifp->if_xname);
269
if ((ll_ifma = ifma->ifma_llifma) != NULL) {
270
MPASS(ifma != ll_ifma);
271
ifma->ifma_llifma = NULL;
272
MPASS(ll_ifma->ifma_llifma == NULL);
273
MPASS(ll_ifma->ifma_ifp == ifp);
274
if (--ll_ifma->ifma_refcount == 0) {
275
if (ll_ifma->ifma_flags & IFMA_F_ENQUEUED) {
276
CK_STAILQ_REMOVE(&ifp->if_multiaddrs, ll_ifma, ifmultiaddr, ifma_link);
277
ll_ifma->ifma_flags &= ~IFMA_F_ENQUEUED;
278
}
279
MCDPRINTF("removed ll_ifma: %p from %s\n", ll_ifma, ifp->if_xname);
280
if_freemulti(ll_ifma);
281
}
282
}
283
}
284
285
void
286
inm_release_deferred(struct in_multi *inm)
287
{
288
struct in_multi_head tmp;
289
290
IN_MULTI_LIST_LOCK_ASSERT();
291
MPASS(inm->inm_refcount > 0);
292
if (--inm->inm_refcount == 0) {
293
SLIST_INIT(&tmp);
294
inm_disconnect(inm);
295
inm->inm_ifma->ifma_protospec = NULL;
296
SLIST_INSERT_HEAD(&tmp, inm, inm_nrele);
297
inm_release_list_deferred(&tmp);
298
}
299
}
300
301
static void
302
inm_release_task(void *arg __unused, int pending __unused)
303
{
304
struct in_multi_head inm_free_tmp;
305
struct in_multi *inm, *tinm;
306
307
SLIST_INIT(&inm_free_tmp);
308
mtx_lock(&in_multi_free_mtx);
309
SLIST_CONCAT(&inm_free_tmp, &inm_free_list, in_multi, inm_nrele);
310
mtx_unlock(&in_multi_free_mtx);
311
IN_MULTI_LOCK();
312
SLIST_FOREACH_SAFE(inm, &inm_free_tmp, inm_nrele, tinm) {
313
SLIST_REMOVE_HEAD(&inm_free_tmp, inm_nrele);
314
MPASS(inm);
315
inm_release(inm);
316
}
317
IN_MULTI_UNLOCK();
318
}
319
320
/*
321
* Initialize an in_mfilter structure to a known state at t0, t1
322
* with an empty source filter list.
323
*/
324
static __inline void
325
imf_init(struct in_mfilter *imf, const int st0, const int st1)
326
{
327
memset(imf, 0, sizeof(struct in_mfilter));
328
RB_INIT(&imf->imf_sources);
329
imf->imf_st[0] = st0;
330
imf->imf_st[1] = st1;
331
}
332
333
struct in_mfilter *
334
ip_mfilter_alloc(const int mflags, const int st0, const int st1)
335
{
336
struct in_mfilter *imf;
337
338
imf = malloc(sizeof(*imf), M_INMFILTER, mflags);
339
if (imf != NULL)
340
imf_init(imf, st0, st1);
341
342
return (imf);
343
}
344
345
void
346
ip_mfilter_free(struct in_mfilter *imf)
347
{
348
349
imf_purge(imf);
350
free(imf, M_INMFILTER);
351
}
352
353
/*
354
* Function for looking up an in_multi record for an IPv4 multicast address
355
* on a given interface. ifp must be valid. If no record found, return NULL.
356
* The IN_MULTI_LIST_LOCK and IF_ADDR_LOCK on ifp must be held.
357
*/
358
struct in_multi *
359
inm_lookup_locked(struct ifnet *ifp, const struct in_addr ina)
360
{
361
struct ifmultiaddr *ifma;
362
struct in_multi *inm;
363
364
IN_MULTI_LIST_LOCK_ASSERT();
365
IF_ADDR_LOCK_ASSERT(ifp);
366
367
CK_STAILQ_FOREACH(ifma, &((ifp)->if_multiaddrs), ifma_link) {
368
inm = inm_ifmultiaddr_get_inm(ifma);
369
if (inm == NULL)
370
continue;
371
if (inm->inm_addr.s_addr == ina.s_addr)
372
return (inm);
373
}
374
return (NULL);
375
}
376
377
/*
378
* Wrapper for inm_lookup_locked().
379
* The IF_ADDR_LOCK will be taken on ifp and released on return.
380
*/
381
struct in_multi *
382
inm_lookup(struct ifnet *ifp, const struct in_addr ina)
383
{
384
struct epoch_tracker et;
385
struct in_multi *inm;
386
387
IN_MULTI_LIST_LOCK_ASSERT();
388
NET_EPOCH_ENTER(et);
389
390
inm = inm_lookup_locked(ifp, ina);
391
NET_EPOCH_EXIT(et);
392
393
return (inm);
394
}
395
396
/*
397
* Find an IPv4 multicast group entry for this ip_moptions instance
398
* which matches the specified group, and optionally an interface.
399
* Return its index into the array, or -1 if not found.
400
*/
401
static struct in_mfilter *
402
imo_match_group(const struct ip_moptions *imo, const struct ifnet *ifp,
403
const struct sockaddr *group)
404
{
405
const struct sockaddr_in *gsin;
406
struct in_mfilter *imf;
407
struct in_multi *inm;
408
409
gsin = (const struct sockaddr_in *)group;
410
411
IP_MFILTER_FOREACH(imf, &imo->imo_head) {
412
inm = imf->imf_inm;
413
if (inm == NULL)
414
continue;
415
if ((ifp == NULL || (inm->inm_ifp == ifp)) &&
416
in_hosteq(inm->inm_addr, gsin->sin_addr)) {
417
break;
418
}
419
}
420
return (imf);
421
}
422
423
/*
424
* Find an IPv4 multicast source entry for this imo which matches
425
* the given group index for this socket, and source address.
426
*
427
* NOTE: This does not check if the entry is in-mode, merely if
428
* it exists, which may not be the desired behaviour.
429
*/
430
static struct in_msource *
431
imo_match_source(struct in_mfilter *imf, const struct sockaddr *src)
432
{
433
struct ip_msource find;
434
struct ip_msource *ims;
435
const sockunion_t *psa;
436
437
KASSERT(src->sa_family == AF_INET, ("%s: !AF_INET", __func__));
438
439
/* Source trees are keyed in host byte order. */
440
psa = (const sockunion_t *)src;
441
find.ims_haddr = ntohl(psa->sin.sin_addr.s_addr);
442
ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find);
443
444
return ((struct in_msource *)ims);
445
}
446
447
/*
448
* Perform filtering for multicast datagrams on a socket by group and source.
449
*
450
* Returns 0 if a datagram should be allowed through, or various error codes
451
* if the socket was not a member of the group, or the source was muted, etc.
452
*/
453
int
454
imo_multi_filter(const struct ip_moptions *imo, const struct ifnet *ifp,
455
const struct sockaddr *group, const struct sockaddr *src)
456
{
457
struct in_mfilter *imf;
458
struct in_msource *ims;
459
int mode;
460
461
KASSERT(ifp != NULL, ("%s: null ifp", __func__));
462
463
imf = imo_match_group(imo, ifp, group);
464
if (imf == NULL)
465
return (MCAST_NOTGMEMBER);
466
467
/*
468
* Check if the source was included in an (S,G) join.
469
* Allow reception on exclusive memberships by default,
470
* reject reception on inclusive memberships by default.
471
* Exclude source only if an in-mode exclude filter exists.
472
* Include source only if an in-mode include filter exists.
473
* NOTE: We are comparing group state here at IGMP t1 (now)
474
* with socket-layer t0 (since last downcall).
475
*/
476
mode = imf->imf_st[1];
477
ims = imo_match_source(imf, src);
478
479
if ((ims == NULL && mode == MCAST_INCLUDE) ||
480
(ims != NULL && ims->imsl_st[0] == MCAST_EXCLUDE))
481
return (MCAST_NOTSMEMBER);
482
483
return (MCAST_PASS);
484
}
485
486
/*
487
* Find and return a reference to an in_multi record for (ifp, group),
488
* and bump its reference count.
489
* If one does not exist, try to allocate it, and update link-layer multicast
490
* filters on ifp to listen for group.
491
* Assumes the IN_MULTI lock is held across the call.
492
* Return 0 if successful, otherwise return an appropriate error code.
493
*/
494
static int
495
in_getmulti(struct ifnet *ifp, const struct in_addr *group,
496
struct in_multi **pinm)
497
{
498
struct sockaddr_in gsin;
499
struct ifmultiaddr *ifma;
500
struct in_ifinfo *ii;
501
struct in_multi *inm;
502
int error;
503
504
IN_MULTI_LOCK_ASSERT();
505
506
ii = (struct in_ifinfo *)ifp->if_afdata[AF_INET];
507
IN_MULTI_LIST_LOCK();
508
inm = inm_lookup(ifp, *group);
509
if (inm != NULL) {
510
/*
511
* If we already joined this group, just bump the
512
* refcount and return it.
513
*/
514
KASSERT(inm->inm_refcount >= 1,
515
("%s: bad refcount %d", __func__, inm->inm_refcount));
516
inm_acquire_locked(inm);
517
*pinm = inm;
518
}
519
IN_MULTI_LIST_UNLOCK();
520
if (inm != NULL)
521
return (0);
522
523
memset(&gsin, 0, sizeof(gsin));
524
gsin.sin_family = AF_INET;
525
gsin.sin_len = sizeof(struct sockaddr_in);
526
gsin.sin_addr = *group;
527
528
/*
529
* Check if a link-layer group is already associated
530
* with this network-layer group on the given ifnet.
531
*/
532
error = if_addmulti(ifp, (struct sockaddr *)&gsin, &ifma);
533
if (error != 0)
534
return (error);
535
536
/* XXX ifma_protospec must be covered by IF_ADDR_LOCK */
537
IN_MULTI_LIST_LOCK();
538
IF_ADDR_WLOCK(ifp);
539
540
/*
541
* If something other than netinet is occupying the link-layer
542
* group, print a meaningful error message and back out of
543
* the allocation.
544
* Otherwise, bump the refcount on the existing network-layer
545
* group association and return it.
546
*/
547
if (ifma->ifma_protospec != NULL) {
548
inm = (struct in_multi *)ifma->ifma_protospec;
549
#ifdef INVARIANTS
550
KASSERT(ifma->ifma_addr != NULL, ("%s: no ifma_addr",
551
__func__));
552
KASSERT(ifma->ifma_addr->sa_family == AF_INET,
553
("%s: ifma not AF_INET", __func__));
554
KASSERT(inm != NULL, ("%s: no ifma_protospec", __func__));
555
if (inm->inm_ifma != ifma || inm->inm_ifp != ifp ||
556
!in_hosteq(inm->inm_addr, *group)) {
557
char addrbuf[INET_ADDRSTRLEN];
558
559
panic("%s: ifma %p is inconsistent with %p (%s)",
560
__func__, ifma, inm, inet_ntoa_r(*group, addrbuf));
561
}
562
#endif
563
inm_acquire_locked(inm);
564
*pinm = inm;
565
goto out_locked;
566
}
567
568
IF_ADDR_WLOCK_ASSERT(ifp);
569
570
/*
571
* A new in_multi record is needed; allocate and initialize it.
572
* We DO NOT perform an IGMP join as the in_ layer may need to
573
* push an initial source list down to IGMP to support SSM.
574
*
575
* The initial source filter state is INCLUDE, {} as per the RFC.
576
*/
577
inm = malloc(sizeof(*inm), M_IPMADDR, M_NOWAIT | M_ZERO);
578
if (inm == NULL) {
579
IF_ADDR_WUNLOCK(ifp);
580
IN_MULTI_LIST_UNLOCK();
581
if_delmulti_ifma(ifma);
582
return (ENOMEM);
583
}
584
inm->inm_addr = *group;
585
inm->inm_ifp = ifp;
586
inm->inm_igi = ii->ii_igmp;
587
inm->inm_ifma = ifma;
588
inm->inm_refcount = 1;
589
inm->inm_state = IGMP_NOT_MEMBER;
590
mbufq_init(&inm->inm_scq, IGMP_MAX_STATE_CHANGES);
591
inm->inm_st[0].iss_fmode = MCAST_UNDEFINED;
592
inm->inm_st[1].iss_fmode = MCAST_UNDEFINED;
593
RB_INIT(&inm->inm_srcs);
594
595
ifma->ifma_protospec = inm;
596
597
*pinm = inm;
598
out_locked:
599
IF_ADDR_WUNLOCK(ifp);
600
IN_MULTI_LIST_UNLOCK();
601
return (0);
602
}
603
604
/*
605
* Drop a reference to an in_multi record.
606
*
607
* If the refcount drops to 0, free the in_multi record and
608
* delete the underlying link-layer membership.
609
*/
610
static void
611
inm_release(struct in_multi *inm)
612
{
613
struct ifmultiaddr *ifma;
614
struct ifnet *ifp;
615
616
CTR2(KTR_IGMPV3, "%s: refcount is %d", __func__, inm->inm_refcount);
617
MPASS(inm->inm_refcount == 0);
618
CTR2(KTR_IGMPV3, "%s: freeing inm %p", __func__, inm);
619
620
ifma = inm->inm_ifma;
621
ifp = inm->inm_ifp;
622
623
/* XXX this access is not covered by IF_ADDR_LOCK */
624
CTR2(KTR_IGMPV3, "%s: purging ifma %p", __func__, ifma);
625
if (ifp != NULL) {
626
CURVNET_SET(ifp->if_vnet);
627
inm_purge(inm);
628
free(inm, M_IPMADDR);
629
if_delmulti_ifma_flags(ifma, 1);
630
CURVNET_RESTORE();
631
if_rele(ifp);
632
} else {
633
inm_purge(inm);
634
free(inm, M_IPMADDR);
635
if_delmulti_ifma_flags(ifma, 1);
636
}
637
}
638
639
/*
640
* Clear recorded source entries for a group.
641
* Used by the IGMP code. Caller must hold the IN_MULTI lock.
642
* FIXME: Should reap.
643
*/
644
void
645
inm_clear_recorded(struct in_multi *inm)
646
{
647
struct ip_msource *ims;
648
649
IN_MULTI_LIST_LOCK_ASSERT();
650
651
RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) {
652
if (ims->ims_stp) {
653
ims->ims_stp = 0;
654
--inm->inm_st[1].iss_rec;
655
}
656
}
657
KASSERT(inm->inm_st[1].iss_rec == 0,
658
("%s: iss_rec %d not 0", __func__, inm->inm_st[1].iss_rec));
659
}
660
661
/*
662
* Record a source as pending for a Source-Group IGMPv3 query.
663
* This lives here as it modifies the shared tree.
664
*
665
* inm is the group descriptor.
666
* naddr is the address of the source to record in network-byte order.
667
*
668
* If the net.inet.igmp.sgalloc sysctl is non-zero, we will
669
* lazy-allocate a source node in response to an SG query.
670
* Otherwise, no allocation is performed. This saves some memory
671
* with the trade-off that the source will not be reported to the
672
* router if joined in the window between the query response and
673
* the group actually being joined on the local host.
674
*
675
* VIMAGE: XXX: Currently the igmp_sgalloc feature has been removed.
676
* This turns off the allocation of a recorded source entry if
677
* the group has not been joined.
678
*
679
* Return 0 if the source didn't exist or was already marked as recorded.
680
* Return 1 if the source was marked as recorded by this function.
681
* Return <0 if any error occurred (negated errno code).
682
*/
683
int
684
inm_record_source(struct in_multi *inm, const in_addr_t naddr)
685
{
686
struct ip_msource find;
687
struct ip_msource *ims, *nims;
688
689
IN_MULTI_LIST_LOCK_ASSERT();
690
691
find.ims_haddr = ntohl(naddr);
692
ims = RB_FIND(ip_msource_tree, &inm->inm_srcs, &find);
693
if (ims && ims->ims_stp)
694
return (0);
695
if (ims == NULL) {
696
if (inm->inm_nsrc == in_mcast_maxgrpsrc)
697
return (-ENOSPC);
698
nims = malloc(sizeof(struct ip_msource), M_IPMSOURCE,
699
M_NOWAIT | M_ZERO);
700
if (nims == NULL)
701
return (-ENOMEM);
702
nims->ims_haddr = find.ims_haddr;
703
RB_INSERT(ip_msource_tree, &inm->inm_srcs, nims);
704
++inm->inm_nsrc;
705
ims = nims;
706
}
707
708
/*
709
* Mark the source as recorded and update the recorded
710
* source count.
711
*/
712
++ims->ims_stp;
713
++inm->inm_st[1].iss_rec;
714
715
return (1);
716
}
717
718
/*
719
* Return a pointer to an in_msource owned by an in_mfilter,
720
* given its source address.
721
* Lazy-allocate if needed. If this is a new entry its filter state is
722
* undefined at t0.
723
*
724
* imf is the filter set being modified.
725
* haddr is the source address in *host* byte-order.
726
*
727
* SMPng: May be called with locks held; malloc must not block.
728
*/
729
static int
730
imf_get_source(struct in_mfilter *imf, const struct sockaddr_in *psin,
731
struct in_msource **plims)
732
{
733
struct ip_msource find;
734
struct ip_msource *ims, *nims;
735
struct in_msource *lims;
736
int error;
737
738
error = 0;
739
ims = NULL;
740
lims = NULL;
741
742
/* key is host byte order */
743
find.ims_haddr = ntohl(psin->sin_addr.s_addr);
744
ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find);
745
lims = (struct in_msource *)ims;
746
if (lims == NULL) {
747
if (imf->imf_nsrc == in_mcast_maxsocksrc)
748
return (ENOSPC);
749
nims = malloc(sizeof(struct in_msource), M_INMFILTER,
750
M_NOWAIT | M_ZERO);
751
if (nims == NULL)
752
return (ENOMEM);
753
lims = (struct in_msource *)nims;
754
lims->ims_haddr = find.ims_haddr;
755
lims->imsl_st[0] = MCAST_UNDEFINED;
756
RB_INSERT(ip_msource_tree, &imf->imf_sources, nims);
757
++imf->imf_nsrc;
758
}
759
760
*plims = lims;
761
762
return (error);
763
}
764
765
/*
766
* Graft a source entry into an existing socket-layer filter set,
767
* maintaining any required invariants and checking allocations.
768
*
769
* The source is marked as being in the new filter mode at t1.
770
*
771
* Return the pointer to the new node, otherwise return NULL.
772
*/
773
static struct in_msource *
774
imf_graft(struct in_mfilter *imf, const uint8_t st1,
775
const struct sockaddr_in *psin)
776
{
777
struct ip_msource *nims;
778
struct in_msource *lims;
779
780
nims = malloc(sizeof(struct in_msource), M_INMFILTER,
781
M_NOWAIT | M_ZERO);
782
if (nims == NULL)
783
return (NULL);
784
lims = (struct in_msource *)nims;
785
lims->ims_haddr = ntohl(psin->sin_addr.s_addr);
786
lims->imsl_st[0] = MCAST_UNDEFINED;
787
lims->imsl_st[1] = st1;
788
RB_INSERT(ip_msource_tree, &imf->imf_sources, nims);
789
++imf->imf_nsrc;
790
791
return (lims);
792
}
793
794
/*
795
* Prune a source entry from an existing socket-layer filter set,
796
* maintaining any required invariants and checking allocations.
797
*
798
* The source is marked as being left at t1, it is not freed.
799
*
800
* Return 0 if no error occurred, otherwise return an errno value.
801
*/
802
static int
803
imf_prune(struct in_mfilter *imf, const struct sockaddr_in *psin)
804
{
805
struct ip_msource find;
806
struct ip_msource *ims;
807
struct in_msource *lims;
808
809
/* key is host byte order */
810
find.ims_haddr = ntohl(psin->sin_addr.s_addr);
811
ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find);
812
if (ims == NULL)
813
return (ENOENT);
814
lims = (struct in_msource *)ims;
815
lims->imsl_st[1] = MCAST_UNDEFINED;
816
return (0);
817
}
818
819
/*
820
* Revert socket-layer filter set deltas at t1 to t0 state.
821
*/
822
static void
823
imf_rollback(struct in_mfilter *imf)
824
{
825
struct ip_msource *ims, *tims;
826
struct in_msource *lims;
827
828
RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) {
829
lims = (struct in_msource *)ims;
830
if (lims->imsl_st[0] == lims->imsl_st[1]) {
831
/* no change at t1 */
832
continue;
833
} else if (lims->imsl_st[0] != MCAST_UNDEFINED) {
834
/* revert change to existing source at t1 */
835
lims->imsl_st[1] = lims->imsl_st[0];
836
} else {
837
/* revert source added t1 */
838
CTR2(KTR_IGMPV3, "%s: free ims %p", __func__, ims);
839
RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims);
840
free(ims, M_INMFILTER);
841
imf->imf_nsrc--;
842
}
843
}
844
imf->imf_st[1] = imf->imf_st[0];
845
}
846
847
/*
848
* Mark socket-layer filter set as INCLUDE {} at t1.
849
*/
850
static void
851
imf_leave(struct in_mfilter *imf)
852
{
853
struct ip_msource *ims;
854
struct in_msource *lims;
855
856
RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) {
857
lims = (struct in_msource *)ims;
858
lims->imsl_st[1] = MCAST_UNDEFINED;
859
}
860
imf->imf_st[1] = MCAST_INCLUDE;
861
}
862
863
/*
864
* Mark socket-layer filter set deltas as committed.
865
*/
866
static void
867
imf_commit(struct in_mfilter *imf)
868
{
869
struct ip_msource *ims;
870
struct in_msource *lims;
871
872
RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) {
873
lims = (struct in_msource *)ims;
874
lims->imsl_st[0] = lims->imsl_st[1];
875
}
876
imf->imf_st[0] = imf->imf_st[1];
877
}
878
879
/*
880
* Reap unreferenced sources from socket-layer filter set.
881
*/
882
static void
883
imf_reap(struct in_mfilter *imf)
884
{
885
struct ip_msource *ims, *tims;
886
struct in_msource *lims;
887
888
RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) {
889
lims = (struct in_msource *)ims;
890
if ((lims->imsl_st[0] == MCAST_UNDEFINED) &&
891
(lims->imsl_st[1] == MCAST_UNDEFINED)) {
892
CTR2(KTR_IGMPV3, "%s: free lims %p", __func__, ims);
893
RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims);
894
free(ims, M_INMFILTER);
895
imf->imf_nsrc--;
896
}
897
}
898
}
899
900
/*
901
* Purge socket-layer filter set.
902
*/
903
static void
904
imf_purge(struct in_mfilter *imf)
905
{
906
struct ip_msource *ims, *tims;
907
908
RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) {
909
CTR2(KTR_IGMPV3, "%s: free ims %p", __func__, ims);
910
RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims);
911
free(ims, M_INMFILTER);
912
imf->imf_nsrc--;
913
}
914
imf->imf_st[0] = imf->imf_st[1] = MCAST_UNDEFINED;
915
KASSERT(RB_EMPTY(&imf->imf_sources),
916
("%s: imf_sources not empty", __func__));
917
}
918
919
/*
920
* Look up a source filter entry for a multicast group.
921
*
922
* inm is the group descriptor to work with.
923
* haddr is the host-byte-order IPv4 address to look up.
924
* noalloc may be non-zero to suppress allocation of sources.
925
* *pims will be set to the address of the retrieved or allocated source.
926
*
927
* SMPng: NOTE: may be called with locks held.
928
* Return 0 if successful, otherwise return a non-zero error code.
929
*/
930
static int
931
inm_get_source(struct in_multi *inm, const in_addr_t haddr,
932
const int noalloc, struct ip_msource **pims)
933
{
934
struct ip_msource find;
935
struct ip_msource *ims, *nims;
936
937
find.ims_haddr = haddr;
938
ims = RB_FIND(ip_msource_tree, &inm->inm_srcs, &find);
939
if (ims == NULL && !noalloc) {
940
if (inm->inm_nsrc == in_mcast_maxgrpsrc)
941
return (ENOSPC);
942
nims = malloc(sizeof(struct ip_msource), M_IPMSOURCE,
943
M_NOWAIT | M_ZERO);
944
if (nims == NULL)
945
return (ENOMEM);
946
nims->ims_haddr = haddr;
947
RB_INSERT(ip_msource_tree, &inm->inm_srcs, nims);
948
++inm->inm_nsrc;
949
ims = nims;
950
#ifdef KTR
951
CTR3(KTR_IGMPV3, "%s: allocated 0x%08x as %p", __func__,
952
haddr, ims);
953
#endif
954
}
955
956
*pims = ims;
957
return (0);
958
}
959
960
/*
961
* Merge socket-layer source into IGMP-layer source.
962
* If rollback is non-zero, perform the inverse of the merge.
963
*/
964
static void
965
ims_merge(struct ip_msource *ims, const struct in_msource *lims,
966
const int rollback)
967
{
968
int n = rollback ? -1 : 1;
969
970
if (lims->imsl_st[0] == MCAST_EXCLUDE) {
971
CTR3(KTR_IGMPV3, "%s: t1 ex -= %d on 0x%08x",
972
__func__, n, ims->ims_haddr);
973
ims->ims_st[1].ex -= n;
974
} else if (lims->imsl_st[0] == MCAST_INCLUDE) {
975
CTR3(KTR_IGMPV3, "%s: t1 in -= %d on 0x%08x",
976
__func__, n, ims->ims_haddr);
977
ims->ims_st[1].in -= n;
978
}
979
980
if (lims->imsl_st[1] == MCAST_EXCLUDE) {
981
CTR3(KTR_IGMPV3, "%s: t1 ex += %d on 0x%08x",
982
__func__, n, ims->ims_haddr);
983
ims->ims_st[1].ex += n;
984
} else if (lims->imsl_st[1] == MCAST_INCLUDE) {
985
CTR3(KTR_IGMPV3, "%s: t1 in += %d on 0x%08x",
986
__func__, n, ims->ims_haddr);
987
ims->ims_st[1].in += n;
988
}
989
}
990
991
/*
992
* Atomically update the global in_multi state, when a membership's
993
* filter list is being updated in any way.
994
*
995
* imf is the per-inpcb-membership group filter pointer.
996
* A fake imf may be passed for in-kernel consumers.
997
*
998
* XXX This is a candidate for a set-symmetric-difference style loop
999
* which would eliminate the repeated lookup from root of ims nodes,
1000
* as they share the same key space.
1001
*
1002
* If any error occurred this function will back out of refcounts
1003
* and return a non-zero value.
1004
*/
1005
static int
1006
inm_merge(struct in_multi *inm, /*const*/ struct in_mfilter *imf)
1007
{
1008
struct ip_msource *ims, *nims;
1009
struct in_msource *lims;
1010
int schanged, error;
1011
int nsrc0, nsrc1;
1012
1013
schanged = 0;
1014
error = 0;
1015
nsrc1 = nsrc0 = 0;
1016
IN_MULTI_LIST_LOCK_ASSERT();
1017
1018
/*
1019
* Update the source filters first, as this may fail.
1020
* Maintain count of in-mode filters at t0, t1. These are
1021
* used to work out if we transition into ASM mode or not.
1022
* Maintain a count of source filters whose state was
1023
* actually modified by this operation.
1024
*/
1025
RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) {
1026
lims = (struct in_msource *)ims;
1027
if (lims->imsl_st[0] == imf->imf_st[0]) nsrc0++;
1028
if (lims->imsl_st[1] == imf->imf_st[1]) nsrc1++;
1029
if (lims->imsl_st[0] == lims->imsl_st[1]) continue;
1030
error = inm_get_source(inm, lims->ims_haddr, 0, &nims);
1031
++schanged;
1032
if (error)
1033
break;
1034
ims_merge(nims, lims, 0);
1035
}
1036
if (error) {
1037
struct ip_msource *bims;
1038
1039
RB_FOREACH_REVERSE_FROM(ims, ip_msource_tree, nims) {
1040
lims = (struct in_msource *)ims;
1041
if (lims->imsl_st[0] == lims->imsl_st[1])
1042
continue;
1043
(void)inm_get_source(inm, lims->ims_haddr, 1, &bims);
1044
if (bims == NULL)
1045
continue;
1046
ims_merge(bims, lims, 1);
1047
}
1048
goto out_reap;
1049
}
1050
1051
CTR3(KTR_IGMPV3, "%s: imf filters in-mode: %d at t0, %d at t1",
1052
__func__, nsrc0, nsrc1);
1053
1054
/* Handle transition between INCLUDE {n} and INCLUDE {} on socket. */
1055
if (imf->imf_st[0] == imf->imf_st[1] &&
1056
imf->imf_st[1] == MCAST_INCLUDE) {
1057
if (nsrc1 == 0) {
1058
CTR1(KTR_IGMPV3, "%s: --in on inm at t1", __func__);
1059
--inm->inm_st[1].iss_in;
1060
}
1061
}
1062
1063
/* Handle filter mode transition on socket. */
1064
if (imf->imf_st[0] != imf->imf_st[1]) {
1065
CTR3(KTR_IGMPV3, "%s: imf transition %d to %d",
1066
__func__, imf->imf_st[0], imf->imf_st[1]);
1067
1068
if (imf->imf_st[0] == MCAST_EXCLUDE) {
1069
CTR1(KTR_IGMPV3, "%s: --ex on inm at t1", __func__);
1070
--inm->inm_st[1].iss_ex;
1071
} else if (imf->imf_st[0] == MCAST_INCLUDE) {
1072
CTR1(KTR_IGMPV3, "%s: --in on inm at t1", __func__);
1073
--inm->inm_st[1].iss_in;
1074
}
1075
1076
if (imf->imf_st[1] == MCAST_EXCLUDE) {
1077
CTR1(KTR_IGMPV3, "%s: ex++ on inm at t1", __func__);
1078
inm->inm_st[1].iss_ex++;
1079
} else if (imf->imf_st[1] == MCAST_INCLUDE && nsrc1 > 0) {
1080
CTR1(KTR_IGMPV3, "%s: in++ on inm at t1", __func__);
1081
inm->inm_st[1].iss_in++;
1082
}
1083
}
1084
1085
/*
1086
* Track inm filter state in terms of listener counts.
1087
* If there are any exclusive listeners, stack-wide
1088
* membership is exclusive.
1089
* Otherwise, if only inclusive listeners, stack-wide is inclusive.
1090
* If no listeners remain, state is undefined at t1,
1091
* and the IGMP lifecycle for this group should finish.
1092
*/
1093
if (inm->inm_st[1].iss_ex > 0) {
1094
CTR1(KTR_IGMPV3, "%s: transition to EX", __func__);
1095
inm->inm_st[1].iss_fmode = MCAST_EXCLUDE;
1096
} else if (inm->inm_st[1].iss_in > 0) {
1097
CTR1(KTR_IGMPV3, "%s: transition to IN", __func__);
1098
inm->inm_st[1].iss_fmode = MCAST_INCLUDE;
1099
} else {
1100
CTR1(KTR_IGMPV3, "%s: transition to UNDEF", __func__);
1101
inm->inm_st[1].iss_fmode = MCAST_UNDEFINED;
1102
}
1103
1104
/* Decrement ASM listener count on transition out of ASM mode. */
1105
if (imf->imf_st[0] == MCAST_EXCLUDE && nsrc0 == 0) {
1106
if ((imf->imf_st[1] != MCAST_EXCLUDE) ||
1107
(imf->imf_st[1] == MCAST_EXCLUDE && nsrc1 > 0)) {
1108
CTR1(KTR_IGMPV3, "%s: --asm on inm at t1", __func__);
1109
--inm->inm_st[1].iss_asm;
1110
}
1111
}
1112
1113
/* Increment ASM listener count on transition to ASM mode. */
1114
if (imf->imf_st[1] == MCAST_EXCLUDE && nsrc1 == 0) {
1115
CTR1(KTR_IGMPV3, "%s: asm++ on inm at t1", __func__);
1116
inm->inm_st[1].iss_asm++;
1117
}
1118
1119
CTR3(KTR_IGMPV3, "%s: merged imf %p to inm %p", __func__, imf, inm);
1120
inm_print(inm);
1121
1122
out_reap:
1123
if (schanged > 0) {
1124
CTR1(KTR_IGMPV3, "%s: sources changed; reaping", __func__);
1125
inm_reap(inm);
1126
}
1127
return (error);
1128
}
1129
1130
/*
1131
* Mark an in_multi's filter set deltas as committed.
1132
* Called by IGMP after a state change has been enqueued.
1133
*/
1134
void
1135
inm_commit(struct in_multi *inm)
1136
{
1137
struct ip_msource *ims;
1138
1139
CTR2(KTR_IGMPV3, "%s: commit inm %p", __func__, inm);
1140
CTR1(KTR_IGMPV3, "%s: pre commit:", __func__);
1141
inm_print(inm);
1142
1143
RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) {
1144
ims->ims_st[0] = ims->ims_st[1];
1145
}
1146
inm->inm_st[0] = inm->inm_st[1];
1147
}
1148
1149
/*
1150
* Reap unreferenced nodes from an in_multi's filter set.
1151
*/
1152
static void
1153
inm_reap(struct in_multi *inm)
1154
{
1155
struct ip_msource *ims, *tims;
1156
1157
RB_FOREACH_SAFE(ims, ip_msource_tree, &inm->inm_srcs, tims) {
1158
if (ims->ims_st[0].ex > 0 || ims->ims_st[0].in > 0 ||
1159
ims->ims_st[1].ex > 0 || ims->ims_st[1].in > 0 ||
1160
ims->ims_stp != 0)
1161
continue;
1162
CTR2(KTR_IGMPV3, "%s: free ims %p", __func__, ims);
1163
RB_REMOVE(ip_msource_tree, &inm->inm_srcs, ims);
1164
free(ims, M_IPMSOURCE);
1165
inm->inm_nsrc--;
1166
}
1167
}
1168
1169
/*
1170
* Purge all source nodes from an in_multi's filter set.
1171
*/
1172
static void
1173
inm_purge(struct in_multi *inm)
1174
{
1175
struct ip_msource *ims, *tims;
1176
1177
RB_FOREACH_SAFE(ims, ip_msource_tree, &inm->inm_srcs, tims) {
1178
CTR2(KTR_IGMPV3, "%s: free ims %p", __func__, ims);
1179
RB_REMOVE(ip_msource_tree, &inm->inm_srcs, ims);
1180
free(ims, M_IPMSOURCE);
1181
inm->inm_nsrc--;
1182
}
1183
mbufq_drain(&inm->inm_scq);
1184
}
1185
1186
/*
1187
* Join a multicast group; unlocked entry point.
1188
*
1189
* SMPng: XXX: in_joingroup() is called from in_control(). Fortunately,
1190
* ifp is unlikely to have been detached at this point, so we assume
1191
* it's OK to recurse.
1192
*/
1193
int
1194
in_joingroup(struct ifnet *ifp, const struct in_addr *gina,
1195
/*const*/ struct in_mfilter *imf, struct in_multi **pinm)
1196
{
1197
int error;
1198
1199
IN_MULTI_LOCK();
1200
error = in_joingroup_locked(ifp, gina, imf, pinm);
1201
IN_MULTI_UNLOCK();
1202
1203
return (error);
1204
}
1205
1206
/*
1207
* Join a multicast group; real entry point.
1208
*
1209
* Only preserves atomicity at inm level.
1210
* NOTE: imf argument cannot be const due to sys/tree.h limitations.
1211
*
1212
* If the IGMP downcall fails, the group is not joined, and an error
1213
* code is returned.
1214
*/
1215
int
1216
in_joingroup_locked(struct ifnet *ifp, const struct in_addr *gina,
1217
/*const*/ struct in_mfilter *imf, struct in_multi **pinm)
1218
{
1219
struct in_mfilter timf;
1220
struct in_multi *inm;
1221
int error;
1222
1223
IN_MULTI_LOCK_ASSERT();
1224
IN_MULTI_LIST_UNLOCK_ASSERT();
1225
1226
CTR4(KTR_IGMPV3, "%s: join 0x%08x on %p(%s))", __func__,
1227
ntohl(gina->s_addr), ifp, ifp->if_xname);
1228
1229
error = 0;
1230
inm = NULL;
1231
1232
/*
1233
* If no imf was specified (i.e. kernel consumer),
1234
* fake one up and assume it is an ASM join.
1235
*/
1236
if (imf == NULL) {
1237
imf_init(&timf, MCAST_UNDEFINED, MCAST_EXCLUDE);
1238
imf = &timf;
1239
}
1240
1241
error = in_getmulti(ifp, gina, &inm);
1242
if (error) {
1243
CTR1(KTR_IGMPV3, "%s: in_getmulti() failure", __func__);
1244
return (error);
1245
}
1246
IN_MULTI_LIST_LOCK();
1247
CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
1248
error = inm_merge(inm, imf);
1249
if (error) {
1250
CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__);
1251
goto out_inm_release;
1252
}
1253
1254
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
1255
error = igmp_change_state(inm);
1256
if (error) {
1257
CTR1(KTR_IGMPV3, "%s: failed to update source", __func__);
1258
goto out_inm_release;
1259
}
1260
1261
out_inm_release:
1262
if (error) {
1263
CTR2(KTR_IGMPV3, "%s: dropping ref on %p", __func__, inm);
1264
IF_ADDR_WLOCK(ifp);
1265
inm_release_deferred(inm);
1266
IF_ADDR_WUNLOCK(ifp);
1267
} else {
1268
*pinm = inm;
1269
}
1270
IN_MULTI_LIST_UNLOCK();
1271
1272
return (error);
1273
}
1274
1275
/*
1276
* Leave a multicast group; unlocked entry point.
1277
*/
1278
int
1279
in_leavegroup(struct in_multi *inm, /*const*/ struct in_mfilter *imf)
1280
{
1281
int error;
1282
1283
IN_MULTI_LOCK();
1284
error = in_leavegroup_locked(inm, imf);
1285
IN_MULTI_UNLOCK();
1286
1287
return (error);
1288
}
1289
1290
/*
1291
* Leave a multicast group; real entry point.
1292
* All source filters will be expunged.
1293
*
1294
* Only preserves atomicity at inm level.
1295
*
1296
* Holding the write lock for the INP which contains imf
1297
* is highly advisable. We can't assert for it as imf does not
1298
* contain a back-pointer to the owning inp.
1299
*
1300
* Note: This is not the same as inm_release(*) as this function also
1301
* makes a state change downcall into IGMP.
1302
*/
1303
int
1304
in_leavegroup_locked(struct in_multi *inm, /*const*/ struct in_mfilter *imf)
1305
{
1306
struct in_mfilter timf;
1307
int error;
1308
1309
IN_MULTI_LOCK_ASSERT();
1310
IN_MULTI_LIST_UNLOCK_ASSERT();
1311
1312
error = 0;
1313
1314
CTR5(KTR_IGMPV3, "%s: leave inm %p, 0x%08x/%s, imf %p", __func__,
1315
inm, ntohl(inm->inm_addr.s_addr),
1316
(inm_is_ifp_detached(inm) ? "null" : inm->inm_ifp->if_xname),
1317
imf);
1318
1319
/*
1320
* If no imf was specified (i.e. kernel consumer),
1321
* fake one up and assume it is an ASM join.
1322
*/
1323
if (imf == NULL) {
1324
imf_init(&timf, MCAST_EXCLUDE, MCAST_UNDEFINED);
1325
imf = &timf;
1326
}
1327
1328
/*
1329
* Begin state merge transaction at IGMP layer.
1330
*
1331
* As this particular invocation should not cause any memory
1332
* to be allocated, and there is no opportunity to roll back
1333
* the transaction, it MUST NOT fail.
1334
*/
1335
CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
1336
IN_MULTI_LIST_LOCK();
1337
error = inm_merge(inm, imf);
1338
KASSERT(error == 0, ("%s: failed to merge inm state", __func__));
1339
1340
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
1341
CURVNET_SET(inm->inm_ifp->if_vnet);
1342
error = igmp_change_state(inm);
1343
IF_ADDR_WLOCK(inm->inm_ifp);
1344
inm_release_deferred(inm);
1345
IF_ADDR_WUNLOCK(inm->inm_ifp);
1346
IN_MULTI_LIST_UNLOCK();
1347
CURVNET_RESTORE();
1348
if (error)
1349
CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__);
1350
1351
CTR2(KTR_IGMPV3, "%s: dropping ref on %p", __func__, inm);
1352
1353
return (error);
1354
}
1355
1356
/*#ifndef BURN_BRIDGES*/
1357
1358
/*
1359
* Block or unblock an ASM multicast source on an inpcb.
1360
* This implements the delta-based API described in RFC 3678.
1361
*
1362
* The delta-based API applies only to exclusive-mode memberships.
1363
* An IGMP downcall will be performed.
1364
*
1365
* Return 0 if successful, otherwise return an appropriate error code.
1366
*/
1367
static int
1368
inp_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
1369
{
1370
struct epoch_tracker et;
1371
struct group_source_req gsr;
1372
sockunion_t *gsa, *ssa;
1373
struct ifnet *ifp;
1374
struct in_mfilter *imf;
1375
struct ip_moptions *imo;
1376
struct in_msource *ims;
1377
struct in_multi *inm;
1378
uint16_t fmode;
1379
int error, doblock;
1380
1381
ifp = NULL;
1382
error = 0;
1383
doblock = 0;
1384
1385
memset(&gsr, 0, sizeof(struct group_source_req));
1386
gsa = (sockunion_t *)&gsr.gsr_group;
1387
ssa = (sockunion_t *)&gsr.gsr_source;
1388
1389
switch (sopt->sopt_name) {
1390
case IP_BLOCK_SOURCE:
1391
case IP_UNBLOCK_SOURCE: {
1392
struct ip_mreq_source mreqs;
1393
1394
error = sooptcopyin(sopt, &mreqs,
1395
sizeof(struct ip_mreq_source),
1396
sizeof(struct ip_mreq_source));
1397
if (error)
1398
return (error);
1399
1400
gsa->sin.sin_family = AF_INET;
1401
gsa->sin.sin_len = sizeof(struct sockaddr_in);
1402
gsa->sin.sin_addr = mreqs.imr_multiaddr;
1403
1404
ssa->sin.sin_family = AF_INET;
1405
ssa->sin.sin_len = sizeof(struct sockaddr_in);
1406
ssa->sin.sin_addr = mreqs.imr_sourceaddr;
1407
1408
if (!in_nullhost(mreqs.imr_interface)) {
1409
NET_EPOCH_ENTER(et);
1410
INADDR_TO_IFP(mreqs.imr_interface, ifp);
1411
/* XXXGL: ifref? */
1412
NET_EPOCH_EXIT(et);
1413
}
1414
if (sopt->sopt_name == IP_BLOCK_SOURCE)
1415
doblock = 1;
1416
1417
CTR3(KTR_IGMPV3, "%s: imr_interface = 0x%08x, ifp = %p",
1418
__func__, ntohl(mreqs.imr_interface.s_addr), ifp);
1419
break;
1420
}
1421
1422
case MCAST_BLOCK_SOURCE:
1423
case MCAST_UNBLOCK_SOURCE:
1424
error = sooptcopyin(sopt, &gsr,
1425
sizeof(struct group_source_req),
1426
sizeof(struct group_source_req));
1427
if (error)
1428
return (error);
1429
1430
if (gsa->sin.sin_family != AF_INET ||
1431
gsa->sin.sin_len != sizeof(struct sockaddr_in))
1432
return (EINVAL);
1433
1434
if (ssa->sin.sin_family != AF_INET ||
1435
ssa->sin.sin_len != sizeof(struct sockaddr_in))
1436
return (EINVAL);
1437
1438
NET_EPOCH_ENTER(et);
1439
ifp = ifnet_byindex(gsr.gsr_interface);
1440
NET_EPOCH_EXIT(et);
1441
if (ifp == NULL)
1442
return (EADDRNOTAVAIL);
1443
1444
if (sopt->sopt_name == MCAST_BLOCK_SOURCE)
1445
doblock = 1;
1446
break;
1447
1448
default:
1449
CTR2(KTR_IGMPV3, "%s: unknown sopt_name %d",
1450
__func__, sopt->sopt_name);
1451
return (EOPNOTSUPP);
1452
break;
1453
}
1454
1455
if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
1456
return (EINVAL);
1457
1458
IN_MULTI_LOCK();
1459
1460
/*
1461
* Check if we are actually a member of this group.
1462
*/
1463
imo = inp_findmoptions(inp);
1464
imf = imo_match_group(imo, ifp, &gsa->sa);
1465
if (imf == NULL) {
1466
error = EADDRNOTAVAIL;
1467
goto out_inp_locked;
1468
}
1469
inm = imf->imf_inm;
1470
1471
/*
1472
* Attempting to use the delta-based API on an
1473
* non exclusive-mode membership is an error.
1474
*/
1475
fmode = imf->imf_st[0];
1476
if (fmode != MCAST_EXCLUDE) {
1477
error = EINVAL;
1478
goto out_inp_locked;
1479
}
1480
1481
/*
1482
* Deal with error cases up-front:
1483
* Asked to block, but already blocked; or
1484
* Asked to unblock, but nothing to unblock.
1485
* If adding a new block entry, allocate it.
1486
*/
1487
ims = imo_match_source(imf, &ssa->sa);
1488
if ((ims != NULL && doblock) || (ims == NULL && !doblock)) {
1489
CTR3(KTR_IGMPV3, "%s: source 0x%08x %spresent", __func__,
1490
ntohl(ssa->sin.sin_addr.s_addr), doblock ? "" : "not ");
1491
error = EADDRNOTAVAIL;
1492
goto out_inp_locked;
1493
}
1494
1495
INP_WLOCK_ASSERT(inp);
1496
1497
/*
1498
* Begin state merge transaction at socket layer.
1499
*/
1500
if (doblock) {
1501
CTR2(KTR_IGMPV3, "%s: %s source", __func__, "block");
1502
ims = imf_graft(imf, fmode, &ssa->sin);
1503
if (ims == NULL)
1504
error = ENOMEM;
1505
} else {
1506
CTR2(KTR_IGMPV3, "%s: %s source", __func__, "allow");
1507
error = imf_prune(imf, &ssa->sin);
1508
}
1509
1510
if (error) {
1511
CTR1(KTR_IGMPV3, "%s: merge imf state failed", __func__);
1512
goto out_imf_rollback;
1513
}
1514
1515
/*
1516
* Begin state merge transaction at IGMP layer.
1517
*/
1518
CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
1519
IN_MULTI_LIST_LOCK();
1520
error = inm_merge(inm, imf);
1521
if (error) {
1522
CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__);
1523
IN_MULTI_LIST_UNLOCK();
1524
goto out_imf_rollback;
1525
}
1526
1527
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
1528
error = igmp_change_state(inm);
1529
IN_MULTI_LIST_UNLOCK();
1530
if (error)
1531
CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__);
1532
1533
out_imf_rollback:
1534
if (error)
1535
imf_rollback(imf);
1536
else
1537
imf_commit(imf);
1538
1539
imf_reap(imf);
1540
1541
out_inp_locked:
1542
INP_WUNLOCK(inp);
1543
IN_MULTI_UNLOCK();
1544
return (error);
1545
}
1546
1547
/*
1548
* Given an inpcb, return its multicast options structure pointer. Accepts
1549
* an unlocked inpcb pointer, but will return it locked. May sleep.
1550
*
1551
* SMPng: NOTE: Returns with the INP write lock held.
1552
*/
1553
static struct ip_moptions *
1554
inp_findmoptions(struct inpcb *inp)
1555
{
1556
struct ip_moptions *imo;
1557
1558
INP_WLOCK(inp);
1559
if (inp->inp_moptions != NULL)
1560
return (inp->inp_moptions);
1561
1562
INP_WUNLOCK(inp);
1563
1564
imo = malloc(sizeof(*imo), M_IPMOPTS, M_WAITOK);
1565
1566
imo->imo_multicast_ifp = NULL;
1567
imo->imo_multicast_addr.s_addr = INADDR_ANY;
1568
imo->imo_multicast_vif = -1;
1569
imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
1570
imo->imo_multicast_loop = in_mcast_loop;
1571
STAILQ_INIT(&imo->imo_head);
1572
1573
INP_WLOCK(inp);
1574
if (inp->inp_moptions != NULL) {
1575
free(imo, M_IPMOPTS);
1576
return (inp->inp_moptions);
1577
}
1578
inp->inp_moptions = imo;
1579
return (imo);
1580
}
1581
1582
void
1583
inp_freemoptions(struct ip_moptions *imo)
1584
{
1585
struct in_mfilter *imf;
1586
struct in_multi *inm;
1587
struct ifnet *ifp;
1588
1589
if (imo == NULL)
1590
return;
1591
1592
while ((imf = ip_mfilter_first(&imo->imo_head)) != NULL) {
1593
ip_mfilter_remove(&imo->imo_head, imf);
1594
1595
imf_leave(imf);
1596
if ((inm = imf->imf_inm) != NULL) {
1597
if ((ifp = inm->inm_ifp) != NULL) {
1598
CURVNET_SET(ifp->if_vnet);
1599
(void)in_leavegroup(inm, imf);
1600
CURVNET_RESTORE();
1601
} else {
1602
(void)in_leavegroup(inm, imf);
1603
}
1604
}
1605
ip_mfilter_free(imf);
1606
}
1607
free(imo, M_IPMOPTS);
1608
}
1609
1610
/*
1611
* Atomically get source filters on a socket for an IPv4 multicast group.
1612
* Called with INP lock held; returns with lock released.
1613
*/
1614
static int
1615
inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
1616
{
1617
struct epoch_tracker et;
1618
struct __msfilterreq msfr;
1619
sockunion_t *gsa;
1620
struct ifnet *ifp;
1621
struct ip_moptions *imo;
1622
struct in_mfilter *imf;
1623
struct ip_msource *ims;
1624
struct in_msource *lims;
1625
struct sockaddr_in *psin;
1626
struct sockaddr_storage *ptss;
1627
struct sockaddr_storage *tss;
1628
int error;
1629
size_t nsrcs, ncsrcs;
1630
1631
INP_WLOCK_ASSERT(inp);
1632
1633
imo = inp->inp_moptions;
1634
KASSERT(imo != NULL, ("%s: null ip_moptions", __func__));
1635
1636
INP_WUNLOCK(inp);
1637
1638
error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq),
1639
sizeof(struct __msfilterreq));
1640
if (error)
1641
return (error);
1642
1643
NET_EPOCH_ENTER(et);
1644
ifp = ifnet_byindex(msfr.msfr_ifindex);
1645
NET_EPOCH_EXIT(et); /* XXXGL: unsafe ifnet pointer left */
1646
if (ifp == NULL)
1647
return (EINVAL);
1648
1649
INP_WLOCK(inp);
1650
1651
/*
1652
* Lookup group on the socket.
1653
*/
1654
gsa = (sockunion_t *)&msfr.msfr_group;
1655
imf = imo_match_group(imo, ifp, &gsa->sa);
1656
if (imf == NULL) {
1657
INP_WUNLOCK(inp);
1658
return (EADDRNOTAVAIL);
1659
}
1660
1661
/*
1662
* Ignore memberships which are in limbo.
1663
*/
1664
if (imf->imf_st[1] == MCAST_UNDEFINED) {
1665
INP_WUNLOCK(inp);
1666
return (EAGAIN);
1667
}
1668
msfr.msfr_fmode = imf->imf_st[1];
1669
1670
/*
1671
* If the user specified a buffer, copy out the source filter
1672
* entries to userland gracefully.
1673
* We only copy out the number of entries which userland
1674
* has asked for, but we always tell userland how big the
1675
* buffer really needs to be.
1676
*/
1677
if (msfr.msfr_nsrcs > in_mcast_maxsocksrc)
1678
msfr.msfr_nsrcs = in_mcast_maxsocksrc;
1679
tss = NULL;
1680
if (msfr.msfr_srcs != NULL && msfr.msfr_nsrcs > 0) {
1681
tss = malloc(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs,
1682
M_TEMP, M_NOWAIT | M_ZERO);
1683
if (tss == NULL) {
1684
INP_WUNLOCK(inp);
1685
return (ENOBUFS);
1686
}
1687
}
1688
1689
/*
1690
* Count number of sources in-mode at t0.
1691
* If buffer space exists and remains, copy out source entries.
1692
*/
1693
nsrcs = msfr.msfr_nsrcs;
1694
ncsrcs = 0;
1695
ptss = tss;
1696
RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) {
1697
lims = (struct in_msource *)ims;
1698
if (lims->imsl_st[0] == MCAST_UNDEFINED ||
1699
lims->imsl_st[0] != imf->imf_st[0])
1700
continue;
1701
++ncsrcs;
1702
if (tss != NULL && nsrcs > 0) {
1703
psin = (struct sockaddr_in *)ptss;
1704
psin->sin_family = AF_INET;
1705
psin->sin_len = sizeof(struct sockaddr_in);
1706
psin->sin_addr.s_addr = htonl(lims->ims_haddr);
1707
psin->sin_port = 0;
1708
++ptss;
1709
--nsrcs;
1710
}
1711
}
1712
1713
INP_WUNLOCK(inp);
1714
1715
if (tss != NULL) {
1716
error = copyout(tss, msfr.msfr_srcs,
1717
sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs);
1718
free(tss, M_TEMP);
1719
if (error)
1720
return (error);
1721
}
1722
1723
msfr.msfr_nsrcs = ncsrcs;
1724
error = sooptcopyout(sopt, &msfr, sizeof(struct __msfilterreq));
1725
1726
return (error);
1727
}
1728
1729
/*
1730
* Return the IP multicast options in response to user getsockopt().
1731
*/
1732
int
1733
inp_getmoptions(struct inpcb *inp, struct sockopt *sopt)
1734
{
1735
struct ip_mreqn mreqn;
1736
struct ip_moptions *imo;
1737
struct ifnet *ifp;
1738
struct in_ifaddr *ia;
1739
int error, optval;
1740
u_char coptval;
1741
1742
INP_WLOCK(inp);
1743
imo = inp->inp_moptions;
1744
/* If socket is neither of type SOCK_RAW or SOCK_DGRAM reject it. */
1745
if (inp->inp_socket->so_proto->pr_type != SOCK_RAW &&
1746
inp->inp_socket->so_proto->pr_type != SOCK_DGRAM) {
1747
INP_WUNLOCK(inp);
1748
return (EOPNOTSUPP);
1749
}
1750
1751
error = 0;
1752
switch (sopt->sopt_name) {
1753
case IP_MULTICAST_VIF:
1754
if (imo != NULL)
1755
optval = imo->imo_multicast_vif;
1756
else
1757
optval = -1;
1758
INP_WUNLOCK(inp);
1759
error = sooptcopyout(sopt, &optval, sizeof(int));
1760
break;
1761
1762
case IP_MULTICAST_IF:
1763
memset(&mreqn, 0, sizeof(struct ip_mreqn));
1764
if (imo != NULL) {
1765
ifp = imo->imo_multicast_ifp;
1766
if (!in_nullhost(imo->imo_multicast_addr)) {
1767
mreqn.imr_address = imo->imo_multicast_addr;
1768
} else if (ifp != NULL) {
1769
struct epoch_tracker et;
1770
1771
mreqn.imr_ifindex = ifp->if_index;
1772
NET_EPOCH_ENTER(et);
1773
IFP_TO_IA(ifp, ia);
1774
if (ia != NULL)
1775
mreqn.imr_address =
1776
IA_SIN(ia)->sin_addr;
1777
NET_EPOCH_EXIT(et);
1778
}
1779
}
1780
INP_WUNLOCK(inp);
1781
if (sopt->sopt_valsize == sizeof(struct ip_mreqn)) {
1782
error = sooptcopyout(sopt, &mreqn,
1783
sizeof(struct ip_mreqn));
1784
} else {
1785
error = sooptcopyout(sopt, &mreqn.imr_address,
1786
sizeof(struct in_addr));
1787
}
1788
break;
1789
1790
case IP_MULTICAST_TTL:
1791
if (imo == NULL)
1792
optval = coptval = IP_DEFAULT_MULTICAST_TTL;
1793
else
1794
optval = coptval = imo->imo_multicast_ttl;
1795
INP_WUNLOCK(inp);
1796
if (sopt->sopt_valsize == sizeof(u_char))
1797
error = sooptcopyout(sopt, &coptval, sizeof(u_char));
1798
else
1799
error = sooptcopyout(sopt, &optval, sizeof(int));
1800
break;
1801
1802
case IP_MULTICAST_LOOP:
1803
if (imo == NULL)
1804
optval = coptval = IP_DEFAULT_MULTICAST_LOOP;
1805
else
1806
optval = coptval = imo->imo_multicast_loop;
1807
INP_WUNLOCK(inp);
1808
if (sopt->sopt_valsize == sizeof(u_char))
1809
error = sooptcopyout(sopt, &coptval, sizeof(u_char));
1810
else
1811
error = sooptcopyout(sopt, &optval, sizeof(int));
1812
break;
1813
1814
case IP_MSFILTER:
1815
if (imo == NULL) {
1816
error = EADDRNOTAVAIL;
1817
INP_WUNLOCK(inp);
1818
} else {
1819
error = inp_get_source_filters(inp, sopt);
1820
}
1821
break;
1822
1823
default:
1824
INP_WUNLOCK(inp);
1825
error = ENOPROTOOPT;
1826
break;
1827
}
1828
1829
INP_UNLOCK_ASSERT(inp);
1830
1831
return (error);
1832
}
1833
1834
/*
1835
* Look up the ifnet to use for a multicast group membership,
1836
* given the IPv4 address of an interface, and the IPv4 group address.
1837
*
1838
* This routine exists to support legacy multicast applications
1839
* which do not understand that multicast memberships are scoped to
1840
* specific physical links in the networking stack, or which need
1841
* to join link-scope groups before IPv4 addresses are configured.
1842
*
1843
* Use this socket's current FIB number for any required FIB lookup.
1844
* If ina is INADDR_ANY, look up the group address in the unicast FIB,
1845
* and use its ifp; usually, this points to the default next-hop.
1846
*
1847
* If the FIB lookup fails, attempt to use the first non-loopback
1848
* interface with multicast capability in the system as a
1849
* last resort. The legacy IPv4 ASM API requires that we do
1850
* this in order to allow groups to be joined when the routing
1851
* table has not yet been populated during boot.
1852
*
1853
* Returns NULL if no ifp could be found, otherwise return referenced ifp.
1854
*
1855
* FUTURE: Implement IPv4 source-address selection.
1856
*/
1857
static struct ifnet *
1858
inp_lookup_mcast_ifp(const struct inpcb *inp,
1859
const struct sockaddr_in *gsin, const struct in_addr ina)
1860
{
1861
struct ifnet *ifp;
1862
struct nhop_object *nh;
1863
1864
NET_EPOCH_ASSERT();
1865
KASSERT(inp != NULL, ("%s: inp must not be NULL", __func__));
1866
KASSERT(gsin->sin_family == AF_INET, ("%s: not AF_INET", __func__));
1867
KASSERT(IN_MULTICAST(ntohl(gsin->sin_addr.s_addr)),
1868
("%s: not multicast", __func__));
1869
1870
ifp = NULL;
1871
if (!in_nullhost(ina)) {
1872
INADDR_TO_IFP(ina, ifp);
1873
if (ifp != NULL)
1874
if_ref(ifp);
1875
} else {
1876
nh = fib4_lookup(inp->inp_inc.inc_fibnum, gsin->sin_addr, 0, NHR_NONE, 0);
1877
if (nh != NULL) {
1878
ifp = nh->nh_ifp;
1879
if_ref(ifp);
1880
} else {
1881
struct in_ifaddr *ia;
1882
struct ifnet *mifp;
1883
1884
mifp = NULL;
1885
CK_STAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
1886
mifp = ia->ia_ifp;
1887
if (!(mifp->if_flags & IFF_LOOPBACK) &&
1888
(mifp->if_flags & IFF_MULTICAST)) {
1889
ifp = mifp;
1890
if_ref(ifp);
1891
break;
1892
}
1893
}
1894
}
1895
}
1896
1897
return (ifp);
1898
}
1899
1900
/*
1901
* Join an IPv4 multicast group, possibly with a source.
1902
*/
1903
static int
1904
inp_join_group(struct inpcb *inp, struct sockopt *sopt)
1905
{
1906
struct group_source_req gsr;
1907
sockunion_t *gsa, *ssa;
1908
struct ifnet *ifp;
1909
struct in_mfilter *imf;
1910
struct ip_moptions *imo;
1911
struct in_multi *inm;
1912
struct in_msource *lims;
1913
struct epoch_tracker et;
1914
int error, is_new;
1915
1916
ifp = NULL;
1917
lims = NULL;
1918
error = 0;
1919
1920
memset(&gsr, 0, sizeof(struct group_source_req));
1921
gsa = (sockunion_t *)&gsr.gsr_group;
1922
gsa->ss.ss_family = AF_UNSPEC;
1923
ssa = (sockunion_t *)&gsr.gsr_source;
1924
ssa->ss.ss_family = AF_UNSPEC;
1925
1926
switch (sopt->sopt_name) {
1927
case IP_ADD_MEMBERSHIP: {
1928
struct ip_mreqn mreqn;
1929
1930
if (sopt->sopt_valsize == sizeof(struct ip_mreqn))
1931
error = sooptcopyin(sopt, &mreqn,
1932
sizeof(struct ip_mreqn), sizeof(struct ip_mreqn));
1933
else
1934
error = sooptcopyin(sopt, &mreqn,
1935
sizeof(struct ip_mreq), sizeof(struct ip_mreq));
1936
if (error)
1937
return (error);
1938
1939
gsa->sin.sin_family = AF_INET;
1940
gsa->sin.sin_len = sizeof(struct sockaddr_in);
1941
gsa->sin.sin_addr = mreqn.imr_multiaddr;
1942
if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
1943
return (EINVAL);
1944
1945
NET_EPOCH_ENTER(et);
1946
if (sopt->sopt_valsize == sizeof(struct ip_mreqn) &&
1947
mreqn.imr_ifindex != 0)
1948
ifp = ifnet_byindex_ref(mreqn.imr_ifindex);
1949
else
1950
ifp = inp_lookup_mcast_ifp(inp, &gsa->sin,
1951
mreqn.imr_address);
1952
NET_EPOCH_EXIT(et);
1953
break;
1954
}
1955
case IP_ADD_SOURCE_MEMBERSHIP: {
1956
struct ip_mreq_source mreqs;
1957
1958
error = sooptcopyin(sopt, &mreqs, sizeof(struct ip_mreq_source),
1959
sizeof(struct ip_mreq_source));
1960
if (error)
1961
return (error);
1962
1963
gsa->sin.sin_family = ssa->sin.sin_family = AF_INET;
1964
gsa->sin.sin_len = ssa->sin.sin_len =
1965
sizeof(struct sockaddr_in);
1966
1967
gsa->sin.sin_addr = mreqs.imr_multiaddr;
1968
if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
1969
return (EINVAL);
1970
1971
ssa->sin.sin_addr = mreqs.imr_sourceaddr;
1972
1973
NET_EPOCH_ENTER(et);
1974
ifp = inp_lookup_mcast_ifp(inp, &gsa->sin,
1975
mreqs.imr_interface);
1976
NET_EPOCH_EXIT(et);
1977
CTR3(KTR_IGMPV3, "%s: imr_interface = 0x%08x, ifp = %p",
1978
__func__, ntohl(mreqs.imr_interface.s_addr), ifp);
1979
break;
1980
}
1981
1982
case MCAST_JOIN_GROUP:
1983
case MCAST_JOIN_SOURCE_GROUP:
1984
if (sopt->sopt_name == MCAST_JOIN_GROUP) {
1985
error = sooptcopyin(sopt, &gsr,
1986
sizeof(struct group_req),
1987
sizeof(struct group_req));
1988
} else if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) {
1989
error = sooptcopyin(sopt, &gsr,
1990
sizeof(struct group_source_req),
1991
sizeof(struct group_source_req));
1992
}
1993
if (error)
1994
return (error);
1995
1996
if (gsa->sin.sin_family != AF_INET ||
1997
gsa->sin.sin_len != sizeof(struct sockaddr_in))
1998
return (EINVAL);
1999
2000
/*
2001
* Overwrite the port field if present, as the sockaddr
2002
* being copied in may be matched with a binary comparison.
2003
*/
2004
gsa->sin.sin_port = 0;
2005
if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) {
2006
if (ssa->sin.sin_family != AF_INET ||
2007
ssa->sin.sin_len != sizeof(struct sockaddr_in))
2008
return (EINVAL);
2009
ssa->sin.sin_port = 0;
2010
}
2011
2012
if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
2013
return (EINVAL);
2014
2015
NET_EPOCH_ENTER(et);
2016
ifp = ifnet_byindex_ref(gsr.gsr_interface);
2017
NET_EPOCH_EXIT(et);
2018
if (ifp == NULL)
2019
return (EADDRNOTAVAIL);
2020
break;
2021
2022
default:
2023
CTR2(KTR_IGMPV3, "%s: unknown sopt_name %d",
2024
__func__, sopt->sopt_name);
2025
return (EOPNOTSUPP);
2026
break;
2027
}
2028
2029
if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
2030
if (ifp != NULL)
2031
if_rele(ifp);
2032
return (EADDRNOTAVAIL);
2033
}
2034
2035
IN_MULTI_LOCK();
2036
2037
/*
2038
* Find the membership in the membership list.
2039
*/
2040
imo = inp_findmoptions(inp);
2041
imf = imo_match_group(imo, ifp, &gsa->sa);
2042
if (imf == NULL) {
2043
is_new = 1;
2044
inm = NULL;
2045
2046
if (ip_mfilter_count(&imo->imo_head) >= IP_MAX_MEMBERSHIPS) {
2047
error = ENOMEM;
2048
goto out_inp_locked;
2049
}
2050
} else {
2051
is_new = 0;
2052
inm = imf->imf_inm;
2053
2054
if (ssa->ss.ss_family != AF_UNSPEC) {
2055
/*
2056
* MCAST_JOIN_SOURCE_GROUP on an exclusive membership
2057
* is an error. On an existing inclusive membership,
2058
* it just adds the source to the filter list.
2059
*/
2060
if (imf->imf_st[1] != MCAST_INCLUDE) {
2061
error = EINVAL;
2062
goto out_inp_locked;
2063
}
2064
/*
2065
* Throw out duplicates.
2066
*
2067
* XXX FIXME: This makes a naive assumption that
2068
* even if entries exist for *ssa in this imf,
2069
* they will be rejected as dupes, even if they
2070
* are not valid in the current mode (in-mode).
2071
*
2072
* in_msource is transactioned just as for anything
2073
* else in SSM -- but note naive use of inm_graft()
2074
* below for allocating new filter entries.
2075
*
2076
* This is only an issue if someone mixes the
2077
* full-state SSM API with the delta-based API,
2078
* which is discouraged in the relevant RFCs.
2079
*/
2080
lims = imo_match_source(imf, &ssa->sa);
2081
if (lims != NULL /*&&
2082
lims->imsl_st[1] == MCAST_INCLUDE*/) {
2083
error = EADDRNOTAVAIL;
2084
goto out_inp_locked;
2085
}
2086
} else {
2087
/*
2088
* MCAST_JOIN_GROUP on an existing exclusive
2089
* membership is an error; return EADDRINUSE
2090
* to preserve 4.4BSD API idempotence, and
2091
* avoid tedious detour to code below.
2092
* NOTE: This is bending RFC 3678 a bit.
2093
*
2094
* On an existing inclusive membership, this is also
2095
* an error; if you want to change filter mode,
2096
* you must use the userland API setsourcefilter().
2097
* XXX We don't reject this for imf in UNDEFINED
2098
* state at t1, because allocation of a filter
2099
* is atomic with allocation of a membership.
2100
*/
2101
error = EINVAL;
2102
if (imf->imf_st[1] == MCAST_EXCLUDE)
2103
error = EADDRINUSE;
2104
goto out_inp_locked;
2105
}
2106
}
2107
2108
/*
2109
* Begin state merge transaction at socket layer.
2110
*/
2111
INP_WLOCK_ASSERT(inp);
2112
2113
/*
2114
* Graft new source into filter list for this inpcb's
2115
* membership of the group. The in_multi may not have
2116
* been allocated yet if this is a new membership, however,
2117
* the in_mfilter slot will be allocated and must be initialized.
2118
*
2119
* Note: Grafting of exclusive mode filters doesn't happen
2120
* in this path.
2121
* XXX: Should check for non-NULL lims (node exists but may
2122
* not be in-mode) for interop with full-state API.
2123
*/
2124
if (ssa->ss.ss_family != AF_UNSPEC) {
2125
/* Membership starts in IN mode */
2126
if (is_new) {
2127
CTR1(KTR_IGMPV3, "%s: new join w/source", __func__);
2128
imf = ip_mfilter_alloc(M_NOWAIT, MCAST_UNDEFINED, MCAST_INCLUDE);
2129
if (imf == NULL) {
2130
error = ENOMEM;
2131
goto out_inp_locked;
2132
}
2133
} else {
2134
CTR2(KTR_IGMPV3, "%s: %s source", __func__, "allow");
2135
}
2136
lims = imf_graft(imf, MCAST_INCLUDE, &ssa->sin);
2137
if (lims == NULL) {
2138
CTR1(KTR_IGMPV3, "%s: merge imf state failed",
2139
__func__);
2140
error = ENOMEM;
2141
goto out_inp_locked;
2142
}
2143
} else {
2144
/* No address specified; Membership starts in EX mode */
2145
if (is_new) {
2146
CTR1(KTR_IGMPV3, "%s: new join w/o source", __func__);
2147
imf = ip_mfilter_alloc(M_NOWAIT, MCAST_UNDEFINED, MCAST_EXCLUDE);
2148
if (imf == NULL) {
2149
error = ENOMEM;
2150
goto out_inp_locked;
2151
}
2152
}
2153
}
2154
2155
/*
2156
* Begin state merge transaction at IGMP layer.
2157
*/
2158
if (is_new) {
2159
in_pcbref(inp);
2160
INP_WUNLOCK(inp);
2161
2162
error = in_joingroup_locked(ifp, &gsa->sin.sin_addr, imf,
2163
&imf->imf_inm);
2164
2165
INP_WLOCK(inp);
2166
if (in_pcbrele_wlocked(inp)) {
2167
error = ENXIO;
2168
goto out_inp_unlocked;
2169
}
2170
if (error) {
2171
CTR1(KTR_IGMPV3, "%s: in_joingroup_locked failed",
2172
__func__);
2173
goto out_inp_locked;
2174
}
2175
/*
2176
* NOTE: Refcount from in_joingroup_locked()
2177
* is protecting membership.
2178
*/
2179
ip_mfilter_insert(&imo->imo_head, imf);
2180
} else {
2181
CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
2182
IN_MULTI_LIST_LOCK();
2183
error = inm_merge(inm, imf);
2184
if (error) {
2185
CTR1(KTR_IGMPV3, "%s: failed to merge inm state",
2186
__func__);
2187
IN_MULTI_LIST_UNLOCK();
2188
imf_rollback(imf);
2189
imf_reap(imf);
2190
goto out_inp_locked;
2191
}
2192
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
2193
error = igmp_change_state(inm);
2194
IN_MULTI_LIST_UNLOCK();
2195
if (error) {
2196
CTR1(KTR_IGMPV3, "%s: failed igmp downcall",
2197
__func__);
2198
imf_rollback(imf);
2199
imf_reap(imf);
2200
goto out_inp_locked;
2201
}
2202
}
2203
2204
imf_commit(imf);
2205
imf = NULL;
2206
2207
out_inp_locked:
2208
INP_WUNLOCK(inp);
2209
out_inp_unlocked:
2210
IN_MULTI_UNLOCK();
2211
2212
if (is_new && imf) {
2213
if (imf->imf_inm != NULL) {
2214
IN_MULTI_LIST_LOCK();
2215
IF_ADDR_WLOCK(ifp);
2216
inm_release_deferred(imf->imf_inm);
2217
IF_ADDR_WUNLOCK(ifp);
2218
IN_MULTI_LIST_UNLOCK();
2219
}
2220
ip_mfilter_free(imf);
2221
}
2222
if_rele(ifp);
2223
return (error);
2224
}
2225
2226
/*
2227
* Leave an IPv4 multicast group on an inpcb, possibly with a source.
2228
*/
2229
static int
2230
inp_leave_group(struct inpcb *inp, struct sockopt *sopt)
2231
{
2232
struct epoch_tracker et;
2233
struct group_source_req gsr;
2234
struct ip_mreq_source mreqs;
2235
sockunion_t *gsa, *ssa;
2236
struct ifnet *ifp;
2237
struct in_mfilter *imf;
2238
struct ip_moptions *imo;
2239
struct in_msource *ims;
2240
struct in_multi *inm;
2241
int error;
2242
bool is_final;
2243
2244
ifp = NULL;
2245
error = 0;
2246
is_final = true;
2247
2248
memset(&gsr, 0, sizeof(struct group_source_req));
2249
gsa = (sockunion_t *)&gsr.gsr_group;
2250
gsa->ss.ss_family = AF_UNSPEC;
2251
ssa = (sockunion_t *)&gsr.gsr_source;
2252
ssa->ss.ss_family = AF_UNSPEC;
2253
2254
switch (sopt->sopt_name) {
2255
case IP_DROP_MEMBERSHIP:
2256
case IP_DROP_SOURCE_MEMBERSHIP:
2257
if (sopt->sopt_name == IP_DROP_MEMBERSHIP) {
2258
error = sooptcopyin(sopt, &mreqs,
2259
sizeof(struct ip_mreq),
2260
sizeof(struct ip_mreq));
2261
/*
2262
* Swap interface and sourceaddr arguments,
2263
* as ip_mreq and ip_mreq_source are laid
2264
* out differently.
2265
*/
2266
mreqs.imr_interface = mreqs.imr_sourceaddr;
2267
mreqs.imr_sourceaddr.s_addr = INADDR_ANY;
2268
} else if (sopt->sopt_name == IP_DROP_SOURCE_MEMBERSHIP) {
2269
error = sooptcopyin(sopt, &mreqs,
2270
sizeof(struct ip_mreq_source),
2271
sizeof(struct ip_mreq_source));
2272
}
2273
if (error)
2274
return (error);
2275
2276
gsa->sin.sin_family = AF_INET;
2277
gsa->sin.sin_len = sizeof(struct sockaddr_in);
2278
gsa->sin.sin_addr = mreqs.imr_multiaddr;
2279
2280
if (sopt->sopt_name == IP_DROP_SOURCE_MEMBERSHIP) {
2281
ssa->sin.sin_family = AF_INET;
2282
ssa->sin.sin_len = sizeof(struct sockaddr_in);
2283
ssa->sin.sin_addr = mreqs.imr_sourceaddr;
2284
}
2285
2286
/*
2287
* Attempt to look up hinted ifp from interface address.
2288
* Fallthrough with null ifp iff lookup fails, to
2289
* preserve 4.4BSD mcast API idempotence.
2290
* XXX NOTE WELL: The RFC 3678 API is preferred because
2291
* using an IPv4 address as a key is racy.
2292
*/
2293
if (!in_nullhost(mreqs.imr_interface)) {
2294
NET_EPOCH_ENTER(et);
2295
INADDR_TO_IFP(mreqs.imr_interface, ifp);
2296
/* XXXGL ifref? */
2297
NET_EPOCH_EXIT(et);
2298
}
2299
CTR3(KTR_IGMPV3, "%s: imr_interface = 0x%08x, ifp = %p",
2300
__func__, ntohl(mreqs.imr_interface.s_addr), ifp);
2301
2302
break;
2303
2304
case MCAST_LEAVE_GROUP:
2305
case MCAST_LEAVE_SOURCE_GROUP:
2306
if (sopt->sopt_name == MCAST_LEAVE_GROUP) {
2307
error = sooptcopyin(sopt, &gsr,
2308
sizeof(struct group_req),
2309
sizeof(struct group_req));
2310
} else if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) {
2311
error = sooptcopyin(sopt, &gsr,
2312
sizeof(struct group_source_req),
2313
sizeof(struct group_source_req));
2314
}
2315
if (error)
2316
return (error);
2317
2318
if (gsa->sin.sin_family != AF_INET ||
2319
gsa->sin.sin_len != sizeof(struct sockaddr_in))
2320
return (EINVAL);
2321
2322
if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) {
2323
if (ssa->sin.sin_family != AF_INET ||
2324
ssa->sin.sin_len != sizeof(struct sockaddr_in))
2325
return (EINVAL);
2326
}
2327
2328
NET_EPOCH_ENTER(et);
2329
ifp = ifnet_byindex(gsr.gsr_interface);
2330
NET_EPOCH_EXIT(et); /* XXXGL: unsafe ifp */
2331
if (ifp == NULL)
2332
return (EADDRNOTAVAIL);
2333
break;
2334
2335
default:
2336
CTR2(KTR_IGMPV3, "%s: unknown sopt_name %d",
2337
__func__, sopt->sopt_name);
2338
return (EOPNOTSUPP);
2339
break;
2340
}
2341
2342
if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
2343
return (EINVAL);
2344
2345
IN_MULTI_LOCK();
2346
2347
/*
2348
* Find the membership in the membership list.
2349
*/
2350
imo = inp_findmoptions(inp);
2351
imf = imo_match_group(imo, ifp, &gsa->sa);
2352
if (imf == NULL) {
2353
error = EADDRNOTAVAIL;
2354
goto out_inp_locked;
2355
}
2356
inm = imf->imf_inm;
2357
2358
if (ssa->ss.ss_family != AF_UNSPEC)
2359
is_final = false;
2360
2361
/*
2362
* Begin state merge transaction at socket layer.
2363
*/
2364
INP_WLOCK_ASSERT(inp);
2365
2366
/*
2367
* If we were instructed only to leave a given source, do so.
2368
* MCAST_LEAVE_SOURCE_GROUP is only valid for inclusive memberships.
2369
*/
2370
if (is_final) {
2371
ip_mfilter_remove(&imo->imo_head, imf);
2372
imf_leave(imf);
2373
2374
/*
2375
* Give up the multicast address record to which
2376
* the membership points.
2377
*/
2378
(void) in_leavegroup_locked(imf->imf_inm, imf);
2379
} else {
2380
if (imf->imf_st[0] == MCAST_EXCLUDE) {
2381
error = EADDRNOTAVAIL;
2382
goto out_inp_locked;
2383
}
2384
ims = imo_match_source(imf, &ssa->sa);
2385
if (ims == NULL) {
2386
CTR3(KTR_IGMPV3, "%s: source 0x%08x %spresent",
2387
__func__, ntohl(ssa->sin.sin_addr.s_addr), "not ");
2388
error = EADDRNOTAVAIL;
2389
goto out_inp_locked;
2390
}
2391
CTR2(KTR_IGMPV3, "%s: %s source", __func__, "block");
2392
error = imf_prune(imf, &ssa->sin);
2393
if (error) {
2394
CTR1(KTR_IGMPV3, "%s: merge imf state failed",
2395
__func__);
2396
goto out_inp_locked;
2397
}
2398
}
2399
2400
/*
2401
* Begin state merge transaction at IGMP layer.
2402
*/
2403
if (!is_final) {
2404
CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
2405
IN_MULTI_LIST_LOCK();
2406
error = inm_merge(inm, imf);
2407
if (error) {
2408
CTR1(KTR_IGMPV3, "%s: failed to merge inm state",
2409
__func__);
2410
IN_MULTI_LIST_UNLOCK();
2411
imf_rollback(imf);
2412
imf_reap(imf);
2413
goto out_inp_locked;
2414
}
2415
2416
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
2417
error = igmp_change_state(inm);
2418
IN_MULTI_LIST_UNLOCK();
2419
if (error) {
2420
CTR1(KTR_IGMPV3, "%s: failed igmp downcall",
2421
__func__);
2422
imf_rollback(imf);
2423
imf_reap(imf);
2424
goto out_inp_locked;
2425
}
2426
}
2427
imf_commit(imf);
2428
imf_reap(imf);
2429
2430
out_inp_locked:
2431
INP_WUNLOCK(inp);
2432
2433
if (is_final && imf)
2434
ip_mfilter_free(imf);
2435
2436
IN_MULTI_UNLOCK();
2437
return (error);
2438
}
2439
2440
/*
2441
* Select the interface for transmitting IPv4 multicast datagrams.
2442
*
2443
* Either an instance of struct in_addr or an instance of struct ip_mreqn
2444
* may be passed to this socket option. An address of INADDR_ANY or an
2445
* interface index of 0 is used to remove a previous selection.
2446
* When no interface is selected, one is chosen for every send.
2447
*/
2448
static int
2449
inp_set_multicast_if(struct inpcb *inp, struct sockopt *sopt)
2450
{
2451
struct in_addr addr;
2452
struct ip_mreqn mreqn;
2453
struct ifnet *ifp;
2454
struct ip_moptions *imo;
2455
int error;
2456
2457
if (sopt->sopt_valsize == sizeof(struct ip_mreqn)) {
2458
/*
2459
* An interface index was specified using the
2460
* Linux-derived ip_mreqn structure.
2461
*/
2462
error = sooptcopyin(sopt, &mreqn, sizeof(struct ip_mreqn),
2463
sizeof(struct ip_mreqn));
2464
if (error)
2465
return (error);
2466
2467
if (mreqn.imr_ifindex < 0)
2468
return (EINVAL);
2469
2470
if (mreqn.imr_ifindex == 0) {
2471
ifp = NULL;
2472
} else {
2473
struct epoch_tracker et;
2474
2475
NET_EPOCH_ENTER(et);
2476
ifp = ifnet_byindex(mreqn.imr_ifindex);
2477
NET_EPOCH_EXIT(et); /* XXXGL: unsafe ifp */
2478
if (ifp == NULL)
2479
return (EADDRNOTAVAIL);
2480
}
2481
} else {
2482
/*
2483
* An interface was specified by IPv4 address.
2484
* This is the traditional BSD usage.
2485
*/
2486
error = sooptcopyin(sopt, &addr, sizeof(struct in_addr),
2487
sizeof(struct in_addr));
2488
if (error)
2489
return (error);
2490
if (in_nullhost(addr)) {
2491
ifp = NULL;
2492
} else {
2493
struct epoch_tracker et;
2494
2495
NET_EPOCH_ENTER(et);
2496
INADDR_TO_IFP(addr, ifp);
2497
/* XXXGL ifref? */
2498
NET_EPOCH_EXIT(et);
2499
if (ifp == NULL)
2500
return (EADDRNOTAVAIL);
2501
}
2502
CTR3(KTR_IGMPV3, "%s: ifp = %p, addr = 0x%08x", __func__, ifp,
2503
ntohl(addr.s_addr));
2504
}
2505
2506
/* Reject interfaces which do not support multicast. */
2507
if (ifp != NULL && (ifp->if_flags & IFF_MULTICAST) == 0)
2508
return (EOPNOTSUPP);
2509
2510
imo = inp_findmoptions(inp);
2511
imo->imo_multicast_ifp = ifp;
2512
imo->imo_multicast_addr.s_addr = INADDR_ANY;
2513
INP_WUNLOCK(inp);
2514
2515
return (0);
2516
}
2517
2518
/*
2519
* Atomically set source filters on a socket for an IPv4 multicast group.
2520
*/
2521
static int
2522
inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
2523
{
2524
struct epoch_tracker et;
2525
struct __msfilterreq msfr;
2526
sockunion_t *gsa;
2527
struct ifnet *ifp;
2528
struct in_mfilter *imf;
2529
struct ip_moptions *imo;
2530
struct in_multi *inm;
2531
int error;
2532
2533
error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq),
2534
sizeof(struct __msfilterreq));
2535
if (error)
2536
return (error);
2537
2538
if (msfr.msfr_nsrcs > in_mcast_maxsocksrc)
2539
return (ENOBUFS);
2540
2541
if ((msfr.msfr_fmode != MCAST_EXCLUDE &&
2542
msfr.msfr_fmode != MCAST_INCLUDE))
2543
return (EINVAL);
2544
2545
if (msfr.msfr_group.ss_family != AF_INET ||
2546
msfr.msfr_group.ss_len != sizeof(struct sockaddr_in))
2547
return (EINVAL);
2548
2549
gsa = (sockunion_t *)&msfr.msfr_group;
2550
if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
2551
return (EINVAL);
2552
2553
gsa->sin.sin_port = 0; /* ignore port */
2554
2555
NET_EPOCH_ENTER(et);
2556
ifp = ifnet_byindex(msfr.msfr_ifindex);
2557
NET_EPOCH_EXIT(et); /* XXXGL: unsafe ifp */
2558
if (ifp == NULL)
2559
return (EADDRNOTAVAIL);
2560
2561
IN_MULTI_LOCK();
2562
2563
/*
2564
* Take the INP write lock.
2565
* Check if this socket is a member of this group.
2566
*/
2567
imo = inp_findmoptions(inp);
2568
imf = imo_match_group(imo, ifp, &gsa->sa);
2569
if (imf == NULL) {
2570
error = EADDRNOTAVAIL;
2571
goto out_inp_locked;
2572
}
2573
inm = imf->imf_inm;
2574
2575
/*
2576
* Begin state merge transaction at socket layer.
2577
*/
2578
INP_WLOCK_ASSERT(inp);
2579
2580
imf->imf_st[1] = msfr.msfr_fmode;
2581
2582
/*
2583
* Apply any new source filters, if present.
2584
* Make a copy of the user-space source vector so
2585
* that we may copy them with a single copyin. This
2586
* allows us to deal with page faults up-front.
2587
*/
2588
if (msfr.msfr_nsrcs > 0) {
2589
struct in_msource *lims;
2590
struct sockaddr_in *psin;
2591
struct sockaddr_storage *kss, *pkss;
2592
int i;
2593
2594
INP_WUNLOCK(inp);
2595
2596
CTR2(KTR_IGMPV3, "%s: loading %lu source list entries",
2597
__func__, (unsigned long)msfr.msfr_nsrcs);
2598
kss = malloc(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs,
2599
M_TEMP, M_WAITOK);
2600
error = copyin(msfr.msfr_srcs, kss,
2601
sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs);
2602
if (error) {
2603
free(kss, M_TEMP);
2604
return (error);
2605
}
2606
2607
INP_WLOCK(inp);
2608
2609
/*
2610
* Mark all source filters as UNDEFINED at t1.
2611
* Restore new group filter mode, as imf_leave()
2612
* will set it to INCLUDE.
2613
*/
2614
imf_leave(imf);
2615
imf->imf_st[1] = msfr.msfr_fmode;
2616
2617
/*
2618
* Update socket layer filters at t1, lazy-allocating
2619
* new entries. This saves a bunch of memory at the
2620
* cost of one RB_FIND() per source entry; duplicate
2621
* entries in the msfr_nsrcs vector are ignored.
2622
* If we encounter an error, rollback transaction.
2623
*
2624
* XXX This too could be replaced with a set-symmetric
2625
* difference like loop to avoid walking from root
2626
* every time, as the key space is common.
2627
*/
2628
for (i = 0, pkss = kss; i < msfr.msfr_nsrcs; i++, pkss++) {
2629
psin = (struct sockaddr_in *)pkss;
2630
if (psin->sin_family != AF_INET) {
2631
error = EAFNOSUPPORT;
2632
break;
2633
}
2634
if (psin->sin_len != sizeof(struct sockaddr_in)) {
2635
error = EINVAL;
2636
break;
2637
}
2638
error = imf_get_source(imf, psin, &lims);
2639
if (error)
2640
break;
2641
lims->imsl_st[1] = imf->imf_st[1];
2642
}
2643
free(kss, M_TEMP);
2644
}
2645
2646
if (error)
2647
goto out_imf_rollback;
2648
2649
INP_WLOCK_ASSERT(inp);
2650
2651
/*
2652
* Begin state merge transaction at IGMP layer.
2653
*/
2654
CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
2655
IN_MULTI_LIST_LOCK();
2656
error = inm_merge(inm, imf);
2657
if (error) {
2658
CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__);
2659
IN_MULTI_LIST_UNLOCK();
2660
goto out_imf_rollback;
2661
}
2662
2663
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
2664
error = igmp_change_state(inm);
2665
IN_MULTI_LIST_UNLOCK();
2666
if (error)
2667
CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__);
2668
2669
out_imf_rollback:
2670
if (error)
2671
imf_rollback(imf);
2672
else
2673
imf_commit(imf);
2674
2675
imf_reap(imf);
2676
2677
out_inp_locked:
2678
INP_WUNLOCK(inp);
2679
IN_MULTI_UNLOCK();
2680
return (error);
2681
}
2682
2683
/*
2684
* Set the IP multicast options in response to user setsockopt().
2685
*
2686
* Many of the socket options handled in this function duplicate the
2687
* functionality of socket options in the regular unicast API. However,
2688
* it is not possible to merge the duplicate code, because the idempotence
2689
* of the IPv4 multicast part of the BSD Sockets API must be preserved;
2690
* the effects of these options must be treated as separate and distinct.
2691
*
2692
* SMPng: XXX: Unlocked read of inp_socket believed OK.
2693
* FUTURE: The IP_MULTICAST_VIF option may be eliminated if MROUTING
2694
* is refactored to no longer use vifs.
2695
*/
2696
int
2697
inp_setmoptions(struct inpcb *inp, struct sockopt *sopt)
2698
{
2699
struct ip_moptions *imo;
2700
int error;
2701
2702
error = 0;
2703
2704
/* If socket is neither of type SOCK_RAW or SOCK_DGRAM, reject it. */
2705
if (inp->inp_socket->so_proto->pr_type != SOCK_RAW &&
2706
inp->inp_socket->so_proto->pr_type != SOCK_DGRAM)
2707
return (EOPNOTSUPP);
2708
2709
switch (sopt->sopt_name) {
2710
case IP_MULTICAST_VIF: {
2711
int vifi;
2712
/*
2713
* Select a multicast VIF for transmission.
2714
* Only useful if multicast forwarding is active.
2715
*/
2716
if (legal_vif_num == NULL) {
2717
error = EOPNOTSUPP;
2718
break;
2719
}
2720
error = sooptcopyin(sopt, &vifi, sizeof(int), sizeof(int));
2721
if (error)
2722
break;
2723
if (!legal_vif_num(vifi) && (vifi != -1)) {
2724
error = EINVAL;
2725
break;
2726
}
2727
imo = inp_findmoptions(inp);
2728
imo->imo_multicast_vif = vifi;
2729
INP_WUNLOCK(inp);
2730
break;
2731
}
2732
2733
case IP_MULTICAST_IF:
2734
error = inp_set_multicast_if(inp, sopt);
2735
break;
2736
2737
case IP_MULTICAST_TTL: {
2738
u_char ttl;
2739
2740
/*
2741
* Set the IP time-to-live for outgoing multicast packets.
2742
* The original multicast API required a char argument,
2743
* which is inconsistent with the rest of the socket API.
2744
* We allow either a char or an int.
2745
*/
2746
if (sopt->sopt_valsize == sizeof(u_char)) {
2747
error = sooptcopyin(sopt, &ttl, sizeof(u_char),
2748
sizeof(u_char));
2749
if (error)
2750
break;
2751
} else {
2752
u_int ittl;
2753
2754
error = sooptcopyin(sopt, &ittl, sizeof(u_int),
2755
sizeof(u_int));
2756
if (error)
2757
break;
2758
if (ittl > 255) {
2759
error = EINVAL;
2760
break;
2761
}
2762
ttl = (u_char)ittl;
2763
}
2764
imo = inp_findmoptions(inp);
2765
imo->imo_multicast_ttl = ttl;
2766
INP_WUNLOCK(inp);
2767
break;
2768
}
2769
2770
case IP_MULTICAST_LOOP: {
2771
u_char loop;
2772
2773
/*
2774
* Set the loopback flag for outgoing multicast packets.
2775
* Must be zero or one. The original multicast API required a
2776
* char argument, which is inconsistent with the rest
2777
* of the socket API. We allow either a char or an int.
2778
*/
2779
if (sopt->sopt_valsize == sizeof(u_char)) {
2780
error = sooptcopyin(sopt, &loop, sizeof(u_char),
2781
sizeof(u_char));
2782
if (error)
2783
break;
2784
} else {
2785
u_int iloop;
2786
2787
error = sooptcopyin(sopt, &iloop, sizeof(u_int),
2788
sizeof(u_int));
2789
if (error)
2790
break;
2791
loop = (u_char)iloop;
2792
}
2793
imo = inp_findmoptions(inp);
2794
imo->imo_multicast_loop = !!loop;
2795
INP_WUNLOCK(inp);
2796
break;
2797
}
2798
2799
case IP_ADD_MEMBERSHIP:
2800
case IP_ADD_SOURCE_MEMBERSHIP:
2801
case MCAST_JOIN_GROUP:
2802
case MCAST_JOIN_SOURCE_GROUP:
2803
error = inp_join_group(inp, sopt);
2804
break;
2805
2806
case IP_DROP_MEMBERSHIP:
2807
case IP_DROP_SOURCE_MEMBERSHIP:
2808
case MCAST_LEAVE_GROUP:
2809
case MCAST_LEAVE_SOURCE_GROUP:
2810
error = inp_leave_group(inp, sopt);
2811
break;
2812
2813
case IP_BLOCK_SOURCE:
2814
case IP_UNBLOCK_SOURCE:
2815
case MCAST_BLOCK_SOURCE:
2816
case MCAST_UNBLOCK_SOURCE:
2817
error = inp_block_unblock_source(inp, sopt);
2818
break;
2819
2820
case IP_MSFILTER:
2821
error = inp_set_source_filters(inp, sopt);
2822
break;
2823
2824
default:
2825
error = EOPNOTSUPP;
2826
break;
2827
}
2828
2829
INP_UNLOCK_ASSERT(inp);
2830
2831
return (error);
2832
}
2833
2834
/*
2835
* Expose IGMP's multicast filter mode and source list(s) to userland,
2836
* keyed by (ifindex, group).
2837
* The filter mode is written out as a uint32_t, followed by
2838
* 0..n of struct in_addr.
2839
* For use by ifmcstat(8).
2840
* SMPng: NOTE: unlocked read of ifindex space.
2841
*/
2842
static int
2843
sysctl_ip_mcast_filters(SYSCTL_HANDLER_ARGS)
2844
{
2845
struct in_addr src, group;
2846
struct epoch_tracker et;
2847
struct ifnet *ifp;
2848
struct ifmultiaddr *ifma;
2849
struct in_multi *inm;
2850
struct ip_msource *ims;
2851
int *name;
2852
int retval;
2853
u_int namelen;
2854
uint32_t fmode, ifindex;
2855
2856
name = (int *)arg1;
2857
namelen = arg2;
2858
2859
if (req->newptr != NULL)
2860
return (EPERM);
2861
2862
if (namelen != 2)
2863
return (EINVAL);
2864
2865
group.s_addr = name[1];
2866
if (!IN_MULTICAST(ntohl(group.s_addr))) {
2867
CTR2(KTR_IGMPV3, "%s: group 0x%08x is not multicast",
2868
__func__, ntohl(group.s_addr));
2869
return (EINVAL);
2870
}
2871
2872
ifindex = name[0];
2873
NET_EPOCH_ENTER(et);
2874
ifp = ifnet_byindex(ifindex);
2875
if (ifp == NULL) {
2876
NET_EPOCH_EXIT(et);
2877
CTR2(KTR_IGMPV3, "%s: no ifp for ifindex %u",
2878
__func__, ifindex);
2879
return (ENOENT);
2880
}
2881
2882
retval = sysctl_wire_old_buffer(req,
2883
sizeof(uint32_t) + (in_mcast_maxgrpsrc * sizeof(struct in_addr)));
2884
if (retval) {
2885
NET_EPOCH_EXIT(et);
2886
return (retval);
2887
}
2888
2889
IN_MULTI_LIST_LOCK();
2890
2891
CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
2892
inm = inm_ifmultiaddr_get_inm(ifma);
2893
if (inm == NULL)
2894
continue;
2895
if (!in_hosteq(inm->inm_addr, group))
2896
continue;
2897
fmode = inm->inm_st[1].iss_fmode;
2898
retval = SYSCTL_OUT(req, &fmode, sizeof(uint32_t));
2899
if (retval != 0)
2900
break;
2901
RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) {
2902
CTR2(KTR_IGMPV3, "%s: visit node 0x%08x", __func__,
2903
ims->ims_haddr);
2904
/*
2905
* Only copy-out sources which are in-mode.
2906
*/
2907
if (fmode != ims_get_mode(inm, ims, 1)) {
2908
CTR1(KTR_IGMPV3, "%s: skip non-in-mode",
2909
__func__);
2910
continue;
2911
}
2912
src.s_addr = htonl(ims->ims_haddr);
2913
retval = SYSCTL_OUT(req, &src, sizeof(struct in_addr));
2914
if (retval != 0)
2915
break;
2916
}
2917
}
2918
2919
IN_MULTI_LIST_UNLOCK();
2920
NET_EPOCH_EXIT(et);
2921
2922
return (retval);
2923
}
2924
2925
#if defined(KTR) && (KTR_COMPILE & KTR_IGMPV3)
2926
2927
static const char *inm_modestrs[] = {
2928
[MCAST_UNDEFINED] = "un",
2929
[MCAST_INCLUDE] = "in",
2930
[MCAST_EXCLUDE] = "ex",
2931
};
2932
_Static_assert(MCAST_UNDEFINED == 0 &&
2933
MCAST_EXCLUDE + 1 == nitems(inm_modestrs),
2934
"inm_modestrs: no longer matches #defines");
2935
2936
static const char *
2937
inm_mode_str(const int mode)
2938
{
2939
2940
if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE)
2941
return (inm_modestrs[mode]);
2942
return ("??");
2943
}
2944
2945
static const char *inm_statestrs[] = {
2946
[IGMP_NOT_MEMBER] = "not-member",
2947
[IGMP_SILENT_MEMBER] = "silent",
2948
[IGMP_REPORTING_MEMBER] = "reporting",
2949
[IGMP_IDLE_MEMBER] = "idle",
2950
[IGMP_LAZY_MEMBER] = "lazy",
2951
[IGMP_SLEEPING_MEMBER] = "sleeping",
2952
[IGMP_AWAKENING_MEMBER] = "awakening",
2953
[IGMP_G_QUERY_PENDING_MEMBER] = "query-pending",
2954
[IGMP_SG_QUERY_PENDING_MEMBER] = "sg-query-pending",
2955
[IGMP_LEAVING_MEMBER] = "leaving",
2956
};
2957
_Static_assert(IGMP_NOT_MEMBER == 0 &&
2958
IGMP_LEAVING_MEMBER + 1 == nitems(inm_statestrs),
2959
"inm_statetrs: no longer matches #defines");
2960
2961
static const char *
2962
inm_state_str(const int state)
2963
{
2964
2965
if (state >= IGMP_NOT_MEMBER && state <= IGMP_LEAVING_MEMBER)
2966
return (inm_statestrs[state]);
2967
return ("??");
2968
}
2969
2970
/*
2971
* Dump an in_multi structure to the console.
2972
*/
2973
void
2974
inm_print(const struct in_multi *inm)
2975
{
2976
int t;
2977
char addrbuf[INET_ADDRSTRLEN];
2978
2979
if ((ktr_mask & KTR_IGMPV3) == 0)
2980
return;
2981
2982
printf("%s: --- begin inm %p ---\n", __func__, inm);
2983
printf("addr %s ifp %p(%s) ifma %p\n",
2984
inet_ntoa_r(inm->inm_addr, addrbuf),
2985
inm->inm_ifp,
2986
inm->inm_ifp->if_xname,
2987
inm->inm_ifma);
2988
printf("timer %u state %s refcount %u scq.len %u\n",
2989
inm->inm_timer,
2990
inm_state_str(inm->inm_state),
2991
inm->inm_refcount,
2992
inm->inm_scq.mq_len);
2993
printf("igi %p nsrc %lu sctimer %u scrv %u\n",
2994
inm->inm_igi,
2995
inm->inm_nsrc,
2996
inm->inm_sctimer,
2997
inm->inm_scrv);
2998
for (t = 0; t < 2; t++) {
2999
printf("t%d: fmode %s asm %u ex %u in %u rec %u\n", t,
3000
inm_mode_str(inm->inm_st[t].iss_fmode),
3001
inm->inm_st[t].iss_asm,
3002
inm->inm_st[t].iss_ex,
3003
inm->inm_st[t].iss_in,
3004
inm->inm_st[t].iss_rec);
3005
}
3006
printf("%s: --- end inm %p ---\n", __func__, inm);
3007
}
3008
3009
#else /* !KTR || !(KTR_COMPILE & KTR_IGMPV3) */
3010
3011
void
3012
inm_print(const struct in_multi *inm)
3013
{
3014
3015
}
3016
3017
#endif /* KTR && (KTR_COMPILE & KTR_IGMPV3) */
3018
3019
RB_GENERATE(ip_msource_tree, ip_msource, ims_link, ip_msource_cmp);
3020
3021