Merge final round of MLD changes from p4:
ip6_input.c, in6.h: * Add netinet6-specific mbuf flag M_RTALERT_MLD, shadowing M_PROTO6. * Always set this flag if HBH Router Alert option is present for MLD, even when not forwarding. icmp6.c: * In icmp6_input(), spell m->m_pkthdr.rcvif as ifp to be consistent. * Use scope ID for verifying input. Do not apply SSM filters here, no inpcb. * Check for M_RTALERT_MLD when validating MLD traffic, as we can't see IPv6 hop options outside of ip6_input(). in6_mcast.c: * Use KAME scope/zone ID in in6_multi. * Update net.inet6.ip6.mcast.filters implementation to use scope IDs for comparisons. * Fix scope ID treatment in multicast socket option processing. Scope IDs passed in from userland will be ignored as other less ambiguous APIs exist for specifying the link. * Tighten userland input checks in IPv6 SSM delta and full-state ops. * Source filter embedded scope IDs need to be revisited, for now just clear them and ignore them on input. * Adapt KAME behaviour of looking up the scope ID in the default zone for multicast leaves, when the interface is ambiguous. mld6.c: * Tighten origin checks on MLD traffic as per RFC3810 Section 6.2: * ip6_src MAY be the unspecified address for MLDv1 reports. * ip6_src MAY have link-local address scope for MLDv1 reports, MLDv1 queries, and MLDv2 queries. * Perform address field validation *before* accepting queries. * Use KAME scope/zone ID in query/report processing. * Break const correctness for mld_v1_input_report(), mld_v1_input_query() as we temporarily modify the input mbuf chain. * Clear the scope ID before handoff to userland MLD daemon. * Fix MLDv1 old querier present timer processing. With the protocol defaults, hosts should revert to MLDv2 after 260s. * Add net.inet6.mld.v1enable sysctl, default to on. ifmcstat.c: * Use sysctl by default; -K requests kvm(3) if so compiled. mld.4: * Connect man page to build. Tested using PCS.
This commit is contained in:
parent
22a91927b3
commit
29dc7bc636
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=192923
@ -191,6 +191,7 @@ MAN= aac.4 \
|
|||||||
meteor.4 \
|
meteor.4 \
|
||||||
mfi.4 \
|
mfi.4 \
|
||||||
miibus.4 \
|
miibus.4 \
|
||||||
|
mld.4 \
|
||||||
mlx.4 \
|
mlx.4 \
|
||||||
mly.4 \
|
mly.4 \
|
||||||
mmc.4 \
|
mmc.4 \
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd February 13, 2009
|
.Dd May 27, 2009
|
||||||
.Dt MULTICAST 4
|
.Dt MULTICAST 4
|
||||||
.Os
|
.Os
|
||||||
.\"
|
.\"
|
||||||
@ -962,6 +962,7 @@ after the previous upcall.
|
|||||||
.Xr intro 4 ,
|
.Xr intro 4 ,
|
||||||
.Xr ip 4 ,
|
.Xr ip 4 ,
|
||||||
.Xr ip6 4 ,
|
.Xr ip6 4 ,
|
||||||
|
.Xr mld 4 ,
|
||||||
.Xr pim 4
|
.Xr pim 4
|
||||||
.\"
|
.\"
|
||||||
.Sh HISTORY
|
.Sh HISTORY
|
||||||
@ -1002,6 +1003,8 @@ monitoring were implemented by
|
|||||||
in collaboration with
|
in collaboration with
|
||||||
.An Chris Brown
|
.An Chris Brown
|
||||||
(NextHop).
|
(NextHop).
|
||||||
|
The IGMPv3 and MLDv2 multicast support was implemented by
|
||||||
|
.An Bruce Simpson .
|
||||||
.Pp
|
.Pp
|
||||||
This manual page was written by
|
This manual page was written by
|
||||||
.An Pavlin Radoslavov
|
.An Pavlin Radoslavov
|
||||||
|
@ -403,6 +403,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
|||||||
INIT_VNET_INET6(curvnet);
|
INIT_VNET_INET6(curvnet);
|
||||||
INIT_VPROCG(TD_TO_VPROCG(curthread)); /* XXX V_hostname needs this */
|
INIT_VPROCG(TD_TO_VPROCG(curthread)); /* XXX V_hostname needs this */
|
||||||
struct mbuf *m = *mp, *n;
|
struct mbuf *m = *mp, *n;
|
||||||
|
struct ifnet *ifp;
|
||||||
struct ip6_hdr *ip6, *nip6;
|
struct ip6_hdr *ip6, *nip6;
|
||||||
struct icmp6_hdr *icmp6, *nicmp6;
|
struct icmp6_hdr *icmp6, *nicmp6;
|
||||||
int off = *offp;
|
int off = *offp;
|
||||||
@ -410,6 +411,8 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
|||||||
int code, sum, noff;
|
int code, sum, noff;
|
||||||
char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN];
|
char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN];
|
||||||
|
|
||||||
|
ifp = m->m_pkthdr.rcvif;
|
||||||
|
|
||||||
#ifndef PULLDOWN_TEST
|
#ifndef PULLDOWN_TEST
|
||||||
IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), IPPROTO_DONE);
|
IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), IPPROTO_DONE);
|
||||||
/* m might change if M_LOOP. So, call mtod after this */
|
/* m might change if M_LOOP. So, call mtod after this */
|
||||||
@ -431,10 +434,8 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
|||||||
* Note: SSM filters are not applied for ICMPv6 traffic.
|
* Note: SSM filters are not applied for ICMPv6 traffic.
|
||||||
*/
|
*/
|
||||||
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
|
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
|
||||||
struct ifnet *ifp;
|
struct in6_multi *inm;
|
||||||
struct in6_multi *inm;
|
|
||||||
|
|
||||||
ifp = m->m_pkthdr.rcvif;
|
|
||||||
inm = in6m_lookup(ifp, &ip6->ip6_dst);
|
inm = in6m_lookup(ifp, &ip6->ip6_dst);
|
||||||
if (inm == NULL) {
|
if (inm == NULL) {
|
||||||
IP6STAT_INC(ip6s_notmember);
|
IP6STAT_INC(ip6s_notmember);
|
||||||
@ -483,19 +484,19 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ICMP6STAT_INC(icp6s_inhist[icmp6->icmp6_type]);
|
ICMP6STAT_INC(icp6s_inhist[icmp6->icmp6_type]);
|
||||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_msg);
|
icmp6_ifstat_inc(ifp, ifs6_in_msg);
|
||||||
if (icmp6->icmp6_type < ICMP6_INFOMSG_MASK)
|
if (icmp6->icmp6_type < ICMP6_INFOMSG_MASK)
|
||||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error);
|
icmp6_ifstat_inc(ifp, ifs6_in_error);
|
||||||
|
|
||||||
switch (icmp6->icmp6_type) {
|
switch (icmp6->icmp6_type) {
|
||||||
case ICMP6_DST_UNREACH:
|
case ICMP6_DST_UNREACH:
|
||||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_dstunreach);
|
icmp6_ifstat_inc(ifp, ifs6_in_dstunreach);
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case ICMP6_DST_UNREACH_NOROUTE:
|
case ICMP6_DST_UNREACH_NOROUTE:
|
||||||
code = PRC_UNREACH_NET;
|
code = PRC_UNREACH_NET;
|
||||||
break;
|
break;
|
||||||
case ICMP6_DST_UNREACH_ADMIN:
|
case ICMP6_DST_UNREACH_ADMIN:
|
||||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_adminprohib);
|
icmp6_ifstat_inc(ifp, ifs6_in_adminprohib);
|
||||||
code = PRC_UNREACH_PROTOCOL; /* is this a good code? */
|
code = PRC_UNREACH_PROTOCOL; /* is this a good code? */
|
||||||
break;
|
break;
|
||||||
case ICMP6_DST_UNREACH_ADDR:
|
case ICMP6_DST_UNREACH_ADDR:
|
||||||
@ -515,7 +516,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ICMP6_PACKET_TOO_BIG:
|
case ICMP6_PACKET_TOO_BIG:
|
||||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_pkttoobig);
|
icmp6_ifstat_inc(ifp, ifs6_in_pkttoobig);
|
||||||
|
|
||||||
/* validation is made in icmp6_mtudisc_update */
|
/* validation is made in icmp6_mtudisc_update */
|
||||||
|
|
||||||
@ -529,7 +530,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ICMP6_TIME_EXCEEDED:
|
case ICMP6_TIME_EXCEEDED:
|
||||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_timeexceed);
|
icmp6_ifstat_inc(ifp, ifs6_in_timeexceed);
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case ICMP6_TIME_EXCEED_TRANSIT:
|
case ICMP6_TIME_EXCEED_TRANSIT:
|
||||||
code = PRC_TIMXCEED_INTRANS;
|
code = PRC_TIMXCEED_INTRANS;
|
||||||
@ -544,7 +545,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ICMP6_PARAM_PROB:
|
case ICMP6_PARAM_PROB:
|
||||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_paramprob);
|
icmp6_ifstat_inc(ifp, ifs6_in_paramprob);
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case ICMP6_PARAMPROB_NEXTHEADER:
|
case ICMP6_PARAMPROB_NEXTHEADER:
|
||||||
code = PRC_UNREACH_PROTOCOL;
|
code = PRC_UNREACH_PROTOCOL;
|
||||||
@ -560,7 +561,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ICMP6_ECHO_REQUEST:
|
case ICMP6_ECHO_REQUEST:
|
||||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echo);
|
icmp6_ifstat_inc(ifp, ifs6_in_echo);
|
||||||
if (code != 0)
|
if (code != 0)
|
||||||
goto badcode;
|
goto badcode;
|
||||||
if ((n = m_copy(m, 0, M_COPYALL)) == NULL) {
|
if ((n = m_copy(m, 0, M_COPYALL)) == NULL) {
|
||||||
@ -623,7 +624,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ICMP6_ECHO_REPLY:
|
case ICMP6_ECHO_REPLY:
|
||||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echoreply);
|
icmp6_ifstat_inc(ifp, ifs6_in_echoreply);
|
||||||
if (code != 0)
|
if (code != 0)
|
||||||
goto badcode;
|
goto badcode;
|
||||||
break;
|
break;
|
||||||
@ -633,11 +634,15 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
|||||||
case MLD_LISTENER_DONE:
|
case MLD_LISTENER_DONE:
|
||||||
case MLDV2_LISTENER_REPORT:
|
case MLDV2_LISTENER_REPORT:
|
||||||
/*
|
/*
|
||||||
* Drop MLD traffic which is not link-local.
|
* Drop MLD traffic which is not link-local, has a hop limit
|
||||||
|
* of greater than 1 hop, or which does not have the
|
||||||
|
* IPv6 HBH Router Alert option.
|
||||||
|
* As IPv6 HBH options are stripped in ip6_input() we must
|
||||||
|
* check an mbuf header flag.
|
||||||
* XXX Should we also sanity check that these messages
|
* XXX Should we also sanity check that these messages
|
||||||
* were directed to a link-local multicast prefix?
|
* were directed to a link-local multicast prefix?
|
||||||
*/
|
*/
|
||||||
if (ip6->ip6_hlim != 1)
|
if ((ip6->ip6_hlim != 1) || (m->m_flags & M_RTALERT_MLD) == 0)
|
||||||
goto freeit;
|
goto freeit;
|
||||||
if (mld_input(m, off, icmp6len) != 0)
|
if (mld_input(m, off, icmp6len) != 0)
|
||||||
return (IPPROTO_DONE);
|
return (IPPROTO_DONE);
|
||||||
@ -748,7 +753,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ND_ROUTER_SOLICIT:
|
case ND_ROUTER_SOLICIT:
|
||||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routersolicit);
|
icmp6_ifstat_inc(ifp, ifs6_in_routersolicit);
|
||||||
if (code != 0)
|
if (code != 0)
|
||||||
goto badcode;
|
goto badcode;
|
||||||
if (icmp6len < sizeof(struct nd_router_solicit))
|
if (icmp6len < sizeof(struct nd_router_solicit))
|
||||||
@ -764,7 +769,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ND_ROUTER_ADVERT:
|
case ND_ROUTER_ADVERT:
|
||||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routeradvert);
|
icmp6_ifstat_inc(ifp, ifs6_in_routeradvert);
|
||||||
if (code != 0)
|
if (code != 0)
|
||||||
goto badcode;
|
goto badcode;
|
||||||
if (icmp6len < sizeof(struct nd_router_advert))
|
if (icmp6len < sizeof(struct nd_router_advert))
|
||||||
@ -780,7 +785,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ND_NEIGHBOR_SOLICIT:
|
case ND_NEIGHBOR_SOLICIT:
|
||||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighborsolicit);
|
icmp6_ifstat_inc(ifp, ifs6_in_neighborsolicit);
|
||||||
if (code != 0)
|
if (code != 0)
|
||||||
goto badcode;
|
goto badcode;
|
||||||
if (icmp6len < sizeof(struct nd_neighbor_solicit))
|
if (icmp6len < sizeof(struct nd_neighbor_solicit))
|
||||||
@ -796,7 +801,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ND_NEIGHBOR_ADVERT:
|
case ND_NEIGHBOR_ADVERT:
|
||||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighboradvert);
|
icmp6_ifstat_inc(ifp, ifs6_in_neighboradvert);
|
||||||
if (code != 0)
|
if (code != 0)
|
||||||
goto badcode;
|
goto badcode;
|
||||||
if (icmp6len < sizeof(struct nd_neighbor_advert))
|
if (icmp6len < sizeof(struct nd_neighbor_advert))
|
||||||
@ -812,7 +817,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ND_REDIRECT:
|
case ND_REDIRECT:
|
||||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_redirect);
|
icmp6_ifstat_inc(ifp, ifs6_in_redirect);
|
||||||
if (code != 0)
|
if (code != 0)
|
||||||
goto badcode;
|
goto badcode;
|
||||||
if (icmp6len < sizeof(struct nd_redirect))
|
if (icmp6len < sizeof(struct nd_redirect))
|
||||||
@ -840,7 +845,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
|||||||
"icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%d)\n",
|
"icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%d)\n",
|
||||||
icmp6->icmp6_type, ip6_sprintf(ip6bufs, &ip6->ip6_src),
|
icmp6->icmp6_type, ip6_sprintf(ip6bufs, &ip6->ip6_src),
|
||||||
ip6_sprintf(ip6bufd, &ip6->ip6_dst),
|
ip6_sprintf(ip6bufd, &ip6->ip6_dst),
|
||||||
m->m_pkthdr.rcvif ? m->m_pkthdr.rcvif->if_index : 0));
|
ifp ? ifp->if_index : 0));
|
||||||
if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST) {
|
if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST) {
|
||||||
/* ICMPv6 error: MUST deliver it by spec... */
|
/* ICMPv6 error: MUST deliver it by spec... */
|
||||||
code = PRC_NCMDS;
|
code = PRC_NCMDS;
|
||||||
|
@ -619,6 +619,7 @@ struct ip6_mtuinfo {
|
|||||||
#define M_DECRYPTED M_PROTO3
|
#define M_DECRYPTED M_PROTO3
|
||||||
#define M_LOOP M_PROTO4
|
#define M_LOOP M_PROTO4
|
||||||
#define M_AUTHIPDGM M_PROTO5
|
#define M_AUTHIPDGM M_PROTO5
|
||||||
|
#define M_RTALERT_MLD M_PROTO6
|
||||||
|
|
||||||
#ifdef _KERNEL
|
#ifdef _KERNEL
|
||||||
struct cmsghdr;
|
struct cmsghdr;
|
||||||
|
@ -305,6 +305,10 @@ im6o_match_group(const struct ip6_moptions *imo, const struct ifnet *ifp,
|
|||||||
* Find an IPv6 multicast source entry for this imo which matches
|
* Find an IPv6 multicast source entry for this imo which matches
|
||||||
* the given group index for this socket, and source address.
|
* the given group index for this socket, and source address.
|
||||||
*
|
*
|
||||||
|
* XXX TODO: The scope ID, if present in src, is stripped before
|
||||||
|
* any comparison. We SHOULD enforce scope/zone checks where the source
|
||||||
|
* filter entry has a link scope.
|
||||||
|
*
|
||||||
* NOTE: This does not check if the entry is in-mode, merely if
|
* NOTE: This does not check if the entry is in-mode, merely if
|
||||||
* it exists, which may not be the desired behaviour.
|
* it exists, which may not be the desired behaviour.
|
||||||
*/
|
*/
|
||||||
@ -328,6 +332,7 @@ im6o_match_source(const struct ip6_moptions *imo, const size_t gidx,
|
|||||||
|
|
||||||
psa = (const sockunion_t *)src;
|
psa = (const sockunion_t *)src;
|
||||||
find.im6s_addr = psa->sin6.sin6_addr;
|
find.im6s_addr = psa->sin6.sin6_addr;
|
||||||
|
in6_clearscope(&find.im6s_addr); /* XXX */
|
||||||
ims = RB_FIND(ip6_msource_tree, &imf->im6f_sources, &find);
|
ims = RB_FIND(ip6_msource_tree, &imf->im6f_sources, &find);
|
||||||
|
|
||||||
return ((struct in6_msource *)ims);
|
return ((struct in6_msource *)ims);
|
||||||
@ -1159,6 +1164,20 @@ in6_mc_join_locked(struct ifnet *ifp, const struct in6_addr *mcaddr,
|
|||||||
char ip6tbuf[INET6_ADDRSTRLEN];
|
char ip6tbuf[INET6_ADDRSTRLEN];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef INVARIANTS
|
||||||
|
/*
|
||||||
|
* Sanity: Check scope zone ID was set for ifp, if and
|
||||||
|
* only if group is scoped to an interface.
|
||||||
|
*/
|
||||||
|
KASSERT(IN6_IS_ADDR_MULTICAST(mcaddr),
|
||||||
|
("%s: not a multicast address", __func__));
|
||||||
|
if (IN6_IS_ADDR_MC_LINKLOCAL(mcaddr) ||
|
||||||
|
IN6_IS_ADDR_MC_INTFACELOCAL(mcaddr)) {
|
||||||
|
KASSERT(mcaddr->s6_addr16[1] != 0,
|
||||||
|
("%s: scope zone ID not set", __func__));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
IN6_MULTI_LOCK_ASSERT();
|
IN6_MULTI_LOCK_ASSERT();
|
||||||
|
|
||||||
CTR4(KTR_MLD, "%s: join %s on %p(%s))", __func__,
|
CTR4(KTR_MLD, "%s: join %s on %p(%s))", __func__,
|
||||||
@ -1360,6 +1379,8 @@ in6p_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
|
|||||||
if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
|
if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
|
(void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if we are actually a member of this group.
|
* Check if we are actually a member of this group.
|
||||||
*/
|
*/
|
||||||
@ -1566,19 +1587,26 @@ in6p_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
|
|||||||
if (error)
|
if (error)
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
if (msfr.msfr_ifindex == 0 || V_if_index < msfr.msfr_ifindex)
|
if (msfr.msfr_group.ss_family != AF_INET6 ||
|
||||||
|
msfr.msfr_group.ss_len != sizeof(struct sockaddr_in6))
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
|
gsa = (sockunion_t *)&msfr.msfr_group;
|
||||||
|
if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
|
||||||
|
return (EINVAL);
|
||||||
|
|
||||||
|
if (msfr.msfr_ifindex == 0 || V_if_index < msfr.msfr_ifindex)
|
||||||
|
return (EADDRNOTAVAIL);
|
||||||
ifp = ifnet_byindex(msfr.msfr_ifindex);
|
ifp = ifnet_byindex(msfr.msfr_ifindex);
|
||||||
if (ifp == NULL)
|
if (ifp == NULL)
|
||||||
return (EINVAL);
|
return (EADDRNOTAVAIL);
|
||||||
|
(void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
|
||||||
|
|
||||||
INP_WLOCK(inp);
|
INP_WLOCK(inp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Lookup group on the socket.
|
* Lookup group on the socket.
|
||||||
*/
|
*/
|
||||||
gsa = (sockunion_t *)&msfr.msfr_group;
|
|
||||||
idx = im6o_match_group(imo, ifp, &gsa->sa);
|
idx = im6o_match_group(imo, ifp, &gsa->sa);
|
||||||
if (idx == -1 || imo->im6o_mfilters == NULL) {
|
if (idx == -1 || imo->im6o_mfilters == NULL) {
|
||||||
INP_WUNLOCK(inp);
|
INP_WUNLOCK(inp);
|
||||||
@ -1803,6 +1831,12 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
|
|||||||
ssa = (sockunion_t *)&gsr.gsr_source;
|
ssa = (sockunion_t *)&gsr.gsr_source;
|
||||||
ssa->ss.ss_family = AF_UNSPEC;
|
ssa->ss.ss_family = AF_UNSPEC;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chew everything into struct group_source_req.
|
||||||
|
* Overwrite the port field if present, as the sockaddr
|
||||||
|
* being copied in may be matched with a binary comparison.
|
||||||
|
* Ignore passed-in scope ID.
|
||||||
|
*/
|
||||||
switch (sopt->sopt_name) {
|
switch (sopt->sopt_name) {
|
||||||
case IPV6_JOIN_GROUP: {
|
case IPV6_JOIN_GROUP: {
|
||||||
struct ipv6_mreq mreq;
|
struct ipv6_mreq mreq;
|
||||||
@ -1846,16 +1880,20 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
|
|||||||
gsa->sin6.sin6_len != sizeof(struct sockaddr_in6))
|
gsa->sin6.sin6_len != sizeof(struct sockaddr_in6))
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
/*
|
|
||||||
* Overwrite the port field if present, as the sockaddr
|
|
||||||
* being copied in may be matched with a binary comparison.
|
|
||||||
*/
|
|
||||||
gsa->sin6.sin6_port = 0;
|
|
||||||
if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) {
|
if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) {
|
||||||
if (ssa->sin6.sin6_family != AF_INET6 ||
|
if (ssa->sin6.sin6_family != AF_INET6 ||
|
||||||
ssa->sin6.sin6_len != sizeof(struct sockaddr_in6))
|
ssa->sin6.sin6_len != sizeof(struct sockaddr_in6))
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
if (IN6_IS_ADDR_MULTICAST(&ssa->sin6.sin6_addr))
|
||||||
|
return (EINVAL);
|
||||||
|
/*
|
||||||
|
* TODO: Validate embedded scope ID in source
|
||||||
|
* list entry against passed-in ifp, if and only
|
||||||
|
* if source list filter entry is iface or node local.
|
||||||
|
*/
|
||||||
|
in6_clearscope(&ssa->sin6.sin6_addr);
|
||||||
ssa->sin6.sin6_port = 0;
|
ssa->sin6.sin6_port = 0;
|
||||||
|
ssa->sin6.sin6_scope_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface)
|
if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface)
|
||||||
@ -1870,34 +1908,22 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef notyet
|
|
||||||
/*
|
|
||||||
* FIXME: Check for unspecified address (all groups).
|
|
||||||
* Do we have a normative reference for this 'feature'?
|
|
||||||
*
|
|
||||||
* We use the unspecified address to specify to accept
|
|
||||||
* all multicast addresses. Only super user is allowed
|
|
||||||
* to do this.
|
|
||||||
* XXX-BZ might need a better PRIV_NETINET_x for this
|
|
||||||
*/
|
|
||||||
if (IN6_IS_ADDR_UNSPECIFIED(&gsa->sin6.sin6_addr)) {
|
|
||||||
error = priv_check(curthread, PRIV_NETINET_MROUTE);
|
|
||||||
if (error)
|
|
||||||
break;
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
|
if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0)
|
if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0)
|
||||||
return (EADDRNOTAVAIL);
|
return (EADDRNOTAVAIL);
|
||||||
|
|
||||||
#ifdef notyet
|
gsa->sin6.sin6_port = 0;
|
||||||
|
gsa->sin6.sin6_scope_id = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* FIXME: Set interface scope in group address.
|
* Always set the scope zone ID on memberships created from userland.
|
||||||
|
* Use the passed-in ifp to do this.
|
||||||
|
* XXX The in6_setscope() return value is meaningless.
|
||||||
|
* XXX SCOPE6_LOCK() is taken by in6_setscope().
|
||||||
*/
|
*/
|
||||||
(void)in6_setscope(&gsa->sin6.sin_addr, ifp, NULL);
|
(void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MCAST_JOIN_SOURCE on an exclusive membership is an error.
|
* MCAST_JOIN_SOURCE on an exclusive membership is an error.
|
||||||
@ -2031,6 +2057,8 @@ static int
|
|||||||
in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
|
in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
|
||||||
{
|
{
|
||||||
INIT_VNET_NET(curvnet);
|
INIT_VNET_NET(curvnet);
|
||||||
|
INIT_VNET_INET6(curvnet);
|
||||||
|
struct ipv6_mreq mreq;
|
||||||
struct group_source_req gsr;
|
struct group_source_req gsr;
|
||||||
sockunion_t *gsa, *ssa;
|
sockunion_t *gsa, *ssa;
|
||||||
struct ifnet *ifp;
|
struct ifnet *ifp;
|
||||||
@ -2038,6 +2066,7 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
|
|||||||
struct ip6_moptions *imo;
|
struct ip6_moptions *imo;
|
||||||
struct in6_msource *ims;
|
struct in6_msource *ims;
|
||||||
struct in6_multi *inm;
|
struct in6_multi *inm;
|
||||||
|
uint32_t ifindex;
|
||||||
size_t idx;
|
size_t idx;
|
||||||
int error, is_final;
|
int error, is_final;
|
||||||
#ifdef KTR
|
#ifdef KTR
|
||||||
@ -2045,6 +2074,7 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
ifp = NULL;
|
ifp = NULL;
|
||||||
|
ifindex = 0;
|
||||||
error = 0;
|
error = 0;
|
||||||
is_final = 1;
|
is_final = 1;
|
||||||
|
|
||||||
@ -2054,39 +2084,26 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
|
|||||||
ssa = (sockunion_t *)&gsr.gsr_source;
|
ssa = (sockunion_t *)&gsr.gsr_source;
|
||||||
ssa->ss.ss_family = AF_UNSPEC;
|
ssa->ss.ss_family = AF_UNSPEC;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chew everything passed in up into a struct group_source_req
|
||||||
|
* as that is easier to process.
|
||||||
|
* Note: Any embedded scope ID in the multicast group passed
|
||||||
|
* in by userland is ignored, the interface index is the recommended
|
||||||
|
* mechanism to specify an interface; see below.
|
||||||
|
*/
|
||||||
switch (sopt->sopt_name) {
|
switch (sopt->sopt_name) {
|
||||||
case IPV6_LEAVE_GROUP: {
|
case IPV6_LEAVE_GROUP:
|
||||||
struct ipv6_mreq mreq;
|
|
||||||
|
|
||||||
error = sooptcopyin(sopt, &mreq, sizeof(struct ipv6_mreq),
|
error = sooptcopyin(sopt, &mreq, sizeof(struct ipv6_mreq),
|
||||||
sizeof(struct ipv6_mreq));
|
sizeof(struct ipv6_mreq));
|
||||||
if (error)
|
if (error)
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
gsa->sin6.sin6_family = AF_INET6;
|
gsa->sin6.sin6_family = AF_INET6;
|
||||||
gsa->sin6.sin6_len = sizeof(struct sockaddr_in6);
|
gsa->sin6.sin6_len = sizeof(struct sockaddr_in6);
|
||||||
gsa->sin6.sin6_addr = mreq.ipv6mr_multiaddr;
|
gsa->sin6.sin6_addr = mreq.ipv6mr_multiaddr;
|
||||||
|
gsa->sin6.sin6_port = 0;
|
||||||
if (mreq.ipv6mr_interface == 0) {
|
gsa->sin6.sin6_scope_id = 0;
|
||||||
#ifdef notyet
|
ifindex = mreq.ipv6mr_interface;
|
||||||
/*
|
break;
|
||||||
* FIXME: Resolve scope ambiguity when interface
|
|
||||||
* index is unspecified.
|
|
||||||
*/
|
|
||||||
ifp = in6p_lookup_mcast_ifp(inp, &gsa->sin6);
|
|
||||||
#else
|
|
||||||
return (EADDRNOTAVAIL);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
if (mreq.ipv6mr_interface < 0 ||
|
|
||||||
V_if_index < mreq.ipv6mr_interface)
|
|
||||||
return (EADDRNOTAVAIL);
|
|
||||||
ifp = ifnet_byindex(mreq.ipv6mr_interface);
|
|
||||||
}
|
|
||||||
|
|
||||||
CTR3(KTR_MLD, "%s: ipv6mr_interface = %d, ifp = %p",
|
|
||||||
__func__, mreq.ipv6mr_interface, ifp);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case MCAST_LEAVE_GROUP:
|
case MCAST_LEAVE_GROUP:
|
||||||
case MCAST_LEAVE_SOURCE_GROUP:
|
case MCAST_LEAVE_SOURCE_GROUP:
|
||||||
@ -2105,17 +2122,22 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
|
|||||||
if (gsa->sin6.sin6_family != AF_INET6 ||
|
if (gsa->sin6.sin6_family != AF_INET6 ||
|
||||||
gsa->sin6.sin6_len != sizeof(struct sockaddr_in6))
|
gsa->sin6.sin6_len != sizeof(struct sockaddr_in6))
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) {
|
if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) {
|
||||||
if (ssa->sin6.sin6_family != AF_INET6 ||
|
if (ssa->sin6.sin6_family != AF_INET6 ||
|
||||||
ssa->sin6.sin6_len != sizeof(struct sockaddr_in6))
|
ssa->sin6.sin6_len != sizeof(struct sockaddr_in6))
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
if (IN6_IS_ADDR_MULTICAST(&ssa->sin6.sin6_addr))
|
||||||
|
return (EINVAL);
|
||||||
|
/*
|
||||||
|
* TODO: Validate embedded scope ID in source
|
||||||
|
* list entry against passed-in ifp, if and only
|
||||||
|
* if source list filter entry is iface or node local.
|
||||||
|
*/
|
||||||
|
in6_clearscope(&ssa->sin6.sin6_addr);
|
||||||
}
|
}
|
||||||
|
gsa->sin6.sin6_port = 0;
|
||||||
if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface)
|
gsa->sin6.sin6_scope_id = 0;
|
||||||
return (EADDRNOTAVAIL);
|
ifindex = gsr.gsr_interface;
|
||||||
|
|
||||||
ifp = ifnet_byindex(gsr.gsr_interface);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -2128,14 +2150,39 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
|
|||||||
if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
|
if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
#ifdef notyet
|
|
||||||
/*
|
/*
|
||||||
* FIXME: Need to embed ifp's scope ID in the address
|
* Validate interface index if provided. If no interface index
|
||||||
* handed down to MLD.
|
* was provided separately, attempt to look the membership up
|
||||||
* See KAME IPV6_LEAVE_GROUP implementation.
|
* from the default scope as a last resort to disambiguate
|
||||||
|
* the membership we are being asked to leave.
|
||||||
|
* XXX SCOPE6 lock potentially taken here.
|
||||||
*/
|
*/
|
||||||
(void)in6_setscope(&mreq->ipv6mr_multiaddr, ifp, NULL);
|
if (ifindex != 0) {
|
||||||
#endif
|
if (ifindex < 0 || V_if_index < ifindex)
|
||||||
|
return (EADDRNOTAVAIL);
|
||||||
|
ifp = ifnet_byindex(ifindex);
|
||||||
|
if (ifp == NULL)
|
||||||
|
return (EADDRNOTAVAIL);
|
||||||
|
(void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
|
||||||
|
} else {
|
||||||
|
error = sa6_embedscope(&gsa->sin6, V_ip6_use_defzone);
|
||||||
|
if (error)
|
||||||
|
return (EADDRNOTAVAIL);
|
||||||
|
/*
|
||||||
|
* XXX For now, stomp on zone ID for the corner case.
|
||||||
|
* This is not the 'KAME way', but we need to see the ifp
|
||||||
|
* directly until such time as this implementation is
|
||||||
|
* refactored, assuming the scope IDs are the way to go.
|
||||||
|
*/
|
||||||
|
ifindex = ntohs(gsa->sin6.sin6_addr.s6_addr16[1]);
|
||||||
|
KASSERT(ifindex != 0, ("%s: bad zone ID", __func__));
|
||||||
|
ifp = ifnet_byindex(ifindex);
|
||||||
|
if (ifp == NULL)
|
||||||
|
return (EADDRNOTAVAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
CTR2(KTR_MLD, "%s: ifp = %p", __func__, ifp);
|
||||||
|
KASSERT(ifp != NULL, ("%s: ifp did not resolve", __func__));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find the membership in the membership array.
|
* Find the membership in the membership array.
|
||||||
@ -2312,10 +2359,10 @@ in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
|
|||||||
|
|
||||||
if (msfr.msfr_ifindex == 0 || V_if_index < msfr.msfr_ifindex)
|
if (msfr.msfr_ifindex == 0 || V_if_index < msfr.msfr_ifindex)
|
||||||
return (EADDRNOTAVAIL);
|
return (EADDRNOTAVAIL);
|
||||||
|
|
||||||
ifp = ifnet_byindex(msfr.msfr_ifindex);
|
ifp = ifnet_byindex(msfr.msfr_ifindex);
|
||||||
if (ifp == NULL)
|
if (ifp == NULL)
|
||||||
return (EADDRNOTAVAIL);
|
return (EADDRNOTAVAIL);
|
||||||
|
(void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Take the INP write lock.
|
* Take the INP write lock.
|
||||||
@ -2393,6 +2440,16 @@ in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
|
|||||||
error = EINVAL;
|
error = EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (IN6_IS_ADDR_MULTICAST(&psin->sin6_addr)) {
|
||||||
|
error = EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* TODO: Validate embedded scope ID in source
|
||||||
|
* list entry against passed-in ifp, if and only
|
||||||
|
* if source list filter entry is iface or node local.
|
||||||
|
*/
|
||||||
|
in6_clearscope(&psin->sin6_addr);
|
||||||
error = im6f_get_source(imf, psin, &lims);
|
error = im6f_get_source(imf, psin, &lims);
|
||||||
if (error)
|
if (error)
|
||||||
break;
|
break;
|
||||||
@ -2560,7 +2617,7 @@ static int
|
|||||||
sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS)
|
sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS)
|
||||||
{
|
{
|
||||||
INIT_VNET_NET(curvnet);
|
INIT_VNET_NET(curvnet);
|
||||||
struct in6_addr *pgina;
|
struct in6_addr mcaddr;
|
||||||
struct in6_addr src;
|
struct in6_addr src;
|
||||||
struct ifnet *ifp;
|
struct ifnet *ifp;
|
||||||
struct ifmultiaddr *ifma;
|
struct ifmultiaddr *ifma;
|
||||||
@ -2591,10 +2648,10 @@ sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS)
|
|||||||
return (ENOENT);
|
return (ENOENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
pgina = (struct in6_addr *)&name[1];
|
memcpy(&mcaddr, &name[1], sizeof(struct in6_addr));
|
||||||
if (!IN6_IS_ADDR_MULTICAST(pgina)) {
|
if (!IN6_IS_ADDR_MULTICAST(&mcaddr)) {
|
||||||
CTR2(KTR_MLD, "%s: group %s is not multicast",
|
CTR2(KTR_MLD, "%s: group %s is not multicast",
|
||||||
__func__, ip6_sprintf(ip6tbuf, pgina));
|
__func__, ip6_sprintf(ip6tbuf, &mcaddr));
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2604,6 +2661,10 @@ sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS)
|
|||||||
__func__, ifindex);
|
__func__, ifindex);
|
||||||
return (ENOENT);
|
return (ENOENT);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* Internal MLD lookups require that scope/zone ID is set.
|
||||||
|
*/
|
||||||
|
(void)in6_setscope(&mcaddr, ifp, NULL);
|
||||||
|
|
||||||
retval = sysctl_wire_old_buffer(req,
|
retval = sysctl_wire_old_buffer(req,
|
||||||
sizeof(uint32_t) + (in6_mcast_maxgrpsrc * sizeof(struct in6_addr)));
|
sizeof(uint32_t) + (in6_mcast_maxgrpsrc * sizeof(struct in6_addr)));
|
||||||
@ -2618,7 +2679,7 @@ sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS)
|
|||||||
ifma->ifma_protospec == NULL)
|
ifma->ifma_protospec == NULL)
|
||||||
continue;
|
continue;
|
||||||
inm = (struct in6_multi *)ifma->ifma_protospec;
|
inm = (struct in6_multi *)ifma->ifma_protospec;
|
||||||
if (!IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, pgina))
|
if (!IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, &mcaddr))
|
||||||
continue;
|
continue;
|
||||||
fmode = inm->in6m_st[1].iss_fmode;
|
fmode = inm->in6m_st[1].iss_fmode;
|
||||||
retval = SYSCTL_OUT(req, &fmode, sizeof(uint32_t));
|
retval = SYSCTL_OUT(req, &fmode, sizeof(uint32_t));
|
||||||
|
@ -773,10 +773,11 @@ ip6_input(struct mbuf *m)
|
|||||||
* case we should pass the packet to the multicast routing
|
* case we should pass the packet to the multicast routing
|
||||||
* daemon.
|
* daemon.
|
||||||
*/
|
*/
|
||||||
if (rtalert != ~0 && V_ip6_forwarding) {
|
if (rtalert != ~0) {
|
||||||
switch (rtalert) {
|
switch (rtalert) {
|
||||||
case IP6OPT_RTALERT_MLD:
|
case IP6OPT_RTALERT_MLD:
|
||||||
ours = 1;
|
if (V_ip6_forwarding)
|
||||||
|
ours = 1;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/*
|
/*
|
||||||
@ -820,6 +821,9 @@ ip6_input(struct mbuf *m)
|
|||||||
* The packet is returned (relatively) intact; if
|
* The packet is returned (relatively) intact; if
|
||||||
* ip6_mforward() returns a non-zero value, the packet
|
* ip6_mforward() returns a non-zero value, the packet
|
||||||
* must be discarded, else it may be accepted below.
|
* must be discarded, else it may be accepted below.
|
||||||
|
*
|
||||||
|
* XXX TODO: Check hlim and multicast scope here to avoid
|
||||||
|
* unnecessarily calling into ip6_mforward().
|
||||||
*/
|
*/
|
||||||
if (ip6_mforward &&
|
if (ip6_mforward &&
|
||||||
ip6_mforward(ip6, m->m_pkthdr.rcvif, m)) {
|
ip6_mforward(ip6, m->m_pkthdr.rcvif, m)) {
|
||||||
@ -882,6 +886,14 @@ ip6_input(struct mbuf *m)
|
|||||||
if (ip6_ipsec_input(m, nxt))
|
if (ip6_ipsec_input(m, nxt))
|
||||||
goto bad;
|
goto bad;
|
||||||
#endif /* IPSEC */
|
#endif /* IPSEC */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use mbuf flags to propagate Router Alert option to
|
||||||
|
* ICMPv6 layer, as hop-by-hop options have been stripped.
|
||||||
|
*/
|
||||||
|
if (nxt == IPPROTO_ICMPV6 && rtalert != ~0)
|
||||||
|
m->m_flags |= M_RTALERT_MLD;
|
||||||
|
|
||||||
nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &off, nxt);
|
nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &off, nxt);
|
||||||
}
|
}
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -122,9 +122,9 @@ static void mld_slowtimo_vnet(void);
|
|||||||
static void mld_sysinit(void);
|
static void mld_sysinit(void);
|
||||||
static void mld_sysuninit(void);
|
static void mld_sysuninit(void);
|
||||||
static int mld_v1_input_query(struct ifnet *, const struct ip6_hdr *,
|
static int mld_v1_input_query(struct ifnet *, const struct ip6_hdr *,
|
||||||
const struct mld_hdr *);
|
/*const*/ struct mld_hdr *);
|
||||||
static int mld_v1_input_report(struct ifnet *, const struct ip6_hdr *,
|
static int mld_v1_input_report(struct ifnet *, const struct ip6_hdr *,
|
||||||
const struct mld_hdr *);
|
/*const*/ struct mld_hdr *);
|
||||||
static void mld_v1_process_group_timer(struct in6_multi *, const int);
|
static void mld_v1_process_group_timer(struct in6_multi *, const int);
|
||||||
static void mld_v1_process_querier_timers(struct mld_ifinfo *);
|
static void mld_v1_process_querier_timers(struct mld_ifinfo *);
|
||||||
static int mld_v1_transmit_report(struct in6_multi *, const int);
|
static int mld_v1_transmit_report(struct in6_multi *, const int);
|
||||||
@ -239,6 +239,11 @@ SYSCTL_V_PROC(V_NET, vnet_inet6, _net_inet6_mld, OID_AUTO, gsrdelay,
|
|||||||
SYSCTL_NODE(_net_inet6_mld, OID_AUTO, ifinfo, CTLFLAG_RD | CTLFLAG_MPSAFE,
|
SYSCTL_NODE(_net_inet6_mld, OID_AUTO, ifinfo, CTLFLAG_RD | CTLFLAG_MPSAFE,
|
||||||
sysctl_mld_ifinfo, "Per-interface MLDv2 state");
|
sysctl_mld_ifinfo, "Per-interface MLDv2 state");
|
||||||
|
|
||||||
|
static int mld_v1enable = 1;
|
||||||
|
SYSCTL_INT(_net_inet6_mld, OID_AUTO, v1enable, CTLFLAG_RW,
|
||||||
|
&mld_v1enable, 0, "Enable fallback to MLDv1");
|
||||||
|
TUNABLE_INT("net.inet6.mld.v1enable", &mld_v1enable);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Packed Router Alert option structure declaration.
|
* Packed Router Alert option structure declaration.
|
||||||
*/
|
*/
|
||||||
@ -615,36 +620,97 @@ mli_delete_locked(const struct ifnet *ifp)
|
|||||||
/*
|
/*
|
||||||
* Process a received MLDv1 general or address-specific query.
|
* Process a received MLDv1 general or address-specific query.
|
||||||
* Assumes that the query header has been pulled up to sizeof(mld_hdr).
|
* Assumes that the query header has been pulled up to sizeof(mld_hdr).
|
||||||
|
*
|
||||||
|
* NOTE: Can't be fully const correct as we temporarily embed scope ID in
|
||||||
|
* mld_addr. This is OK as we own the mbuf chain.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
|
mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
|
||||||
const struct mld_hdr *mld)
|
/*const*/ struct mld_hdr *mld)
|
||||||
{
|
{
|
||||||
struct ifmultiaddr *ifma;
|
struct ifmultiaddr *ifma;
|
||||||
struct mld_ifinfo *mli;
|
struct mld_ifinfo *mli;
|
||||||
struct in6_multi *inm;
|
struct in6_multi *inm;
|
||||||
|
int is_general_query;
|
||||||
uint16_t timer;
|
uint16_t timer;
|
||||||
#ifdef KTR
|
#ifdef KTR
|
||||||
char ip6tbuf[INET6_ADDRSTRLEN];
|
char ip6tbuf[INET6_ADDRSTRLEN];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
is_general_query = 0;
|
||||||
|
|
||||||
|
if (!mld_v1enable) {
|
||||||
|
CTR3(KTR_MLD, "ignore v1 query %s on ifp %p(%s)",
|
||||||
|
ip6_sprintf(ip6tbuf, &mld->mld_addr),
|
||||||
|
ifp, ifp->if_xname);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RFC3810 Section 6.2: MLD queries must originate from
|
||||||
|
* a router's link-local address.
|
||||||
|
*/
|
||||||
|
if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
|
||||||
|
CTR3(KTR_MLD, "ignore v1 query src %s on ifp %p(%s)",
|
||||||
|
ip6_sprintf(ip6tbuf, &ip6->ip6_src),
|
||||||
|
ifp, ifp->if_xname);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do address field validation upfront before we accept
|
||||||
|
* the query.
|
||||||
|
*/
|
||||||
|
if (IN6_IS_ADDR_UNSPECIFIED(&mld->mld_addr)) {
|
||||||
|
/*
|
||||||
|
* MLDv1 General Query.
|
||||||
|
* If this was not sent to the all-nodes group, ignore it.
|
||||||
|
*/
|
||||||
|
struct in6_addr dst;
|
||||||
|
|
||||||
|
dst = ip6->ip6_dst;
|
||||||
|
in6_clearscope(&dst);
|
||||||
|
if (!IN6_ARE_ADDR_EQUAL(&dst, &in6addr_linklocal_allnodes))
|
||||||
|
return (EINVAL);
|
||||||
|
is_general_query = 1;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Embed scope ID of receiving interface in MLD query for
|
||||||
|
* lookup whilst we don't hold other locks.
|
||||||
|
*/
|
||||||
|
in6_setscope(&mld->mld_addr, ifp, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
IN6_MULTI_LOCK();
|
IN6_MULTI_LOCK();
|
||||||
MLD_LOCK();
|
MLD_LOCK();
|
||||||
IF_ADDR_LOCK(ifp);
|
IF_ADDR_LOCK(ifp);
|
||||||
|
|
||||||
mli = MLD_IFINFO(ifp);
|
|
||||||
KASSERT(mli != NULL, ("%s: no mld_ifinfo for ifp %p", __func__, ifp));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Switch to MLDv1 host compatibility mode.
|
* Switch to MLDv1 host compatibility mode.
|
||||||
*/
|
*/
|
||||||
|
mli = MLD_IFINFO(ifp);
|
||||||
|
KASSERT(mli != NULL, ("%s: no mld_ifinfo for ifp %p", __func__, ifp));
|
||||||
mld_set_version(mli, MLD_VERSION_1);
|
mld_set_version(mli, MLD_VERSION_1);
|
||||||
|
|
||||||
timer = ntohs(mld->mld_maxdelay) * PR_FASTHZ / MLD_TIMER_SCALE;
|
timer = (ntohs(mld->mld_maxdelay) * PR_FASTHZ) / MLD_TIMER_SCALE;
|
||||||
if (timer == 0)
|
if (timer == 0)
|
||||||
timer = 1;
|
timer = 1;
|
||||||
|
|
||||||
if (!IN6_IS_ADDR_UNSPECIFIED(&mld->mld_addr)) {
|
if (is_general_query) {
|
||||||
|
/*
|
||||||
|
* For each reporting group joined on this
|
||||||
|
* interface, kick the report timer.
|
||||||
|
*/
|
||||||
|
CTR2(KTR_MLD, "process v1 general query on ifp %p(%s)",
|
||||||
|
ifp, ifp->if_xname);
|
||||||
|
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
|
||||||
|
if (ifma->ifma_addr->sa_family != AF_INET6 ||
|
||||||
|
ifma->ifma_protospec == NULL)
|
||||||
|
continue;
|
||||||
|
inm = (struct in6_multi *)ifma->ifma_protospec;
|
||||||
|
mld_v1_update_group(inm, timer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
/*
|
/*
|
||||||
* MLDv1 Group-Specific Query.
|
* MLDv1 Group-Specific Query.
|
||||||
* If this is a group-specific MLDv1 query, we need only
|
* If this is a group-specific MLDv1 query, we need only
|
||||||
@ -657,32 +723,8 @@ mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
|
|||||||
ifp, ifp->if_xname);
|
ifp, ifp->if_xname);
|
||||||
mld_v1_update_group(inm, timer);
|
mld_v1_update_group(inm, timer);
|
||||||
}
|
}
|
||||||
} else {
|
/* XXX Clear embedded scope ID as userland won't expect it. */
|
||||||
/*
|
in6_clearscope(&mld->mld_addr);
|
||||||
* MLDv1 General Query.
|
|
||||||
* If this was not sent to the all-nodes group, ignore it.
|
|
||||||
*/
|
|
||||||
struct in6_addr dst;
|
|
||||||
|
|
||||||
dst = ip6->ip6_dst;
|
|
||||||
in6_clearscope(&dst);
|
|
||||||
if (IN6_ARE_ADDR_EQUAL(&dst, &in6addr_linklocal_allnodes)) {
|
|
||||||
/*
|
|
||||||
* For each reporting group joined on this
|
|
||||||
* interface, kick the report timer.
|
|
||||||
*/
|
|
||||||
CTR2(KTR_MLD,
|
|
||||||
"process v1 general query on ifp %p(%s)",
|
|
||||||
ifp, ifp->if_xname);
|
|
||||||
|
|
||||||
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
|
|
||||||
if (ifma->ifma_addr->sa_family != AF_INET6 ||
|
|
||||||
ifma->ifma_protospec == NULL)
|
|
||||||
continue;
|
|
||||||
inm = (struct in6_multi *)ifma->ifma_protospec;
|
|
||||||
mld_v1_update_group(inm, timer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IF_ADDR_UNLOCK(ifp);
|
IF_ADDR_UNLOCK(ifp);
|
||||||
@ -769,18 +811,38 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
|
|||||||
struct mldv2_query *mld;
|
struct mldv2_query *mld;
|
||||||
struct in6_multi *inm;
|
struct in6_multi *inm;
|
||||||
uint32_t maxdelay, nsrc, qqi;
|
uint32_t maxdelay, nsrc, qqi;
|
||||||
|
int is_general_query;
|
||||||
uint16_t timer;
|
uint16_t timer;
|
||||||
uint8_t qrv;
|
uint8_t qrv;
|
||||||
|
#ifdef KTR
|
||||||
|
char ip6tbuf[INET6_ADDRSTRLEN];
|
||||||
|
#endif
|
||||||
|
|
||||||
CTR2(KTR_MLD, "process v2 query on ifp %p(%s)", ifp, ifp->if_xname);
|
is_general_query = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RFC3810 Section 6.2: MLD queries must originate from
|
||||||
|
* a router's link-local address.
|
||||||
|
*/
|
||||||
|
if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
|
||||||
|
CTR3(KTR_MLD, "ignore v1 query src %s on ifp %p(%s)",
|
||||||
|
ip6_sprintf(ip6tbuf, &ip6->ip6_src),
|
||||||
|
ifp, ifp->if_xname);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
CTR2(KTR_MLD, "input v2 query on ifp %p(%s)", ifp, ifp->if_xname);
|
||||||
|
|
||||||
mld = (struct mldv2_query *)(mtod(m, uint8_t *) + off);
|
mld = (struct mldv2_query *)(mtod(m, uint8_t *) + off);
|
||||||
|
|
||||||
maxdelay = ntohs(mld->mld_maxdelay); /* in 1/10ths of a second */
|
maxdelay = ntohs(mld->mld_maxdelay); /* in 1/10ths of a second */
|
||||||
if (maxdelay >= 32678) {
|
if (maxdelay >= 32678) {
|
||||||
maxdelay = (MLD_MRC_MANT(mld->mld_maxdelay) | 0x1000) <<
|
maxdelay = (MLD_MRC_MANT(maxdelay) | 0x1000) <<
|
||||||
(MLD_MRC_EXP(mld->mld_maxdelay) + 3);
|
(MLD_MRC_EXP(maxdelay) + 3);
|
||||||
}
|
}
|
||||||
|
timer = (maxdelay * PR_FASTHZ) / MLD_TIMER_SCALE;
|
||||||
|
if (timer == 0)
|
||||||
|
timer = 1;
|
||||||
|
|
||||||
qrv = MLD_QRV(mld->mld_misc);
|
qrv = MLD_QRV(mld->mld_misc);
|
||||||
if (qrv < 2) {
|
if (qrv < 2) {
|
||||||
@ -795,10 +857,6 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
|
|||||||
(MLD_QQIC_EXP(mld->mld_qqi) + 3);
|
(MLD_QQIC_EXP(mld->mld_qqi) + 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
timer = maxdelay * PR_FASTHZ / MLD_TIMER_SCALE;
|
|
||||||
if (timer == 0)
|
|
||||||
timer = 1;
|
|
||||||
|
|
||||||
nsrc = ntohs(mld->mld_numsrc);
|
nsrc = ntohs(mld->mld_numsrc);
|
||||||
if (nsrc > MLD_MAX_GS_SOURCES)
|
if (nsrc > MLD_MAX_GS_SOURCES)
|
||||||
return (EMSGSIZE);
|
return (EMSGSIZE);
|
||||||
@ -806,6 +864,33 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
|
|||||||
(nsrc * sizeof(struct in6_addr)))
|
(nsrc * sizeof(struct in6_addr)))
|
||||||
return (EMSGSIZE);
|
return (EMSGSIZE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do further input validation upfront to avoid resetting timers
|
||||||
|
* should we need to discard this query.
|
||||||
|
*/
|
||||||
|
if (IN6_IS_ADDR_UNSPECIFIED(&mld->mld_addr)) {
|
||||||
|
/*
|
||||||
|
* General Queries SHOULD be directed to ff02::1.
|
||||||
|
* A general query with a source list has undefined
|
||||||
|
* behaviour; discard it.
|
||||||
|
*/
|
||||||
|
struct in6_addr dst;
|
||||||
|
|
||||||
|
dst = ip6->ip6_dst;
|
||||||
|
in6_clearscope(&dst);
|
||||||
|
if (!IN6_ARE_ADDR_EQUAL(&dst, &in6addr_linklocal_allnodes) ||
|
||||||
|
nsrc > 0)
|
||||||
|
return (EINVAL);
|
||||||
|
is_general_query = 1;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Embed scope ID of receiving interface in MLD query for
|
||||||
|
* lookup whilst we don't hold other locks (due to KAME
|
||||||
|
* locking lameness). We own this mbuf chain just now.
|
||||||
|
*/
|
||||||
|
in6_setscope(&mld->mld_addr, ifp, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
IN6_MULTI_LOCK();
|
IN6_MULTI_LOCK();
|
||||||
MLD_LOCK();
|
MLD_LOCK();
|
||||||
IF_ADDR_LOCK(ifp);
|
IF_ADDR_LOCK(ifp);
|
||||||
@ -813,8 +898,15 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
|
|||||||
mli = MLD_IFINFO(ifp);
|
mli = MLD_IFINFO(ifp);
|
||||||
KASSERT(mli != NULL, ("%s: no mld_ifinfo for ifp %p", __func__, ifp));
|
KASSERT(mli != NULL, ("%s: no mld_ifinfo for ifp %p", __func__, ifp));
|
||||||
|
|
||||||
mld_set_version(mli, MLD_VERSION_2);
|
/*
|
||||||
|
* Discard the v2 query if we're in Compatibility Mode.
|
||||||
|
* The RFC is pretty clear that hosts need to stay in MLDv1 mode
|
||||||
|
* until the Old Version Querier Present timer expires.
|
||||||
|
*/
|
||||||
|
if (mli->mli_version != MLD_VERSION_2)
|
||||||
|
goto out_locked;
|
||||||
|
|
||||||
|
mld_set_version(mli, MLD_VERSION_2);
|
||||||
mli->mli_rv = qrv;
|
mli->mli_rv = qrv;
|
||||||
mli->mli_qi = qqi;
|
mli->mli_qi = qqi;
|
||||||
mli->mli_qri = maxdelay;
|
mli->mli_qri = maxdelay;
|
||||||
@ -822,39 +914,20 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
|
|||||||
CTR4(KTR_MLD, "%s: qrv %d qi %d maxdelay %d", __func__, qrv, qqi,
|
CTR4(KTR_MLD, "%s: qrv %d qi %d maxdelay %d", __func__, qrv, qqi,
|
||||||
maxdelay);
|
maxdelay);
|
||||||
|
|
||||||
if (IN6_IS_ADDR_UNSPECIFIED(&mld->mld_addr)) {
|
if (is_general_query) {
|
||||||
/*
|
/*
|
||||||
* MLDv2 General Query.
|
* MLDv2 General Query.
|
||||||
*
|
*
|
||||||
* Schedule a current-state report on this ifp for
|
* Schedule a current-state report on this ifp for
|
||||||
* all groups, possibly containing source lists.
|
* all groups, possibly containing source lists.
|
||||||
*
|
*
|
||||||
* Strip scope ID embedded by ip6_input(). We do not need
|
|
||||||
* to do this for the MLD payload.
|
|
||||||
*/
|
|
||||||
struct in6_addr dst;
|
|
||||||
|
|
||||||
dst = ip6->ip6_dst;
|
|
||||||
in6_clearscope(&dst);
|
|
||||||
if (!IN6_ARE_ADDR_EQUAL(&dst, &in6addr_linklocal_allnodes) ||
|
|
||||||
nsrc > 0) {
|
|
||||||
/*
|
|
||||||
* General Queries SHOULD be directed to ff02::1.
|
|
||||||
* A general query with a source list has undefined
|
|
||||||
* behaviour; discard it.
|
|
||||||
*/
|
|
||||||
goto out_locked;
|
|
||||||
}
|
|
||||||
|
|
||||||
CTR2(KTR_MLD, "process v2 general query on ifp %p(%s)",
|
|
||||||
ifp, ifp->if_xname);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If there is a pending General Query response
|
* If there is a pending General Query response
|
||||||
* scheduled earlier than the selected delay, do
|
* scheduled earlier than the selected delay, do
|
||||||
* not schedule any other reports.
|
* not schedule any other reports.
|
||||||
* Otherwise, reset the interface timer.
|
* Otherwise, reset the interface timer.
|
||||||
*/
|
*/
|
||||||
|
CTR2(KTR_MLD, "process v2 general query on ifp %p(%s)",
|
||||||
|
ifp, ifp->if_xname);
|
||||||
if (mli->mli_v2_timer == 0 || mli->mli_v2_timer >= timer) {
|
if (mli->mli_v2_timer == 0 || mli->mli_v2_timer >= timer) {
|
||||||
mli->mli_v2_timer = MLD_RANDOM_DELAY(timer);
|
mli->mli_v2_timer = MLD_RANDOM_DELAY(timer);
|
||||||
V_interface_timers_running6 = 1;
|
V_interface_timers_running6 = 1;
|
||||||
@ -890,6 +963,9 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
|
|||||||
*/
|
*/
|
||||||
if (mli->mli_v2_timer == 0 || mli->mli_v2_timer >= timer)
|
if (mli->mli_v2_timer == 0 || mli->mli_v2_timer >= timer)
|
||||||
mld_v2_process_group_query(inm, mli, timer, m, off);
|
mld_v2_process_group_query(inm, mli, timer, m, off);
|
||||||
|
|
||||||
|
/* XXX Clear embedded scope ID as userland won't expect it. */
|
||||||
|
in6_clearscope(&mld->mld_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
out_locked:
|
out_locked:
|
||||||
@ -1017,27 +1093,57 @@ mld_v2_process_group_query(struct in6_multi *inm, struct mld_ifinfo *mli,
|
|||||||
/*
|
/*
|
||||||
* Process a received MLDv1 host membership report.
|
* Process a received MLDv1 host membership report.
|
||||||
* Assumes mld points to mld_hdr in pulled up mbuf chain.
|
* Assumes mld points to mld_hdr in pulled up mbuf chain.
|
||||||
|
*
|
||||||
|
* NOTE: Can't be fully const correct as we temporarily embed scope ID in
|
||||||
|
* mld_addr. This is OK as we own the mbuf chain.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
mld_v1_input_report(struct ifnet *ifp, const struct ip6_hdr *ip6,
|
mld_v1_input_report(struct ifnet *ifp, const struct ip6_hdr *ip6,
|
||||||
const struct mld_hdr *mld)
|
/*const*/ struct mld_hdr *mld)
|
||||||
{
|
{
|
||||||
|
struct in6_addr src, dst;
|
||||||
struct in6_ifaddr *ia;
|
struct in6_ifaddr *ia;
|
||||||
struct in6_multi *inm;
|
struct in6_multi *inm;
|
||||||
struct in6_addr src, dst;
|
|
||||||
#ifdef KTR
|
#ifdef KTR
|
||||||
char ip6tbuf[INET6_ADDRSTRLEN];
|
char ip6tbuf[INET6_ADDRSTRLEN];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (!mld_v1enable) {
|
||||||
|
CTR3(KTR_MLD, "ignore v1 report %s on ifp %p(%s)",
|
||||||
|
ip6_sprintf(ip6tbuf, &mld->mld_addr),
|
||||||
|
ifp, ifp->if_xname);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
if (ifp->if_flags & IFF_LOOPBACK)
|
if (ifp->if_flags & IFF_LOOPBACK)
|
||||||
return (0);
|
return (0);
|
||||||
if (!IN6_IS_ADDR_MULTICAST(&mld->mld_addr))
|
|
||||||
return (EINVAL);
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MLDv1 reports must originate from a host's link-local address,
|
||||||
|
* or the unspecified address (when booting).
|
||||||
|
*/
|
||||||
|
src = ip6->ip6_src;
|
||||||
|
in6_clearscope(&src);
|
||||||
|
if (!IN6_IS_SCOPE_LINKLOCAL(&src) && !IN6_IS_ADDR_UNSPECIFIED(&src)) {
|
||||||
|
CTR3(KTR_MLD, "ignore v1 query src %s on ifp %p(%s)",
|
||||||
|
ip6_sprintf(ip6tbuf, &ip6->ip6_src),
|
||||||
|
ifp, ifp->if_xname);
|
||||||
|
return (EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RFC2710 Section 4: MLDv1 reports must pertain to a multicast
|
||||||
|
* group, and must be directed to the group itself.
|
||||||
|
*/
|
||||||
dst = ip6->ip6_dst;
|
dst = ip6->ip6_dst;
|
||||||
in6_clearscope(&dst);
|
in6_clearscope(&dst);
|
||||||
if (!IN6_ARE_ADDR_EQUAL(&mld->mld_addr, &dst))
|
if (!IN6_IS_ADDR_MULTICAST(&mld->mld_addr) ||
|
||||||
|
!IN6_ARE_ADDR_EQUAL(&mld->mld_addr, &dst)) {
|
||||||
|
CTR3(KTR_MLD, "ignore v1 query dst %s on ifp %p(%s)",
|
||||||
|
ip6_sprintf(ip6tbuf, &ip6->ip6_dst),
|
||||||
|
ifp, ifp->if_xname);
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure we don't hear our own membership report, as fast
|
* Make sure we don't hear our own membership report, as fast
|
||||||
@ -1050,8 +1156,6 @@ mld_v1_input_report(struct ifnet *ifp, const struct ip6_hdr *ip6,
|
|||||||
* performed for the on-wire address.
|
* performed for the on-wire address.
|
||||||
*/
|
*/
|
||||||
ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST);
|
ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST);
|
||||||
src = ip6->ip6_src;
|
|
||||||
in6_clearscope(&src);
|
|
||||||
if ((ia && IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, IA6_IN6(ia))) ||
|
if ((ia && IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, IA6_IN6(ia))) ||
|
||||||
(ia == NULL && IN6_IS_ADDR_UNSPECIFIED(&src)))
|
(ia == NULL && IN6_IS_ADDR_UNSPECIFIED(&src)))
|
||||||
return (0);
|
return (0);
|
||||||
@ -1059,6 +1163,13 @@ mld_v1_input_report(struct ifnet *ifp, const struct ip6_hdr *ip6,
|
|||||||
CTR3(KTR_MLD, "process v1 report %s on ifp %p(%s)",
|
CTR3(KTR_MLD, "process v1 report %s on ifp %p(%s)",
|
||||||
ip6_sprintf(ip6tbuf, &mld->mld_addr), ifp, ifp->if_xname);
|
ip6_sprintf(ip6tbuf, &mld->mld_addr), ifp, ifp->if_xname);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Embed scope ID of receiving interface in MLD query for lookup
|
||||||
|
* whilst we don't hold other locks (due to KAME locking lameness).
|
||||||
|
*/
|
||||||
|
if (!IN6_IS_ADDR_UNSPECIFIED(&mld->mld_addr))
|
||||||
|
in6_setscope(&mld->mld_addr, ifp, NULL);
|
||||||
|
|
||||||
IN6_MULTI_LOCK();
|
IN6_MULTI_LOCK();
|
||||||
MLD_LOCK();
|
MLD_LOCK();
|
||||||
IF_ADDR_LOCK(ifp);
|
IF_ADDR_LOCK(ifp);
|
||||||
@ -1113,6 +1224,9 @@ mld_v1_input_report(struct ifnet *ifp, const struct ip6_hdr *ip6,
|
|||||||
IF_ADDR_UNLOCK(ifp);
|
IF_ADDR_UNLOCK(ifp);
|
||||||
IN6_MULTI_UNLOCK();
|
IN6_MULTI_UNLOCK();
|
||||||
|
|
||||||
|
/* XXX Clear embedded scope ID as userland won't expect it. */
|
||||||
|
in6_clearscope(&mld->mld_addr);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1156,6 +1270,10 @@ mld_input(struct mbuf *m, int off, int icmp6len)
|
|||||||
return (IPPROTO_DONE);
|
return (IPPROTO_DONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Userland needs to see all of this traffic for implementing
|
||||||
|
* the endpoint discovery portion of multicast routing.
|
||||||
|
*/
|
||||||
switch (mld->mld_type) {
|
switch (mld->mld_type) {
|
||||||
case MLD_LISTENER_QUERY:
|
case MLD_LISTENER_QUERY:
|
||||||
icmp6_ifstat_inc(ifp, ifs6_in_mldquery);
|
icmp6_ifstat_inc(ifp, ifs6_in_mldquery);
|
||||||
@ -1171,7 +1289,7 @@ mld_input(struct mbuf *m, int off, int icmp6len)
|
|||||||
case MLD_LISTENER_REPORT:
|
case MLD_LISTENER_REPORT:
|
||||||
icmp6_ifstat_inc(ifp, ifs6_in_mldreport);
|
icmp6_ifstat_inc(ifp, ifs6_in_mldreport);
|
||||||
if (mld_v1_input_report(ifp, ip6, mld) != 0)
|
if (mld_v1_input_report(ifp, ip6, mld) != 0)
|
||||||
return (0); /* Userland needs to see it. */
|
return (0);
|
||||||
break;
|
break;
|
||||||
case MLDV2_LISTENER_REPORT:
|
case MLDV2_LISTENER_REPORT:
|
||||||
icmp6_ifstat_inc(ifp, ifs6_in_mldreport);
|
icmp6_ifstat_inc(ifp, ifs6_in_mldreport);
|
||||||
@ -1290,8 +1408,16 @@ mld_fasttimo_vnet(void)
|
|||||||
inm = (struct in6_multi *)ifma->ifma_protospec;
|
inm = (struct in6_multi *)ifma->ifma_protospec;
|
||||||
switch (mli->mli_version) {
|
switch (mli->mli_version) {
|
||||||
case MLD_VERSION_1:
|
case MLD_VERSION_1:
|
||||||
|
/*
|
||||||
|
* XXX Drop IF_ADDR lock temporarily to
|
||||||
|
* avoid recursion caused by a potential
|
||||||
|
* call by in6ifa_ifpforlinklocal().
|
||||||
|
* rwlock candidate?
|
||||||
|
*/
|
||||||
|
IF_ADDR_UNLOCK(ifp);
|
||||||
mld_v1_process_group_timer(inm,
|
mld_v1_process_group_timer(inm,
|
||||||
mli->mli_version);
|
mli->mli_version);
|
||||||
|
IF_ADDR_LOCK(ifp);
|
||||||
break;
|
break;
|
||||||
case MLD_VERSION_2:
|
case MLD_VERSION_2:
|
||||||
mld_v2_process_group_timers(mli, &qrq,
|
mld_v2_process_group_timers(mli, &qrq,
|
||||||
@ -1510,7 +1636,7 @@ mld_set_version(struct mld_ifinfo *mli, const int version)
|
|||||||
* Compute the "Older Version Querier Present" timer as per
|
* Compute the "Older Version Querier Present" timer as per
|
||||||
* Section 9.12.
|
* Section 9.12.
|
||||||
*/
|
*/
|
||||||
old_version_timer = mli->mli_rv * mli->mli_qi + mli->mli_qri;
|
old_version_timer = (mli->mli_rv * mli->mli_qi) + mli->mli_qri;
|
||||||
old_version_timer *= PR_SLOWHZ;
|
old_version_timer *= PR_SLOWHZ;
|
||||||
mli->mli_v1_timer = old_version_timer;
|
mli->mli_v1_timer = old_version_timer;
|
||||||
}
|
}
|
||||||
@ -1643,7 +1769,7 @@ mld_v1_process_querier_timers(struct mld_ifinfo *mli)
|
|||||||
|
|
||||||
MLD_LOCK_ASSERT();
|
MLD_LOCK_ASSERT();
|
||||||
|
|
||||||
if (mli->mli_v1_timer == 0 && mli->mli_version != MLD_VERSION_2) {
|
if (mli->mli_version != MLD_VERSION_2 && --mli->mli_v1_timer == 0) {
|
||||||
/*
|
/*
|
||||||
* MLDv1 Querier Present timer expired; revert to MLDv2.
|
* MLDv1 Querier Present timer expired; revert to MLDv2.
|
||||||
*/
|
*/
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd February 28, 2009
|
.Dd May 27, 2009
|
||||||
.Dt IFMCSTAT 8
|
.Dt IFMCSTAT 8
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -71,8 +71,8 @@ Source lists for each group will also be printed.
|
|||||||
.Pp
|
.Pp
|
||||||
If specified twice, and
|
If specified twice, and
|
||||||
.Xr kvm 3
|
.Xr kvm 3
|
||||||
is in use, the IGMP timers for each interface
|
is in use, the control plane timers for each interface
|
||||||
and the IGMP source list counters for each group
|
and the source list counters for each group
|
||||||
will also be printed.
|
will also be printed.
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
@ -82,9 +82,9 @@ has been built with support for
|
|||||||
.Xr kvm 3 :
|
.Xr kvm 3 :
|
||||||
.Bl -tag -width Fl
|
.Bl -tag -width Fl
|
||||||
.It Fl K
|
.It Fl K
|
||||||
forces the use of
|
attempts to use
|
||||||
.Xr kvm 3
|
.Xr kvm 3
|
||||||
to be disabled.
|
to retrieve the multicast group information.
|
||||||
.It Fl M Ar core
|
.It Fl M Ar core
|
||||||
extracts values associated with the name list from the specified core,
|
extracts values associated with the name list from the specified core,
|
||||||
instead of the default
|
instead of the default
|
||||||
@ -94,6 +94,11 @@ extracts the name list from the specified kernel instead of the
|
|||||||
default, which is the kernel image the system has booted from.
|
default, which is the kernel image the system has booted from.
|
||||||
.El
|
.El
|
||||||
.Sh IMPLEMENTATION NOTES
|
.Sh IMPLEMENTATION NOTES
|
||||||
|
.Nm
|
||||||
|
will always print the embedded scope IDs of IPv6 multicast group
|
||||||
|
memberships.
|
||||||
|
This is because memberships are always scoped to an interface.
|
||||||
|
.Pp
|
||||||
When run with the
|
When run with the
|
||||||
.Fl v
|
.Fl v
|
||||||
option,
|
option,
|
||||||
@ -119,13 +124,6 @@ is more limited.
|
|||||||
This support is recommended for debugging purposes.
|
This support is recommended for debugging purposes.
|
||||||
It requires super-user privilege if used to inspect a running kernel.
|
It requires super-user privilege if used to inspect a running kernel.
|
||||||
.Pp
|
.Pp
|
||||||
The
|
|
||||||
.Xr kvm 3
|
|
||||||
back-end will be used by default if
|
|
||||||
.Nm
|
|
||||||
is run with super-user privileges, unless the
|
|
||||||
.Fl K
|
|
||||||
option is specified.
|
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr getifaddrs 3 ,
|
.Xr getifaddrs 3 ,
|
||||||
.Xr getifmaddrs 3 ,
|
.Xr getifmaddrs 3 ,
|
||||||
|
@ -264,13 +264,13 @@ main(int argc, char **argv)
|
|||||||
usage();
|
usage();
|
||||||
|
|
||||||
#ifdef WITH_KVM
|
#ifdef WITH_KVM
|
||||||
if (!Kflag)
|
if (Kflag)
|
||||||
error = ifmcstat_kvm(kernel, core);
|
error = ifmcstat_kvm(kernel, core);
|
||||||
/*
|
/*
|
||||||
* If KVM failed, and user did not explicitly specify a core file,
|
* If KVM failed, and user did not explicitly specify a core file,
|
||||||
* or force KVM backend to be disabled, try the sysctl backend.
|
* or force KVM backend to be disabled, try the sysctl backend.
|
||||||
*/
|
*/
|
||||||
if (Kflag || (error != 0 && (core == NULL && kernel == NULL)))
|
if (!Kflag || (error != 0 && (core == NULL && kernel == NULL)))
|
||||||
#endif
|
#endif
|
||||||
error = ifmcstat_getifmaddrs();
|
error = ifmcstat_getifmaddrs();
|
||||||
if (error != 0)
|
if (error != 0)
|
||||||
|
Loading…
Reference in New Issue
Block a user