diff options
author | bms <bms@FreeBSD.org> | 2009-04-29 19:19:13 +0000 |
---|---|---|
committer | bms <bms@FreeBSD.org> | 2009-04-29 19:19:13 +0000 |
commit | 32a71137f08bc028578417de36a241d7e6011f58 (patch) | |
tree | 51d9a006ee48417962ce45f044b7e5603910fe13 /sys/netinet6/in6_mcast.c | |
parent | 51a4d1c4a3d279a3638c0b40f351aa93f965c7df (diff) | |
download | FreeBSD-src-32a71137f08bc028578417de36a241d7e6011f58.zip FreeBSD-src-32a71137f08bc028578417de36a241d7e6011f58.tar.gz |
Bite the bullet, and make the IPv6 SSM and MLDv2 mega-commit:
import from p4 bms_netdev. Summary of changes:
* Connect netinet6/in6_mcast.c to build.
The legacy KAME KPIs are mostly preserved.
* Eliminate now dead code from ip6_output.c.
Don't do mbuf bingo, we are not going to do RFC 2292 style
CMSG tricks for multicast options as they are not required
by any current IPv6 normative reference.
* Refactor transports (UDP, raw_ip6) to do own mcast filtering.
SCTP, TCP unaffected by this change.
* Add ip6_msource, in6_msource structs to in6_var.h.
* Hookup mld_ifinfo state to in6_ifextra, allocate from
domifattach path.
* Eliminate IN6_LOOKUP_MULTI(), it is no longer referenced.
Kernel consumers which need this should use in6m_lookup().
* Refactor IPv6 socket group memberships to use a vector (like IPv4).
* Update ifmcstat(8) for IPv6 SSM.
* Add witness lock order for IN6_MULTI_LOCK.
* Move IN6_MULTI_LOCK out of lower ip6_output()/ip6_input() paths.
* Introduce IP6STAT_ADD/SUB/INC/DEC as per rwatson's IPv4 cleanup.
* Update carp(4) for new IPv6 SSM KPIs.
* Virtualize ip6_mrouter socket.
Changes mostly localized to IPv6 MROUTING.
* Don't do a local group lookup in MROUTING.
* Kill unused KAME prototypes in6_purgemkludge(), in6_restoremkludge().
* Preserve KAME DAD timer jitter behaviour in MLDv1 compatibility mode.
* Bump __FreeBSD_version to 800084.
* Update UPDATING.
NOTE WELL:
* This code hasn't been tested against real MLDv2 queriers
(yet), although the on-wire protocol has been verified in Wireshark.
* There are a few unresolved issues in the socket layer APIs to
do with scope ID propagation.
* There is a LOR present in ip6_output()'s use of
in6_setscope() which needs to be resolved. See comments in mld6.c.
This is believed to be benign and can't be avoided for the moment
without re-introducing an indirect netisr.
This work was mostly derived from the IGMPv3 implementation, and
has been sponsored by a third party.
Diffstat (limited to 'sys/netinet6/in6_mcast.c')
-rw-r--r-- | sys/netinet6/in6_mcast.c | 165 |
1 files changed, 137 insertions, 28 deletions
diff --git a/sys/netinet6/in6_mcast.c b/sys/netinet6/in6_mcast.c index a4d435c..b3f272c 100644 --- a/sys/netinet6/in6_mcast.c +++ b/sys/netinet6/in6_mcast.c @@ -29,6 +29,7 @@ /* * IPv6 multicast socket, group, and socket option processing module. + * Normative references: RFC 2292, RFC 3492, RFC 3542, RFC 3678, RFC 3810. */ #include <sys/cdefs.h> @@ -142,6 +143,9 @@ static struct ip6_moptions * static int in6p_get_source_filters(struct inpcb *, struct sockopt *); static int in6p_join_group(struct inpcb *, struct sockopt *); static int in6p_leave_group(struct inpcb *, struct sockopt *); +static struct ifnet * + in6p_lookup_mcast_ifp(const struct inpcb *, + const struct sockaddr_in6 *); static int in6p_block_unblock_source(struct inpcb *, struct sockopt *); static int in6p_set_multicast_if(struct inpcb *, struct sockopt *); static int in6p_set_source_filters(struct inpcb *, struct sockopt *); @@ -1655,12 +1659,12 @@ int ip6_getmoptions(struct inpcb *inp, struct sockopt *sopt) { INIT_VNET_INET6(curvnet); - struct ip6_moptions *imo; - int error, optval; - u_char coptval; + struct ip6_moptions *im6o; + int error; + u_int optval; INP_WLOCK(inp); - imo = inp->in6p_moptions; + im6o = inp->in6p_moptions; /* * If socket is neither of type SOCK_RAW or SOCK_DGRAM, * or is a divert socket, reject it. @@ -1674,38 +1678,36 @@ ip6_getmoptions(struct inpcb *inp, struct sockopt *sopt) error = 0; switch (sopt->sopt_name) { -#if 0 /* XXX FIXME */ case IPV6_MULTICAST_IF: - if (imo == NULL || imo->im6o_multicast_ifp == NULL) { + if (im6o == NULL || im6o->im6o_multicast_ifp == NULL) { optval = 0; } else { - optval = imo->im6o_multicast_ifp->if_index; + optval = im6o->im6o_multicast_ifp->if_index; } INP_WUNLOCK(inp); - error = sooptcopyout(sopt, &ifindex, sizeof(u_int)); + error = sooptcopyout(sopt, &optval, sizeof(u_int)); break; -#endif case IPV6_MULTICAST_HOPS: - if (imo == 0) - optval = coptval = V_ip6_defmcasthlim; + if (im6o == NULL) + optval = V_ip6_defmcasthlim; else - optval = coptval = imo->im6o_multicast_loop; + optval = im6o->im6o_multicast_loop; INP_WUNLOCK(inp); error = sooptcopyout(sopt, &optval, sizeof(u_int)); break; case IPV6_MULTICAST_LOOP: - if (imo == 0) - optval = coptval = IPV6_DEFAULT_MULTICAST_LOOP; + if (im6o == NULL) + optval = in6_mcast_loop; /* XXX VIMAGE */ else - optval = coptval = imo->im6o_multicast_loop; + optval = im6o->im6o_multicast_loop; INP_WUNLOCK(inp); error = sooptcopyout(sopt, &optval, sizeof(u_int)); break; case IPV6_MSFILTER: - if (imo == NULL) { + if (im6o == NULL) { error = EADDRNOTAVAIL; INP_WUNLOCK(inp); } else { @@ -1725,7 +1727,57 @@ ip6_getmoptions(struct inpcb *inp, struct sockopt *sopt) } /* + * Look up the ifnet to use for a multicast group membership, + * given the address of an IPv6 group. + * + * This routine exists to support legacy IPv6 multicast applications. + * + * If inp is non-NULL, use this socket's current FIB number for any + * required FIB lookup. Look up the group address in the unicast FIB, + * and use its ifp; usually, this points to the default next-hop. + * If the FIB lookup fails, return NULL. + * + * FUTURE: Support multiple forwarding tables for IPv6. + * + * Returns NULL if no ifp could be found. + */ +static struct ifnet * +in6p_lookup_mcast_ifp(const struct inpcb *in6p __unused, + const struct sockaddr_in6 *gsin6) +{ + INIT_VNET_INET6(curvnet); + struct route_in6 ro6; + struct ifnet *ifp; + + KASSERT(in6p->inp_vflag & INP_IPV6, + ("%s: not INP_IPV6 inpcb", __func__)); + KASSERT(gsin6->sin6_family == AF_INET6, + ("%s: not AF_INET6 group", __func__)); + KASSERT(IN6_IS_ADDR_MULTICAST(&gsin6->sin6_addr), + ("%s: not multicast", __func__)); + + ifp = NULL; + memset(&ro6, 0, sizeof(struct route_in6)); + memcpy(&ro6.ro_dst, gsin6, sizeof(struct sockaddr_in6)); +#ifdef notyet + rtalloc_ign_fib(&ro6, 0, inp ? inp->inp_inc.inc_fibnum : 0); +#else + rtalloc_ign((struct route *)&ro6, 0); +#endif + if (ro6.ro_rt != NULL) { + ifp = ro6.ro_rt->rt_ifp; + KASSERT(ifp != NULL, ("%s: null ifp", __func__)); + RTFREE(ro6.ro_rt); + } + + return (ifp); +} + +/* * Join an IPv6 multicast group, possibly with a source. + * + * FIXME: The KAME use of the unspecified address (::) + * to join *all* multicast groups is currently unsupported. */ static int in6p_join_group(struct inpcb *inp, struct sockopt *sopt) @@ -1765,8 +1817,14 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt) gsa->sin6.sin6_len = sizeof(struct sockaddr_in6); gsa->sin6.sin6_addr = mreq.ipv6mr_multiaddr; - ifp = ifnet_byindex(mreq.ipv6mr_interface); - + if (mreq.ipv6mr_interface == 0) { + ifp = in6p_lookup_mcast_ifp(inp, &gsa->sin6); + } 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; @@ -1813,12 +1871,35 @@ 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 + /* + * FIXME: Set interface scope in group address. + */ + (void)in6_setscope(&gsa->sin6.sin_addr, ifp, NULL); +#endif + /* * MCAST_JOIN_SOURCE on an exclusive membership is an error. * On an existing inclusive membership, it just adds the @@ -1987,7 +2068,23 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt) gsa->sin6.sin6_family = AF_INET6; gsa->sin6.sin6_len = sizeof(struct sockaddr_in6); gsa->sin6.sin6_addr = mreq.ipv6mr_multiaddr; - ifp = ifnet_byindex(mreq.ipv6mr_interface); + + 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); @@ -2033,6 +2130,15 @@ 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. + */ + (void)in6_setscope(&mreq->ipv6mr_multiaddr, ifp, NULL); +#endif + /* * Find the membership in the membership array. */ @@ -2348,7 +2454,7 @@ out_in6p_locked: int ip6_setmoptions(struct inpcb *inp, struct sockopt *sopt) { - struct ip6_moptions *imo; + struct ip6_moptions *im6o; int error; error = 0; @@ -2364,7 +2470,6 @@ ip6_setmoptions(struct inpcb *inp, struct sockopt *sopt) switch (sopt->sopt_name) { case IPV6_MULTICAST_IF: - /* XXX in v6 this one is far more involved */ error = in6p_set_multicast_if(inp, sopt); break; @@ -2381,9 +2486,11 @@ ip6_setmoptions(struct inpcb *inp, struct sockopt *sopt) if (hlim < -1 || hlim > 255) { error = EINVAL; break; + } else if (hlim == -1) { + hlim = V_ip6_defmcasthlim; } - imo = in6p_findmoptions(inp); - imo->im6o_multicast_hlim = hlim; + im6o = in6p_findmoptions(inp); + im6o->im6o_multicast_hlim = hlim; INP_WUNLOCK(inp); break; } @@ -2393,9 +2500,7 @@ ip6_setmoptions(struct inpcb *inp, struct sockopt *sopt) /* * Set the loopback flag for outgoing multicast packets. - * Must be zero or one. The orimcaddrl multicast API required a - * char argument, which is inconsistent with the rest - * of the socket API. We allow either a char or an int. + * Must be zero or one. */ if (sopt->sopt_valsize != sizeof(u_int)) { error = EINVAL; @@ -2404,8 +2509,12 @@ ip6_setmoptions(struct inpcb *inp, struct sockopt *sopt) error = sooptcopyin(sopt, &loop, sizeof(u_int), sizeof(u_int)); if (error) break; - imo = in6p_findmoptions(inp); - imo->im6o_multicast_loop = loop; + if (loop > 1) { + error = EINVAL; + break; + } + im6o = in6p_findmoptions(inp); + im6o->im6o_multicast_loop = loop; INP_WUNLOCK(inp); break; } |