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
954b6f0f3b
commit
f73e96a405
@ -191,6 +191,7 @@ MAN= aac.4 \
|
||||
meteor.4 \
|
||||
mfi.4 \
|
||||
miibus.4 \
|
||||
mld.4 \
|
||||
mlx.4 \
|
||||
mly.4 \
|
||||
mmc.4 \
|
||||
|
@ -25,7 +25,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd February 13, 2009
|
||||
.Dd May 27, 2009
|
||||
.Dt MULTICAST 4
|
||||
.Os
|
||||
.\"
|
||||
@ -962,6 +962,7 @@ after the previous upcall.
|
||||
.Xr intro 4 ,
|
||||
.Xr ip 4 ,
|
||||
.Xr ip6 4 ,
|
||||
.Xr mld 4 ,
|
||||
.Xr pim 4
|
||||
.\"
|
||||
.Sh HISTORY
|
||||
@ -1002,6 +1003,8 @@ monitoring were implemented by
|
||||
in collaboration with
|
||||
.An Chris Brown
|
||||
(NextHop).
|
||||
The IGMPv3 and MLDv2 multicast support was implemented by
|
||||
.An Bruce Simpson .
|
||||
.Pp
|
||||
This manual page was written by
|
||||
.An Pavlin Radoslavov
|
||||
|
@ -403,6 +403,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
||||
INIT_VNET_INET6(curvnet);
|
||||
INIT_VPROCG(TD_TO_VPROCG(curthread)); /* XXX V_hostname needs this */
|
||||
struct mbuf *m = *mp, *n;
|
||||
struct ifnet *ifp;
|
||||
struct ip6_hdr *ip6, *nip6;
|
||||
struct icmp6_hdr *icmp6, *nicmp6;
|
||||
int off = *offp;
|
||||
@ -410,6 +411,8 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
||||
int code, sum, noff;
|
||||
char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN];
|
||||
|
||||
ifp = m->m_pkthdr.rcvif;
|
||||
|
||||
#ifndef PULLDOWN_TEST
|
||||
IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), IPPROTO_DONE);
|
||||
/* 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.
|
||||
*/
|
||||
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);
|
||||
if (inm == NULL) {
|
||||
IP6STAT_INC(ip6s_notmember);
|
||||
@ -483,19 +484,19 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
||||
}
|
||||
|
||||
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)
|
||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error);
|
||||
icmp6_ifstat_inc(ifp, ifs6_in_error);
|
||||
|
||||
switch (icmp6->icmp6_type) {
|
||||
case ICMP6_DST_UNREACH:
|
||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_dstunreach);
|
||||
icmp6_ifstat_inc(ifp, ifs6_in_dstunreach);
|
||||
switch (code) {
|
||||
case ICMP6_DST_UNREACH_NOROUTE:
|
||||
code = PRC_UNREACH_NET;
|
||||
break;
|
||||
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? */
|
||||
break;
|
||||
case ICMP6_DST_UNREACH_ADDR:
|
||||
@ -515,7 +516,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
||||
break;
|
||||
|
||||
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 */
|
||||
|
||||
@ -529,7 +530,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
||||
break;
|
||||
|
||||
case ICMP6_TIME_EXCEEDED:
|
||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_timeexceed);
|
||||
icmp6_ifstat_inc(ifp, ifs6_in_timeexceed);
|
||||
switch (code) {
|
||||
case ICMP6_TIME_EXCEED_TRANSIT:
|
||||
code = PRC_TIMXCEED_INTRANS;
|
||||
@ -544,7 +545,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
||||
break;
|
||||
|
||||
case ICMP6_PARAM_PROB:
|
||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_paramprob);
|
||||
icmp6_ifstat_inc(ifp, ifs6_in_paramprob);
|
||||
switch (code) {
|
||||
case ICMP6_PARAMPROB_NEXTHEADER:
|
||||
code = PRC_UNREACH_PROTOCOL;
|
||||
@ -560,7 +561,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
||||
break;
|
||||
|
||||
case ICMP6_ECHO_REQUEST:
|
||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echo);
|
||||
icmp6_ifstat_inc(ifp, ifs6_in_echo);
|
||||
if (code != 0)
|
||||
goto badcode;
|
||||
if ((n = m_copy(m, 0, M_COPYALL)) == NULL) {
|
||||
@ -623,7 +624,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
||||
break;
|
||||
|
||||
case ICMP6_ECHO_REPLY:
|
||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echoreply);
|
||||
icmp6_ifstat_inc(ifp, ifs6_in_echoreply);
|
||||
if (code != 0)
|
||||
goto badcode;
|
||||
break;
|
||||
@ -633,11 +634,15 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
||||
case MLD_LISTENER_DONE:
|
||||
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
|
||||
* 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;
|
||||
if (mld_input(m, off, icmp6len) != 0)
|
||||
return (IPPROTO_DONE);
|
||||
@ -748,7 +753,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
||||
break;
|
||||
|
||||
case ND_ROUTER_SOLICIT:
|
||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routersolicit);
|
||||
icmp6_ifstat_inc(ifp, ifs6_in_routersolicit);
|
||||
if (code != 0)
|
||||
goto badcode;
|
||||
if (icmp6len < sizeof(struct nd_router_solicit))
|
||||
@ -764,7 +769,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
||||
break;
|
||||
|
||||
case ND_ROUTER_ADVERT:
|
||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routeradvert);
|
||||
icmp6_ifstat_inc(ifp, ifs6_in_routeradvert);
|
||||
if (code != 0)
|
||||
goto badcode;
|
||||
if (icmp6len < sizeof(struct nd_router_advert))
|
||||
@ -780,7 +785,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
||||
break;
|
||||
|
||||
case ND_NEIGHBOR_SOLICIT:
|
||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighborsolicit);
|
||||
icmp6_ifstat_inc(ifp, ifs6_in_neighborsolicit);
|
||||
if (code != 0)
|
||||
goto badcode;
|
||||
if (icmp6len < sizeof(struct nd_neighbor_solicit))
|
||||
@ -796,7 +801,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
||||
break;
|
||||
|
||||
case ND_NEIGHBOR_ADVERT:
|
||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighboradvert);
|
||||
icmp6_ifstat_inc(ifp, ifs6_in_neighboradvert);
|
||||
if (code != 0)
|
||||
goto badcode;
|
||||
if (icmp6len < sizeof(struct nd_neighbor_advert))
|
||||
@ -812,7 +817,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
|
||||
break;
|
||||
|
||||
case ND_REDIRECT:
|
||||
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_redirect);
|
||||
icmp6_ifstat_inc(ifp, ifs6_in_redirect);
|
||||
if (code != 0)
|
||||
goto badcode;
|
||||
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->icmp6_type, ip6_sprintf(ip6bufs, &ip6->ip6_src),
|
||||
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) {
|
||||
/* ICMPv6 error: MUST deliver it by spec... */
|
||||
code = PRC_NCMDS;
|
||||
|
@ -619,6 +619,7 @@ struct ip6_mtuinfo {
|
||||
#define M_DECRYPTED M_PROTO3
|
||||
#define M_LOOP M_PROTO4
|
||||
#define M_AUTHIPDGM M_PROTO5
|
||||
#define M_RTALERT_MLD M_PROTO6
|
||||
|
||||
#ifdef _KERNEL
|
||||
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
|
||||
* 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
|
||||
* 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;
|
||||
find.im6s_addr = psa->sin6.sin6_addr;
|
||||
in6_clearscope(&find.im6s_addr); /* XXX */
|
||||
ims = RB_FIND(ip6_msource_tree, &imf->im6f_sources, &find);
|
||||
|
||||
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];
|
||||
#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();
|
||||
|
||||
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))
|
||||
return (EINVAL);
|
||||
|
||||
(void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
|
||||
|
||||
/*
|
||||
* 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)
|
||||
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);
|
||||
|
||||
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);
|
||||
if (ifp == NULL)
|
||||
return (EINVAL);
|
||||
return (EADDRNOTAVAIL);
|
||||
(void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
|
||||
|
||||
INP_WLOCK(inp);
|
||||
|
||||
/*
|
||||
* Lookup group on the socket.
|
||||
*/
|
||||
gsa = (sockunion_t *)&msfr.msfr_group;
|
||||
idx = im6o_match_group(imo, ifp, &gsa->sa);
|
||||
if (idx == -1 || imo->im6o_mfilters == NULL) {
|
||||
INP_WUNLOCK(inp);
|
||||
@ -1803,6 +1831,12 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
|
||||
ssa = (sockunion_t *)&gsr.gsr_source;
|
||||
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) {
|
||||
case IPV6_JOIN_GROUP: {
|
||||
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))
|
||||
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 (ssa->sin6.sin6_family != AF_INET6 ||
|
||||
ssa->sin6.sin6_len != sizeof(struct sockaddr_in6))
|
||||
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_scope_id = 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#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))
|
||||
return (EINVAL);
|
||||
|
||||
if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0)
|
||||
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);
|
||||
#endif
|
||||
(void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
INIT_VNET_NET(curvnet);
|
||||
INIT_VNET_INET6(curvnet);
|
||||
struct ipv6_mreq mreq;
|
||||
struct group_source_req gsr;
|
||||
sockunion_t *gsa, *ssa;
|
||||
struct ifnet *ifp;
|
||||
@ -2038,6 +2066,7 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
|
||||
struct ip6_moptions *imo;
|
||||
struct in6_msource *ims;
|
||||
struct in6_multi *inm;
|
||||
uint32_t ifindex;
|
||||
size_t idx;
|
||||
int error, is_final;
|
||||
#ifdef KTR
|
||||
@ -2045,6 +2074,7 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
|
||||
#endif
|
||||
|
||||
ifp = NULL;
|
||||
ifindex = 0;
|
||||
error = 0;
|
||||
is_final = 1;
|
||||
|
||||
@ -2054,39 +2084,26 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
|
||||
ssa = (sockunion_t *)&gsr.gsr_source;
|
||||
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) {
|
||||
case IPV6_LEAVE_GROUP: {
|
||||
struct ipv6_mreq mreq;
|
||||
|
||||
case IPV6_LEAVE_GROUP:
|
||||
error = sooptcopyin(sopt, &mreq, sizeof(struct ipv6_mreq),
|
||||
sizeof(struct ipv6_mreq));
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
gsa->sin6.sin6_family = AF_INET6;
|
||||
gsa->sin6.sin6_len = sizeof(struct sockaddr_in6);
|
||||
gsa->sin6.sin6_addr = mreq.ipv6mr_multiaddr;
|
||||
|
||||
if (mreq.ipv6mr_interface == 0) {
|
||||
#ifdef notyet
|
||||
/*
|
||||
* 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;
|
||||
gsa->sin6.sin6_port = 0;
|
||||
gsa->sin6.sin6_scope_id = 0;
|
||||
ifindex = mreq.ipv6mr_interface;
|
||||
break;
|
||||
|
||||
case MCAST_LEAVE_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 ||
|
||||
gsa->sin6.sin6_len != sizeof(struct sockaddr_in6))
|
||||
return (EINVAL);
|
||||
|
||||
if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) {
|
||||
if (ssa->sin6.sin6_family != AF_INET6 ||
|
||||
ssa->sin6.sin6_len != sizeof(struct sockaddr_in6))
|
||||
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);
|
||||
}
|
||||
|
||||
if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface)
|
||||
return (EADDRNOTAVAIL);
|
||||
|
||||
ifp = ifnet_byindex(gsr.gsr_interface);
|
||||
gsa->sin6.sin6_port = 0;
|
||||
gsa->sin6.sin6_scope_id = 0;
|
||||
ifindex = gsr.gsr_interface;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -2128,14 +2150,39 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
|
||||
if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
|
||||
return (EINVAL);
|
||||
|
||||
#ifdef notyet
|
||||
/*
|
||||
* FIXME: Need to embed ifp's scope ID in the address
|
||||
* handed down to MLD.
|
||||
* See KAME IPV6_LEAVE_GROUP implementation.
|
||||
* Validate interface index if provided. If no interface index
|
||||
* was provided separately, attempt to look the membership up
|
||||
* 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);
|
||||
#endif
|
||||
if (ifindex != 0) {
|
||||
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.
|
||||
@ -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)
|
||||
return (EADDRNOTAVAIL);
|
||||
|
||||
ifp = ifnet_byindex(msfr.msfr_ifindex);
|
||||
if (ifp == NULL)
|
||||
return (EADDRNOTAVAIL);
|
||||
(void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
|
||||
|
||||
/*
|
||||
* Take the INP write lock.
|
||||
@ -2393,6 +2440,16 @@ in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
|
||||
error = EINVAL;
|
||||
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);
|
||||
if (error)
|
||||
break;
|
||||
@ -2560,7 +2617,7 @@ static int
|
||||
sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
INIT_VNET_NET(curvnet);
|
||||
struct in6_addr *pgina;
|
||||
struct in6_addr mcaddr;
|
||||
struct in6_addr src;
|
||||
struct ifnet *ifp;
|
||||
struct ifmultiaddr *ifma;
|
||||
@ -2591,10 +2648,10 @@ sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS)
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
pgina = (struct in6_addr *)&name[1];
|
||||
if (!IN6_IS_ADDR_MULTICAST(pgina)) {
|
||||
memcpy(&mcaddr, &name[1], sizeof(struct in6_addr));
|
||||
if (!IN6_IS_ADDR_MULTICAST(&mcaddr)) {
|
||||
CTR2(KTR_MLD, "%s: group %s is not multicast",
|
||||
__func__, ip6_sprintf(ip6tbuf, pgina));
|
||||
__func__, ip6_sprintf(ip6tbuf, &mcaddr));
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
@ -2604,6 +2661,10 @@ sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS)
|
||||
__func__, ifindex);
|
||||
return (ENOENT);
|
||||
}
|
||||
/*
|
||||
* Internal MLD lookups require that scope/zone ID is set.
|
||||
*/
|
||||
(void)in6_setscope(&mcaddr, ifp, NULL);
|
||||
|
||||
retval = sysctl_wire_old_buffer(req,
|
||||
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)
|
||||
continue;
|
||||
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;
|
||||
fmode = inm->in6m_st[1].iss_fmode;
|
||||
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
|
||||
* daemon.
|
||||
*/
|
||||
if (rtalert != ~0 && V_ip6_forwarding) {
|
||||
if (rtalert != ~0) {
|
||||
switch (rtalert) {
|
||||
case IP6OPT_RTALERT_MLD:
|
||||
ours = 1;
|
||||
if (V_ip6_forwarding)
|
||||
ours = 1;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
@ -820,6 +821,9 @@ ip6_input(struct mbuf *m)
|
||||
* The packet is returned (relatively) intact; if
|
||||
* ip6_mforward() returns a non-zero value, the packet
|
||||
* 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 &&
|
||||
ip6_mforward(ip6, m->m_pkthdr.rcvif, m)) {
|
||||
@ -882,6 +886,14 @@ ip6_input(struct mbuf *m)
|
||||
if (ip6_ipsec_input(m, nxt))
|
||||
goto bad;
|
||||
#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);
|
||||
}
|
||||
goto out;
|
||||
|
@ -122,9 +122,9 @@ static void mld_slowtimo_vnet(void);
|
||||
static void mld_sysinit(void);
|
||||
static void mld_sysuninit(void);
|
||||
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 *,
|
||||
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_querier_timers(struct mld_ifinfo *);
|
||||
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_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.
|
||||
*/
|
||||
@ -615,36 +620,97 @@ mli_delete_locked(const struct ifnet *ifp)
|
||||
/*
|
||||
* Process a received MLDv1 general or address-specific query.
|
||||
* 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
|
||||
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 mld_ifinfo *mli;
|
||||
struct in6_multi *inm;
|
||||
int is_general_query;
|
||||
uint16_t timer;
|
||||
#ifdef KTR
|
||||
char ip6tbuf[INET6_ADDRSTRLEN];
|
||||
#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();
|
||||
MLD_LOCK();
|
||||
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.
|
||||
*/
|
||||
mli = MLD_IFINFO(ifp);
|
||||
KASSERT(mli != NULL, ("%s: no mld_ifinfo for ifp %p", __func__, ifp));
|
||||
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)
|
||||
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.
|
||||
* 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);
|
||||
mld_v1_update_group(inm, timer);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
/* XXX Clear embedded scope ID as userland won't expect it. */
|
||||
in6_clearscope(&mld->mld_addr);
|
||||
}
|
||||
|
||||
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 in6_multi *inm;
|
||||
uint32_t maxdelay, nsrc, qqi;
|
||||
int is_general_query;
|
||||
uint16_t timer;
|
||||
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);
|
||||
|
||||
maxdelay = ntohs(mld->mld_maxdelay); /* in 1/10ths of a second */
|
||||
if (maxdelay >= 32678) {
|
||||
maxdelay = (MLD_MRC_MANT(mld->mld_maxdelay) | 0x1000) <<
|
||||
(MLD_MRC_EXP(mld->mld_maxdelay) + 3);
|
||||
maxdelay = (MLD_MRC_MANT(maxdelay) | 0x1000) <<
|
||||
(MLD_MRC_EXP(maxdelay) + 3);
|
||||
}
|
||||
timer = (maxdelay * PR_FASTHZ) / MLD_TIMER_SCALE;
|
||||
if (timer == 0)
|
||||
timer = 1;
|
||||
|
||||
qrv = MLD_QRV(mld->mld_misc);
|
||||
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);
|
||||
}
|
||||
|
||||
timer = maxdelay * PR_FASTHZ / MLD_TIMER_SCALE;
|
||||
if (timer == 0)
|
||||
timer = 1;
|
||||
|
||||
nsrc = ntohs(mld->mld_numsrc);
|
||||
if (nsrc > MLD_MAX_GS_SOURCES)
|
||||
return (EMSGSIZE);
|
||||
@ -806,6 +864,33 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
|
||||
(nsrc * sizeof(struct in6_addr)))
|
||||
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();
|
||||
MLD_LOCK();
|
||||
IF_ADDR_LOCK(ifp);
|
||||
@ -813,8 +898,15 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
|
||||
mli = MLD_IFINFO(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_qi = qqi;
|
||||
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,
|
||||
maxdelay);
|
||||
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(&mld->mld_addr)) {
|
||||
if (is_general_query) {
|
||||
/*
|
||||
* MLDv2 General Query.
|
||||
*
|
||||
* Schedule a current-state report on this ifp for
|
||||
* 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
|
||||
* scheduled earlier than the selected delay, do
|
||||
* not schedule any other reports.
|
||||
* 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) {
|
||||
mli->mli_v2_timer = MLD_RANDOM_DELAY(timer);
|
||||
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)
|
||||
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:
|
||||
@ -1017,27 +1093,57 @@ mld_v2_process_group_query(struct in6_multi *inm, struct mld_ifinfo *mli,
|
||||
/*
|
||||
* Process a received MLDv1 host membership report.
|
||||
* 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
|
||||
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_multi *inm;
|
||||
struct in6_addr src, dst;
|
||||
#ifdef KTR
|
||||
char ip6tbuf[INET6_ADDRSTRLEN];
|
||||
#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)
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
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))) ||
|
||||
(ia == NULL && IN6_IS_ADDR_UNSPECIFIED(&src)))
|
||||
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)",
|
||||
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();
|
||||
MLD_LOCK();
|
||||
IF_ADDR_LOCK(ifp);
|
||||
@ -1113,6 +1224,9 @@ mld_v1_input_report(struct ifnet *ifp, const struct ip6_hdr *ip6,
|
||||
IF_ADDR_UNLOCK(ifp);
|
||||
IN6_MULTI_UNLOCK();
|
||||
|
||||
/* XXX Clear embedded scope ID as userland won't expect it. */
|
||||
in6_clearscope(&mld->mld_addr);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -1156,6 +1270,10 @@ mld_input(struct mbuf *m, int off, int icmp6len)
|
||||
return (IPPROTO_DONE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Userland needs to see all of this traffic for implementing
|
||||
* the endpoint discovery portion of multicast routing.
|
||||
*/
|
||||
switch (mld->mld_type) {
|
||||
case MLD_LISTENER_QUERY:
|
||||
icmp6_ifstat_inc(ifp, ifs6_in_mldquery);
|
||||
@ -1171,7 +1289,7 @@ mld_input(struct mbuf *m, int off, int icmp6len)
|
||||
case MLD_LISTENER_REPORT:
|
||||
icmp6_ifstat_inc(ifp, ifs6_in_mldreport);
|
||||
if (mld_v1_input_report(ifp, ip6, mld) != 0)
|
||||
return (0); /* Userland needs to see it. */
|
||||
return (0);
|
||||
break;
|
||||
case MLDV2_LISTENER_REPORT:
|
||||
icmp6_ifstat_inc(ifp, ifs6_in_mldreport);
|
||||
@ -1290,8 +1408,16 @@ mld_fasttimo_vnet(void)
|
||||
inm = (struct in6_multi *)ifma->ifma_protospec;
|
||||
switch (mli->mli_version) {
|
||||
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,
|
||||
mli->mli_version);
|
||||
IF_ADDR_LOCK(ifp);
|
||||
break;
|
||||
case MLD_VERSION_2:
|
||||
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
|
||||
* 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;
|
||||
mli->mli_v1_timer = old_version_timer;
|
||||
}
|
||||
@ -1643,7 +1769,7 @@ mld_v1_process_querier_timers(struct mld_ifinfo *mli)
|
||||
|
||||
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.
|
||||
*/
|
||||
|
@ -30,7 +30,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd February 28, 2009
|
||||
.Dd May 27, 2009
|
||||
.Dt IFMCSTAT 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -71,8 +71,8 @@ Source lists for each group will also be printed.
|
||||
.Pp
|
||||
If specified twice, and
|
||||
.Xr kvm 3
|
||||
is in use, the IGMP timers for each interface
|
||||
and the IGMP source list counters for each group
|
||||
is in use, the control plane timers for each interface
|
||||
and the source list counters for each group
|
||||
will also be printed.
|
||||
.El
|
||||
.Pp
|
||||
@ -82,9 +82,9 @@ has been built with support for
|
||||
.Xr kvm 3 :
|
||||
.Bl -tag -width Fl
|
||||
.It Fl K
|
||||
forces the use of
|
||||
attempts to use
|
||||
.Xr kvm 3
|
||||
to be disabled.
|
||||
to retrieve the multicast group information.
|
||||
.It Fl M Ar core
|
||||
extracts values associated with the name list from the specified core,
|
||||
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.
|
||||
.El
|
||||
.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
|
||||
.Fl v
|
||||
option,
|
||||
@ -119,13 +124,6 @@ is more limited.
|
||||
This support is recommended for debugging purposes.
|
||||
It requires super-user privilege if used to inspect a running kernel.
|
||||
.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
|
||||
.Xr getifaddrs 3 ,
|
||||
.Xr getifmaddrs 3 ,
|
||||
|
@ -264,13 +264,13 @@ main(int argc, char **argv)
|
||||
usage();
|
||||
|
||||
#ifdef WITH_KVM
|
||||
if (!Kflag)
|
||||
if (Kflag)
|
||||
error = ifmcstat_kvm(kernel, core);
|
||||
/*
|
||||
* If KVM failed, and user did not explicitly specify a core file,
|
||||
* 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
|
||||
error = ifmcstat_getifmaddrs();
|
||||
if (error != 0)
|
||||
|
Loading…
Reference in New Issue
Block a user