44#include <sys/kernel.h>
46#include <sys/malloc.h>
48#include <sys/protosw.h>
49#include <sys/socket.h>
50#include <sys/socketvar.h>
51#include <sys/sysctl.h>
53#include <sys/taskqueue.h>
57#include <net/if_var.h>
60#include <net/route/nhop.h>
63#include <netinet/in.h>
64#include <netinet/udp.h>
65#include <netinet/in_var.h>
66#include <netinet/ip_var.h>
67#include <netinet/udp_var.h>
70#include <netinet/ip6.h>
71#include <netinet/icmp6.h>
73#include <netinet/in_pcb.h>
74#include <netinet/tcp_var.h>
80#define KTR_MLD KTR_INET6
83#ifndef __SOCKUNION_DECLARED
85 struct sockaddr_storage
ss;
87 struct sockaddr_dl
sdl;
91#define __SOCKUNION_DECLARED
95 "IPv6 multicast PCB-layer source filter");
97static MALLOC_DEFINE(M_IP6MOPTS,
"ip6_moptions",
"IPv6 multicast options");
99 "IPv6 multicast MLD-layer source filter");
138 const struct ifnet *,
const struct sockaddr *);
142 const struct in6_msource *lims,
const int rollback);
148 const struct in6_addr *addr,
const int noalloc,
151static int in6m_is_ifp_detached(
const struct in6_multi *);
156static struct ip6_moptions *
172 CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
178 "Max source filters per group");
183 "Max source filters per socket");
187SYSCTL_INT(_net_inet6_ip6_mcast, OID_AUTO, loop, CTLFLAG_RWTUN,
192 "Per-interface stack-wide source filters");
201in6m_is_ifp_detached(
const struct in6_multi *inm)
205 KASSERT(inm->
in6m_ifma != NULL, (
"%s: no ifma", __func__));
212 KASSERT(inm->
in6m_ifp == ifp, (
"%s: bad ifp", __func__));
215 return (ifp == NULL);
237 imf = malloc(
sizeof(*imf), M_IN6MFILTER, mflags);
250 free(imf, M_IN6MFILTER);
260 const struct sockaddr *group)
272 if ((ifp == NULL || (inm->
in6m_ifp == ifp)) &&
299 KASSERT(src->sa_family == AF_INET6, (
"%s: !AF_INET6", __func__));
304 ims = RB_FIND(ip6_msource_tree, &imf->
im6f_sources, &find);
317 const struct sockaddr *group,
const struct sockaddr *src)
323 KASSERT(ifp != NULL, (
"%s: null ifp", __func__));
327 return (MCAST_NOTGMEMBER);
341 if ((ims == NULL && mode == MCAST_INCLUDE) ||
342 (ims != NULL && ims->
im6sl_st[0] != mode))
343 return (MCAST_NOTSMEMBER);
360 struct epoch_tracker et;
362 struct ifmultiaddr *ifma;
380 if (ifp->if_afdata[AF_INET6] == NULL)
401 memset(&gsin6, 0,
sizeof(gsin6));
411 IF_ADDR_WUNLOCK(ifp);
412 error = if_addmulti(ifp, (
struct sockaddr *)&gsin6, &ifma);
425 if (ifma->ifma_protospec != NULL) {
426 inm = (
struct in6_multi *)ifma->ifma_protospec;
428 KASSERT(ifma->ifma_addr != NULL, (
"%s: no ifma_addr",
430 KASSERT(ifma->ifma_addr->sa_family == AF_INET6,
431 (
"%s: ifma not AF_INET6", __func__));
432 KASSERT(inm != NULL, (
"%s: no ifma_protospec", __func__));
435 panic(
"%s: ifma %p is inconsistent with %p (%p)",
436 __func__, ifma, inm, group);
443 IF_ADDR_WLOCK_ASSERT(ifp);
453 inm = malloc(
sizeof(*inm), M_IP6MADDR, M_NOWAIT | M_ZERO);
456 IF_ADDR_WUNLOCK(ifp);
457 if_delmulti_ifma(ifma);
472 ifma->ifma_protospec = inm;
477 IF_ADDR_WUNLOCK(ifp);
490 struct ifmultiaddr *ifma;
496 CTR2(
KTR_MLD,
"%s: freeing inm %p", __func__, inm);
500 MPASS(ifma->ifma_llifma == NULL);
503 CTR2(
KTR_MLD,
"%s: purging ifma %p", __func__, ifma);
504 KASSERT(ifma->ifma_protospec == NULL,
505 (
"%s: ifma_protospec != NULL", __func__));
507 ifp = ifma->ifma_ifp;
510 CURVNET_SET(ifp->if_vnet);
512 free(inm, M_IP6MADDR);
513 if_delmulti_ifma_flags(ifma, 1);
518 free(inm, M_IP6MADDR);
519 if_delmulti_ifma_flags(ifma, 1);
535 if (SLIST_EMPTY(inmh))
551 taskqueue_drain_all(taskqueue_in6m_free);
565 struct ifmultiaddr *ifma, *ll_ifma;
574 IF_ADDR_WLOCK_ASSERT(ifp);
580 if (ifma->ifma_flags & IFMA_F_ENQUEUED) {
581 CK_STAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifmultiaddr, ifma_link);
582 ifma->ifma_flags &= ~IFMA_F_ENQUEUED;
584 MCDPRINTF(
"removed ifma: %p from %s\n", ifma, ifp->if_xname);
585 if ((ll_ifma = ifma->ifma_llifma) != NULL) {
586 MPASS(ifma != ll_ifma);
587 ifma->ifma_llifma = NULL;
588 MPASS(ll_ifma->ifma_llifma == NULL);
589 MPASS(ll_ifma->ifma_ifp == ifp);
590 if (--ll_ifma->ifma_refcount == 0) {
591 if (ll_ifma->ifma_flags & IFMA_F_ENQUEUED) {
592 CK_STAILQ_REMOVE(&ifp->if_multiaddrs, ll_ifma, ifmultiaddr, ifma_link);
593 ll_ifma->ifma_flags &= ~IFMA_F_ENQUEUED;
595 MCDPRINTF(
"removed ll_ifma: %p from %s\n", ll_ifma, ifp->if_xname);
596 if_freemulti(ll_ifma);
599 CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
600 if (ifa->ifa_addr->sa_family != AF_INET6)
603 LIST_FOREACH_SAFE(imm, &ifa6->ia6_memberships,
604 i6mm_chain, imm_tmp) {
606 LIST_REMOVE(imm, i6mm_chain);
607 free(imm, M_IP6MADDR);
617 struct in6_multi_head in6m_free_tmp;
620 SLIST_INIT(&in6m_free_tmp);
625 SLIST_FOREACH_SAFE(inm, &in6m_free_tmp, in6m_nrele, tinm) {
626 SLIST_REMOVE_HEAD(&in6m_free_tmp, in6m_nrele);
644 RB_FOREACH(ims, ip6_msource_tree, &inm->
in6m_srcs) {
685 ims = RB_FIND(ip6_msource_tree, &inm->
in6m_srcs, &find);
691 nims = malloc(
sizeof(
struct ip6_msource), M_IP6MSOURCE,
696 RB_INSERT(ip6_msource_tree, &inm->
in6m_srcs, nims);
736 ims = RB_FIND(ip6_msource_tree, &imf->
im6f_sources, &find);
741 nims = malloc(
sizeof(
struct in6_msource), M_IN6MFILTER,
747 lims->
im6sl_st[0] = MCAST_UNDEFINED;
772 nims = malloc(
sizeof(
struct in6_msource), M_IN6MFILTER,
778 lims->
im6sl_st[0] = MCAST_UNDEFINED;
802 ims = RB_FIND(ip6_msource_tree, &imf->
im6f_sources, &find);
806 lims->
im6sl_st[1] = MCAST_UNDEFINED;
819 RB_FOREACH_SAFE(ims, ip6_msource_tree, &imf->
im6f_sources, tims) {
824 }
else if (lims->
im6sl_st[0] != MCAST_UNDEFINED) {
829 CTR2(
KTR_MLD,
"%s: free ims %p", __func__, ims);
831 free(ims, M_IN6MFILTER);
849 lims->
im6sl_st[1] = MCAST_UNDEFINED;
851 imf->
im6f_st[1] = MCAST_INCLUDE;
879 RB_FOREACH_SAFE(ims, ip6_msource_tree, &imf->
im6f_sources, tims) {
881 if ((lims->
im6sl_st[0] == MCAST_UNDEFINED) &&
882 (lims->
im6sl_st[1] == MCAST_UNDEFINED)) {
883 CTR2(
KTR_MLD,
"%s: free lims %p", __func__, ims);
885 free(ims, M_IN6MFILTER);
899 RB_FOREACH_SAFE(ims, ip6_msource_tree, &imf->
im6f_sources, tims) {
900 CTR2(
KTR_MLD,
"%s: free ims %p", __func__, ims);
902 free(ims, M_IN6MFILTER);
907 (
"%s: im6f_sources not empty", __func__));
932 ims = RB_FIND(ip6_msource_tree, &inm->
in6m_srcs, &find);
933 if (ims == NULL && !noalloc) {
936 nims = malloc(
sizeof(
struct ip6_msource), M_IP6MSOURCE,
941 RB_INSERT(ip6_msource_tree, &inm->
in6m_srcs, nims);
944 CTR3(
KTR_MLD,
"%s: allocated %s as %p", __func__,
960 int n = rollback ? -1 : 1;
967 if (lims->
im6sl_st[0] == MCAST_EXCLUDE) {
968 CTR3(
KTR_MLD,
"%s: t1 ex -= %d on %s", __func__, n, ip6tbuf);
970 }
else if (lims->
im6sl_st[0] == MCAST_INCLUDE) {
971 CTR3(
KTR_MLD,
"%s: t1 in -= %d on %s", __func__, n, ip6tbuf);
975 if (lims->
im6sl_st[1] == MCAST_EXCLUDE) {
976 CTR3(
KTR_MLD,
"%s: t1 ex += %d on %s", __func__, n, ip6tbuf);
978 }
else if (lims->
im6sl_st[1] == MCAST_INCLUDE) {
979 CTR3(
KTR_MLD,
"%s: t1 in += %d on %s", __func__, n, ip6tbuf);
1003 int schanged, error;
1018 RB_FOREACH(ims, ip6_msource_tree, &imf->
im6f_sources) {
1032 RB_FOREACH_REVERSE_FROM(ims, ip6_msource_tree, nims) {
1044 CTR3(
KTR_MLD,
"%s: imf filters in-mode: %d at t0, %d at t1",
1045 __func__, nsrc0, nsrc1);
1049 imf->
im6f_st[1] == MCAST_INCLUDE) {
1051 CTR1(
KTR_MLD,
"%s: --in on inm at t1", __func__);
1058 CTR3(
KTR_MLD,
"%s: imf transition %d to %d",
1061 if (imf->
im6f_st[0] == MCAST_EXCLUDE) {
1062 CTR1(
KTR_MLD,
"%s: --ex on inm at t1", __func__);
1064 }
else if (imf->
im6f_st[0] == MCAST_INCLUDE) {
1065 CTR1(
KTR_MLD,
"%s: --in on inm at t1", __func__);
1069 if (imf->
im6f_st[1] == MCAST_EXCLUDE) {
1070 CTR1(
KTR_MLD,
"%s: ex++ on inm at t1", __func__);
1072 }
else if (imf->
im6f_st[1] == MCAST_INCLUDE && nsrc1 > 0) {
1073 CTR1(
KTR_MLD,
"%s: in++ on inm at t1", __func__);
1087 CTR1(
KTR_MLD,
"%s: transition to EX", __func__);
1090 CTR1(
KTR_MLD,
"%s: transition to IN", __func__);
1093 CTR1(
KTR_MLD,
"%s: transition to UNDEF", __func__);
1098 if (imf->
im6f_st[0] == MCAST_EXCLUDE && nsrc0 == 0) {
1099 if ((imf->
im6f_st[1] != MCAST_EXCLUDE) ||
1100 (imf->
im6f_st[1] == MCAST_EXCLUDE && nsrc1 > 0)) {
1101 CTR1(
KTR_MLD,
"%s: --asm on inm at t1", __func__);
1107 if (imf->
im6f_st[1] == MCAST_EXCLUDE && nsrc1 == 0) {
1108 CTR1(
KTR_MLD,
"%s: asm++ on inm at t1", __func__);
1112 CTR3(
KTR_MLD,
"%s: merged imf %p to inm %p", __func__, imf, inm);
1117 CTR1(
KTR_MLD,
"%s: sources changed; reaping", __func__);
1132 CTR2(
KTR_MLD,
"%s: commit inm %p", __func__, inm);
1133 CTR1(
KTR_MLD,
"%s: pre commit:", __func__);
1136 RB_FOREACH(ims, ip6_msource_tree, &inm->
in6m_srcs) {
1150 RB_FOREACH_SAFE(ims, ip6_msource_tree, &inm->
in6m_srcs, tims) {
1155 CTR2(
KTR_MLD,
"%s: free ims %p", __func__, ims);
1156 RB_REMOVE(ip6_msource_tree, &inm->
in6m_srcs, ims);
1157 free(ims, M_IP6MSOURCE);
1170 RB_FOREACH_SAFE(ims, ip6_msource_tree, &inm->
in6m_srcs, tims) {
1171 CTR2(
KTR_MLD,
"%s: free ims %p", __func__, ims);
1172 RB_REMOVE(ip6_msource_tree, &inm->
in6m_srcs, ims);
1173 free(ims, M_IP6MSOURCE);
1213 struct in6_multi_head inmh;
1216 struct ifmultiaddr *ifma;
1227 (
"%s: not a multicast address", __func__));
1230 KASSERT(mcaddr->s6_addr16[1] != 0,
1231 (
"%s: scope zone ID not set", __func__));
1237 CTR4(
KTR_MLD,
"%s: join %s on %p(%s))", __func__,
1248 im6f_init(&timf, MCAST_UNDEFINED, MCAST_EXCLUDE);
1253 CTR1(
KTR_MLD,
"%s: in6_getmulti() failure", __func__);
1258 CTR1(
KTR_MLD,
"%s: merge inm state", __func__);
1261 CTR1(
KTR_MLD,
"%s: failed to merge inm state", __func__);
1262 goto out_in6m_release;
1265 CTR1(
KTR_MLD,
"%s: doing mld downcall", __func__);
1268 CTR1(
KTR_MLD,
"%s: failed to update source", __func__);
1269 goto out_in6m_release;
1275 struct epoch_tracker et;
1277 CTR2(
KTR_MLD,
"%s: dropping ref on %p", __func__, inm);
1279 NET_EPOCH_ENTER(et);
1280 CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
1281 if (ifma->ifma_protospec == inm) {
1282 ifma->ifma_protospec = NULL;
1289 IF_ADDR_WUNLOCK(ifp);
1328 struct in6_multi_head inmh;
1340 CTR5(
KTR_MLD,
"%s: leave inm %p, %s/%s, imf %p", __func__,
1342 (in6m_is_ifp_detached(inm) ?
"null" : if_name(inm->
in6m_ifp)),
1350 im6f_init(&timf, MCAST_EXCLUDE, MCAST_UNDEFINED);
1364 CTR1(
KTR_MLD,
"%s: merge inm state", __func__);
1366 KASSERT(error == 0, (
"%s: failed to merge inm state", __func__));
1368 CTR1(
KTR_MLD,
"%s: doing mld downcall", __func__);
1373 CTR1(
KTR_MLD,
"%s: failed mld downcall", __func__);
1375 CTR2(
KTR_MLD,
"%s: dropping ref on %p", __func__, inm);
1384 IF_ADDR_WUNLOCK(ifp);
1404 struct group_source_req gsr;
1405 struct epoch_tracker et;
1409 struct ip6_moptions *imo;
1422 memset(&gsr, 0,
sizeof(
struct group_source_req));
1426 switch (sopt->sopt_name) {
1427 case MCAST_BLOCK_SOURCE:
1428 case MCAST_UNBLOCK_SOURCE:
1429 error = sooptcopyin(sopt, &gsr,
1430 sizeof(
struct group_source_req),
1431 sizeof(
struct group_source_req));
1435 if (gsa->sin6.sin6_family != AF_INET6 ||
1439 if (ssa->sin6.sin6_family != AF_INET6 ||
1448 NET_EPOCH_ENTER(et);
1449 ifp = ifnet_byindex(gsr.gsr_interface);
1452 return (EADDRNOTAVAIL);
1454 if (sopt->sopt_name == MCAST_BLOCK_SOURCE)
1459 CTR2(
KTR_MLD,
"%s: unknown sopt_name %d",
1460 __func__, sopt->sopt_name);
1461 return (EOPNOTSUPP);
1476 error = EADDRNOTAVAIL;
1477 goto out_in6p_locked;
1486 if (fmode != MCAST_EXCLUDE) {
1488 goto out_in6p_locked;
1498 if ((ims != NULL && doblock) || (ims == NULL && !doblock)) {
1499 CTR3(
KTR_MLD,
"%s: source %s %spresent", __func__,
1501 doblock ?
"" :
"not ");
1502 error = EADDRNOTAVAIL;
1503 goto out_in6p_locked;
1506 INP_WLOCK_ASSERT(inp);
1512 CTR2(
KTR_MLD,
"%s: %s source", __func__,
"block");
1517 CTR2(
KTR_MLD,
"%s: %s source", __func__,
"allow");
1522 CTR1(
KTR_MLD,
"%s: merge imf state failed", __func__);
1523 goto out_im6f_rollback;
1530 CTR1(
KTR_MLD,
"%s: merge inm state", __func__);
1533 CTR1(
KTR_MLD,
"%s: failed to merge inm state", __func__);
1535 CTR1(
KTR_MLD,
"%s: doing mld downcall", __func__);
1538 CTR1(
KTR_MLD,
"%s: failed mld downcall", __func__);
1563static struct ip6_moptions *
1566 struct ip6_moptions *imo;
1569 if (inp->in6p_moptions != NULL)
1570 return (inp->in6p_moptions);
1574 imo = malloc(
sizeof(*imo), M_IP6MOPTS, M_WAITOK);
1576 imo->im6o_multicast_ifp = NULL;
1579 STAILQ_INIT(&imo->im6o_head);
1582 if (inp->in6p_moptions != NULL) {
1583 free(imo, M_IP6MOPTS);
1584 return (inp->in6p_moptions);
1586 inp->in6p_moptions = imo;
1611 if ((ifp = inm->
in6m_ifp) != NULL) {
1612 CURVNET_SET(ifp->if_vnet);
1621 free(imo, M_IP6MOPTS);
1639 struct epoch_tracker et;
1640 struct __msfilterreq msfr;
1643 struct ip6_moptions *imo;
1648 struct sockaddr_storage *ptss;
1649 struct sockaddr_storage *tss;
1651 size_t nsrcs, ncsrcs;
1653 INP_WLOCK_ASSERT(inp);
1655 imo = inp->in6p_moptions;
1656 KASSERT(imo != NULL, (
"%s: null ip6_moptions", __func__));
1660 error = sooptcopyin(sopt, &msfr,
sizeof(
struct __msfilterreq),
1661 sizeof(
struct __msfilterreq));
1665 if (msfr.msfr_group.ss_family != AF_INET6 ||
1677 NET_EPOCH_ENTER(et);
1678 ifp = ifnet_byindex(msfr.msfr_ifindex);
1681 return (EADDRNOTAVAIL);
1692 return (EADDRNOTAVAIL);
1698 if (imf->
im6f_st[1] == MCAST_UNDEFINED) {
1702 msfr.msfr_fmode = imf->
im6f_st[1];
1714 if (msfr.msfr_srcs != NULL && msfr.msfr_nsrcs > 0) {
1715 tss = malloc(
sizeof(
struct sockaddr_storage) * msfr.msfr_nsrcs,
1716 M_TEMP, M_NOWAIT | M_ZERO);
1727 nsrcs = msfr.msfr_nsrcs;
1730 RB_FOREACH(ims, ip6_msource_tree, &imf->
im6f_sources) {
1732 if (lims->
im6sl_st[0] == MCAST_UNDEFINED ||
1736 if (tss != NULL && nsrcs > 0) {
1750 error = copyout(tss, msfr.msfr_srcs,
1751 sizeof(
struct sockaddr_storage) * msfr.msfr_nsrcs);
1757 msfr.msfr_nsrcs = ncsrcs;
1758 error = sooptcopyout(sopt, &msfr,
sizeof(
struct __msfilterreq));
1769 struct ip6_moptions *im6o;
1774 im6o = inp->in6p_moptions;
1779 if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT ||
1780 (inp->inp_socket->so_proto->pr_type != SOCK_RAW &&
1781 inp->inp_socket->so_proto->pr_type != SOCK_DGRAM)) {
1783 return (EOPNOTSUPP);
1787 switch (sopt->sopt_name) {
1789 if (im6o == NULL || im6o->im6o_multicast_ifp == NULL) {
1792 optval = im6o->im6o_multicast_ifp->if_index;
1795 error = sooptcopyout(sopt, &optval,
sizeof(u_int));
1802 optval = im6o->im6o_multicast_hlim;
1804 error = sooptcopyout(sopt, &optval,
sizeof(u_int));
1811 optval = im6o->im6o_multicast_loop;
1813 error = sooptcopyout(sopt, &optval,
sizeof(u_int));
1818 error = EADDRNOTAVAIL;
1827 error = ENOPROTOOPT;
1831 INP_UNLOCK_ASSERT(inp);
1850static struct ifnet *
1853 struct nhop_object *nh;
1859 (
"%s: not AF_INET6 group", __func__));
1862 fibnum = inp->inp_inc.inc_fibnum;
1865 return (nh ? nh->nh_ifp : NULL);
1880 struct in6_multi_head inmh;
1881 struct group_source_req gsr;
1882 struct epoch_tracker et;
1886 struct ip6_moptions *imo;
1896 memset(&gsr, 0,
sizeof(
struct group_source_req));
1898 gsa->ss.ss_family = AF_UNSPEC;
1900 ssa->ss.ss_family = AF_UNSPEC;
1908 switch (sopt->sopt_name) {
1912 error = sooptcopyin(sopt, &mreq,
sizeof(
struct ipv6_mreq),
1917 gsa->sin6.sin6_family = AF_INET6;
1924 NET_EPOCH_ENTER(et);
1928 return (EADDRNOTAVAIL);
1930 CTR3(
KTR_MLD,
"%s: ipv6mr_interface = %d, ifp = %p",
1934 case MCAST_JOIN_GROUP:
1935 case MCAST_JOIN_SOURCE_GROUP:
1936 if (sopt->sopt_name == MCAST_JOIN_GROUP) {
1937 error = sooptcopyin(sopt, &gsr,
1938 sizeof(
struct group_req),
1939 sizeof(
struct group_req));
1940 }
else if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) {
1941 error = sooptcopyin(sopt, &gsr,
1942 sizeof(
struct group_source_req),
1943 sizeof(
struct group_source_req));
1948 if (gsa->sin6.sin6_family != AF_INET6 ||
1952 if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) {
1953 if (ssa->sin6.sin6_family != AF_INET6 ||
1964 ssa->sin6.sin6_port = 0;
1965 ssa->sin6.sin6_scope_id = 0;
1967 NET_EPOCH_ENTER(et);
1968 ifp = ifnet_byindex(gsr.gsr_interface);
1971 return (EADDRNOTAVAIL);
1975 CTR2(
KTR_MLD,
"%s: unknown sopt_name %d",
1976 __func__, sopt->sopt_name);
1977 return (EOPNOTSUPP);
1984 if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0)
1985 return (EADDRNOTAVAIL);
1987 gsa->sin6.sin6_port = 0;
1988 gsa->sin6.sin6_scope_id = 0;
2011 goto out_in6p_locked;
2017 if (ssa->ss.ss_family != AF_UNSPEC) {
2023 if (imf->
im6f_st[1] != MCAST_INCLUDE) {
2025 goto out_in6p_locked;
2046 error = EADDRNOTAVAIL;
2047 goto out_in6p_locked;
2062 goto out_in6p_locked;
2069 INP_WLOCK_ASSERT(inp);
2082 if (ssa->ss.ss_family != AF_UNSPEC) {
2085 CTR1(
KTR_MLD,
"%s: new join w/source", __func__);
2089 goto out_in6p_locked;
2092 CTR2(
KTR_MLD,
"%s: %s source", __func__,
"allow");
2094 lims =
im6f_graft(imf, MCAST_INCLUDE, &ssa->sin6);
2096 CTR1(
KTR_MLD,
"%s: merge imf state failed",
2099 goto out_in6p_locked;
2104 CTR1(
KTR_MLD,
"%s: new join w/o source", __func__);
2108 goto out_in6p_locked;
2124 if (in_pcbrele_wlocked(inp)) {
2126 goto out_in6p_unlocked;
2129 goto out_in6p_locked;
2137 CTR1(
KTR_MLD,
"%s: merge inm state", __func__);
2141 CTR1(
KTR_MLD,
"%s: failed to merge inm state",
2146 goto out_in6p_locked;
2148 CTR1(
KTR_MLD,
"%s: doing mld downcall", __func__);
2153 CTR1(
KTR_MLD,
"%s: failed mld downcall",
2157 goto out_in6p_locked;
2169 if (is_new && imf) {
2171 struct in6_multi_head inmh;
2174 SLIST_INSERT_HEAD(&inmh, imf->
im6f_in6m, in6m_defer);
2189 struct group_source_req gsr;
2190 struct epoch_tracker et;
2194 struct ip6_moptions *imo;
2209 memset(&gsr, 0,
sizeof(
struct group_source_req));
2211 gsa->ss.ss_family = AF_UNSPEC;
2213 ssa->ss.ss_family = AF_UNSPEC;
2222 switch (sopt->sopt_name) {
2224 error = sooptcopyin(sopt, &mreq,
sizeof(
struct ipv6_mreq),
2228 gsa->sin6.sin6_family = AF_INET6;
2231 gsa->sin6.sin6_port = 0;
2232 gsa->sin6.sin6_scope_id = 0;
2236 case MCAST_LEAVE_GROUP:
2237 case MCAST_LEAVE_SOURCE_GROUP:
2238 if (sopt->sopt_name == MCAST_LEAVE_GROUP) {
2239 error = sooptcopyin(sopt, &gsr,
2240 sizeof(
struct group_req),
2241 sizeof(
struct group_req));
2242 }
else if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) {
2243 error = sooptcopyin(sopt, &gsr,
2244 sizeof(
struct group_source_req),
2245 sizeof(
struct group_source_req));
2250 if (gsa->sin6.sin6_family != AF_INET6 ||
2253 if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) {
2254 if (ssa->sin6.sin6_family != AF_INET6 ||
2266 gsa->sin6.sin6_port = 0;
2267 gsa->sin6.sin6_scope_id = 0;
2268 ifindex = gsr.gsr_interface;
2272 CTR2(
KTR_MLD,
"%s: unknown sopt_name %d",
2273 __func__, sopt->sopt_name);
2274 return (EOPNOTSUPP);
2289 NET_EPOCH_ENTER(et);
2290 ifp = ifnet_byindex(ifindex);
2293 return (EADDRNOTAVAIL);
2298 return (EADDRNOTAVAIL);
2309 ifindex = ntohs(gsa->sin6.sin6_addr.s6_addr16[1]);
2311 CTR2(
KTR_MLD,
"%s: warning: no ifindex, looking up "
2312 "ifp for group %s.", __func__,
2316 NET_EPOCH_ENTER(et);
2317 ifp = ifnet_byindex(ifindex);
2321 return (EADDRNOTAVAIL);
2324 CTR2(
KTR_MLD,
"%s: ifp = %p", __func__, ifp);
2325 KASSERT(ifp != NULL, (
"%s: ifp did not resolve", __func__));
2335 error = EADDRNOTAVAIL;
2336 goto out_in6p_locked;
2340 if (ssa->ss.ss_family != AF_UNSPEC)
2346 INP_WLOCK_ASSERT(inp);
2362 if (imf->
im6f_st[0] == MCAST_EXCLUDE) {
2363 error = EADDRNOTAVAIL;
2364 goto out_in6p_locked;
2368 CTR3(
KTR_MLD,
"%s: source %p %spresent", __func__,
2371 error = EADDRNOTAVAIL;
2372 goto out_in6p_locked;
2374 CTR2(
KTR_MLD,
"%s: %s source", __func__,
"block");
2377 CTR1(
KTR_MLD,
"%s: merge imf state failed",
2379 goto out_in6p_locked;
2387 CTR1(
KTR_MLD,
"%s: merge inm state", __func__);
2391 CTR1(
KTR_MLD,
"%s: failed to merge inm state",
2396 goto out_in6p_locked;
2399 CTR1(
KTR_MLD,
"%s: doing mld downcall", __func__);
2403 CTR1(
KTR_MLD,
"%s: failed mld downcall",
2407 goto out_in6p_locked;
2417 if (is_final && imf)
2435 struct epoch_tracker et;
2437 struct ip6_moptions *imo;
2441 if (sopt->sopt_valsize !=
sizeof(u_int))
2444 error = sooptcopyin(sopt, &ifindex,
sizeof(u_int),
sizeof(u_int));
2447 NET_EPOCH_ENTER(et);
2451 ifp = ifnet_byindex(ifindex);
2452 if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
2454 return (EADDRNOTAVAIL);
2459 imo->im6o_multicast_ifp = ifp;
2473 struct __msfilterreq msfr;
2474 struct epoch_tracker et;
2478 struct ip6_moptions *imo;
2482 error = sooptcopyin(sopt, &msfr,
sizeof(
struct __msfilterreq),
2483 sizeof(
struct __msfilterreq));
2490 if (msfr.msfr_fmode != MCAST_EXCLUDE &&
2491 msfr.msfr_fmode != MCAST_INCLUDE)
2494 if (msfr.msfr_group.ss_family != AF_INET6 ||
2502 gsa->sin6.sin6_port = 0;
2504 NET_EPOCH_ENTER(et);
2505 ifp = ifnet_byindex(msfr.msfr_ifindex);
2508 return (EADDRNOTAVAIL);
2518 error = EADDRNOTAVAIL;
2519 goto out_in6p_locked;
2526 INP_WLOCK_ASSERT(inp);
2528 imf->
im6f_st[1] = msfr.msfr_fmode;
2536 if (msfr.msfr_nsrcs > 0) {
2539 struct sockaddr_storage *kss, *pkss;
2544 CTR2(
KTR_MLD,
"%s: loading %lu source list entries",
2545 __func__, (
unsigned long)msfr.msfr_nsrcs);
2546 kss = malloc(
sizeof(
struct sockaddr_storage) * msfr.msfr_nsrcs,
2548 error = copyin(msfr.msfr_srcs, kss,
2549 sizeof(
struct sockaddr_storage) * msfr.msfr_nsrcs);
2563 imf->
im6f_st[1] = msfr.msfr_fmode;
2576 for (i = 0, pkss = kss; i < msfr.msfr_nsrcs; i++, pkss++) {
2579 error = EAFNOSUPPORT;
2605 goto out_im6f_rollback;
2607 INP_WLOCK_ASSERT(inp);
2613 CTR1(
KTR_MLD,
"%s: merge inm state", __func__);
2616 CTR1(
KTR_MLD,
"%s: failed to merge inm state", __func__);
2618 CTR1(
KTR_MLD,
"%s: doing mld downcall", __func__);
2621 CTR1(
KTR_MLD,
"%s: failed mld downcall", __func__);
2653 struct ip6_moptions *im6o;
2662 if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT ||
2663 (inp->inp_socket->so_proto->pr_type != SOCK_RAW &&
2664 inp->inp_socket->so_proto->pr_type != SOCK_DGRAM))
2665 return (EOPNOTSUPP);
2667 switch (sopt->sopt_name) {
2675 if (sopt->sopt_valsize !=
sizeof(
int)) {
2679 error = sooptcopyin(sopt, &hlim,
sizeof(hlim),
sizeof(
int));
2682 if (hlim < -1 || hlim > 255) {
2685 }
else if (hlim == -1) {
2689 im6o->im6o_multicast_hlim = hlim;
2701 if (sopt->sopt_valsize !=
sizeof(u_int)) {
2705 error = sooptcopyin(sopt, &loop,
sizeof(u_int),
sizeof(u_int));
2713 im6o->im6o_multicast_loop = loop;
2719 case MCAST_JOIN_GROUP:
2720 case MCAST_JOIN_SOURCE_GROUP:
2725 case MCAST_LEAVE_GROUP:
2726 case MCAST_LEAVE_SOURCE_GROUP:
2730 case MCAST_BLOCK_SOURCE:
2731 case MCAST_UNBLOCK_SOURCE:
2744 INP_UNLOCK_ASSERT(inp);
2762 struct epoch_tracker et;
2764 struct ifmultiaddr *ifma;
2770 uint32_t fmode, ifindex;
2778 if (req->newptr != NULL)
2785 memcpy(&mcaddr, &name[1],
sizeof(
struct in6_addr));
2787 CTR2(
KTR_MLD,
"%s: group %s is not multicast",
2793 NET_EPOCH_ENTER(et);
2794 ifp = ifnet_byindex(ifindex);
2797 CTR2(
KTR_MLD,
"%s: no ifp for ifindex %u",
2806 retval = sysctl_wire_old_buffer(req,
2815 CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
2822 retval = SYSCTL_OUT(req, &fmode,
sizeof(uint32_t));
2825 RB_FOREACH(ims, ip6_msource_tree, &inm->
in6m_srcs) {
2826 CTR2(
KTR_MLD,
"%s: visit node %p", __func__, ims);
2831 CTR1(
KTR_MLD,
"%s: skip non-in-mode",
2836 retval = SYSCTL_OUT(req, &src,
2851static const char *in6m_modestrs[] = {
"un",
"in",
"ex" };
2854in6m_mode_str(
const int mode)
2857 if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE)
2858 return (in6m_modestrs[mode]);
2862static const char *in6m_statestrs[] = {
2875in6m_state_str(
const int state)
2879 return (in6m_statestrs[state]);
2892 if ((ktr_mask &
KTR_MLD) == 0)
2895 printf(
"%s: --- begin in6m %p ---\n", __func__, inm);
2896 printf(
"addr %s ifp %p(%s) ifma %p\n",
2901 printf(
"timer %u state %s refcount %u scq.len %u\n",
2906 printf(
"mli %p nsrc %lu sctimer %u scrv %u\n",
2911 for (t = 0; t < 2; t++) {
2912 printf(
"t%d: fmode %s asm %u ex %u in %u rec %u\n", t,
2919 printf(
"%s: --- end in6m %p ---\n", __func__, inm);
char * ip6_sprintf(char *ip6buf, const struct in6_addr *addr)
#define IPV6_MAX_MEMBERSHIPS
#define IPV6_MULTICAST_LOOP
#define IPV6_DEFAULT_MULTICAST_LOOP
#define IN6_IS_ADDR_MC_LINKLOCAL(a)
#define IPV6_MAX_SOCK_SRC_FILTER
#define IPV6_MULTICAST_HOPS
#define IN6_ARE_ADDR_EQUAL(a, b)
#define IPV6_MAX_GROUP_SRC_FILTER
#define IN6_IS_ADDR_MULTICAST(a)
#define IN6_IS_ADDR_MC_INTFACELOCAL(a)
#define IPV6_MULTICAST_IF
struct nhop_object * fib6_lookup(uint32_t fibnum, const struct in6_addr *dst6, uint32_t scopeid, uint32_t flags, uint32_t flowid)
void in6m_release_wait(void *arg __unused)
static void im6f_commit(struct in6_mfilter *)
static MALLOC_DEFINE(M_IN6MFILTER, "in6_mfilter", "IPv6 multicast PCB-layer source filter")
void in6m_clear_recorded(struct in6_multi *inm)
static struct ip6_moptions * in6p_findmoptions(struct inpcb *)
static int in6_getmulti(struct ifnet *, const struct in6_addr *, struct in6_multi **)
TASKQUEUE_DEFINE_THREAD(in6m_free)
SYSCTL_DECL(_net_inet6_ip6)
int ip6_getmoptions(struct inpcb *inp, struct sockopt *sopt)
int in6_leavegroup(struct in6_multi *inm, struct in6_mfilter *imf)
SYSCTL_ULONG(_net_inet6_ip6_mcast, OID_AUTO, maxgrpsrc, CTLFLAG_RWTUN, &in6_mcast_maxgrpsrc, 0, "Max source filters per group")
int ip6_setmoptions(struct inpcb *inp, struct sockopt *sopt)
static struct in6_msource * im6o_match_source(struct in6_mfilter *, const struct sockaddr *)
RB_GENERATE(ip6_msource_tree, ip6_msource, im6s_link, ip6_msource_cmp)
static int in6m_merge(struct in6_multi *, struct in6_mfilter *)
static int in6p_block_unblock_source(struct inpcb *, struct sockopt *)
static int in6p_set_multicast_if(struct inpcb *, struct sockopt *)
static struct in6_mfilter * im6o_match_group(const struct ip6_moptions *, const struct ifnet *, const struct sockaddr *)
static void in6m_release_task(void *arg __unused, int pending __unused)
void in6m_disconnect_locked(struct in6_multi_head *inmh, struct in6_multi *inm)
void ip6_freemoptions(struct ip6_moptions *imo)
SYSCTL_INT(_net_inet6_ip6_mcast, OID_AUTO, loop, CTLFLAG_RWTUN, &in6_mcast_loop, 0, "Loopback multicast datagrams by default")
static void im6f_purge(struct in6_mfilter *)
static void in6m_purge(struct in6_multi *)
int in6m_record_source(struct in6_multi *inm, const struct in6_addr *addr)
static int im6f_prune(struct in6_mfilter *, const struct sockaddr_in6 *)
static void im6f_reap(struct in6_mfilter *)
static int sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS)
static struct in6_multi_head in6m_free_list
static int in6m_get_source(struct in6_multi *inm, const struct in6_addr *addr, const int noalloc, struct ip6_msource **pims)
static void im6f_rollback(struct in6_mfilter *)
static void im6f_leave(struct in6_mfilter *)
static void in6m_reap(struct in6_multi *)
static struct in6_msource * im6f_graft(struct in6_mfilter *, const uint8_t, const struct sockaddr_in6 *)
static __inline void im6f_init(struct in6_mfilter *imf, const int st0, const int st1)
static u_long in6_mcast_maxsocksrc
struct in6_mfilter * ip6_mfilter_alloc(const int mflags, const int st0, const int st1)
static u_long in6_mcast_maxgrpsrc
static int in6_joingroup_locked(struct ifnet *, const struct in6_addr *, struct in6_mfilter *, struct in6_multi **, int)
static SYSCTL_NODE(_net_inet6_ip6, OID_AUTO, mcast, CTLFLAG_RW|CTLFLAG_MPSAFE, 0, "IPv6 multicast")
struct mtx in6_multi_free_mtx
int in6_leavegroup_locked(struct in6_multi *inm, struct in6_mfilter *imf)
static void inp_gcmoptions(struct ip6_moptions *imo)
static int in6p_leave_group(struct inpcb *, struct sockopt *)
static void im6s_merge(struct ip6_msource *ims, const struct in6_msource *lims, const int rollback)
int im6o_mc_filter(const struct ip6_moptions *imo, const struct ifnet *ifp, const struct sockaddr *group, const struct sockaddr *src)
static int im6f_get_source(struct in6_mfilter *imf, const struct sockaddr_in6 *psin, struct in6_msource **)
MTX_SYSINIT(in6_multi_mtx, &in6_multi_list_mtx, "in6_multi_list_mtx", MTX_DEF)
void in6m_release_list_deferred(struct in6_multi_head *inmh)
static struct task in6m_free_task
void ip6_mfilter_free(struct in6_mfilter *imf)
struct mtx in6_multi_list_mtx
void in6m_print(const struct in6_multi *inm)
SX_SYSINIT(in6_multi_sx, &in6_multi_sx, "in6_multi_sx")
static void in6m_release(struct in6_multi *inm)
static int in6p_set_source_filters(struct inpcb *, struct sockopt *)
int in6_joingroup(struct ifnet *ifp, const struct in6_addr *mcaddr, struct in6_mfilter *imf, struct in6_multi **pinm, const int delay)
void in6m_commit(struct in6_multi *inm)
static int in6p_get_source_filters(struct inpcb *, struct sockopt *)
static int in6p_join_group(struct inpcb *, struct sockopt *)
static struct ifnet * in6p_lookup_mcast_ifp(const struct inpcb *, const struct sockaddr_in6 *)
#define IN6_MULTI_LOCK_ASSERT()
#define IN6_MULTI_LIST_UNLOCK_ASSERT()
static __inline void in6m_acquire_locked(struct in6_multi *inm)
static __inline void in6m_rele_locked(struct in6_multi_head *inmh, struct in6_multi *inm)
static void ip6_mfilter_remove(struct ip6_mfilter_head *head, struct in6_mfilter *imf)
#define IN6_MULTI_LIST_LOCK()
static void ip6_mfilter_insert(struct ip6_mfilter_head *head, struct in6_mfilter *imf)
#define IP6_MFILTER_FOREACH(imf, head)
static __inline struct in6_multi * in6m_ifmultiaddr_get_inm(struct ifmultiaddr *ifma)
static __inline struct in6_multi * in6m_lookup_locked(struct ifnet *ifp, const struct in6_addr *mcaddr)
#define IN6_MULTI_UNLOCK()
#define IN6_MULTI_LIST_UNLOCK()
#define IN6_MULTI_LIST_LOCK_ASSERT()
static size_t ip6_mfilter_count(struct ip6_mfilter_head *head)
static struct in6_mfilter * ip6_mfilter_first(const struct ip6_mfilter_head *head)
static __inline int ip6_msource_cmp(const struct ip6_msource *a, const struct ip6_msource *b)
static __inline uint8_t im6s_get_mode(const struct in6_multi *inm, const struct ip6_msource *ims, uint8_t t)
#define V_ip6_defmcasthlim
#define V_ip6_use_defzone
int mld_change_state(struct in6_multi *inm, const int delay)
VNET_SYSUNINIT(vnet_mld_uninit, SI_SUB_PROTO_MC, SI_ORDER_ANY, vnet_mld_uninit, NULL)
#define MLD_MAX_STATE_CHANGES
#define MLD_LEAVING_MEMBER
int in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id)
void in6_splitscope(const struct in6_addr *src, struct in6_addr *dst, uint32_t *scopeid)
int in6_clearscope(struct in6_addr *in6)
int sa6_embedscope(struct sockaddr_in6 *sin6, int defaultok)
struct in6_multi * im6f_in6m
struct ip6_msource_tree im6f_sources
struct in6_addr im6s_addr
struct in6_multi * i6mm_maddr
struct mld_ifsoftc * in6m_mli
struct in6_addr in6m_addr
struct ifmultiaddr * in6m_ifma
struct ip6_msource_tree in6m_srcs
struct in6_multi::in6m_st in6m_st[2]
struct ip6_msource::im6s_st im6s_st[2]
struct in6_addr im6s_addr
struct in6_addr ipv6mr_multiaddr
unsigned int ipv6mr_interface
struct in6_addr sin6_addr
struct sockaddr_storage ss