svn commit: r191672 - in head: . sys/conf sys/kern sys/netinet
sys/netinet6 sys/sys usr.sbin/ifmcstat
Bruce M Simpson
bms at FreeBSD.org
Wed Apr 29 19:19:14 UTC 2009
Author: bms
Date: Wed Apr 29 19:19:13 2009
New Revision: 191672
URL: http://svn.freebsd.org/changeset/base/191672
Log:
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.
Modified:
head/UPDATING
head/sys/conf/files
head/sys/kern/subr_witness.c
head/sys/netinet/ip_carp.c
head/sys/netinet6/icmp6.c
head/sys/netinet6/in6.c
head/sys/netinet6/in6_ifattach.c
head/sys/netinet6/in6_mcast.c
head/sys/netinet6/in6_pcb.c
head/sys/netinet6/in6_proto.c
head/sys/netinet6/in6_var.h
head/sys/netinet6/ip6_input.c
head/sys/netinet6/ip6_mroute.c
head/sys/netinet6/ip6_output.c
head/sys/netinet6/ip6_var.h
head/sys/netinet6/mld6.c
head/sys/netinet6/mld6_var.h
head/sys/netinet6/raw_ip6.c
head/sys/netinet6/udp6_usrreq.c
head/sys/netinet6/vinet6.h
head/sys/sys/param.h
head/usr.sbin/ifmcstat/ifmcstat.c
Modified: head/UPDATING
==============================================================================
--- head/UPDATING Wed Apr 29 18:41:08 2009 (r191671)
+++ head/UPDATING Wed Apr 29 19:19:13 2009 (r191672)
@@ -22,6 +22,92 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 8.
to maximize performance. (To disable malloc debugging, run
ln -s aj /etc/malloc.conf.)
+20090429:
+ MLDv2 and Source-Specific Multicast (SSM) have been merged
+ to the IPv6 stack. VIMAGE hooks are in but not yet used.
+ The implementation of SSM within FreeBSD's IPv6 stack closely
+ follows the IPv4 implementation.
+
+ For kernel developers:
+
+ * The most important changes are that the ip6_output() and
+ ip6_input() paths no longer take the IN6_MULTI_LOCK,
+ and this lock has been downgraded to a non-recursive mutex.
+
+ * As with the changes to the IPv4 stack to support SSM, filtering
+ of inbound multicast traffic must now be performed by transport
+ protocols within the IPv6 stack. This does not apply to TCP and
+ SCTP, however, it does apply to UDP in IPv6 and raw IPv6.
+
+ * The KPIs used by IPv6 multicast are similar to those used by
+ the IPv4 stack, with the following differences:
+ * im6o_mc_filter() is analogous to imo_multicast_filter().
+ * The legacy KAME entry points in6_joingroup and in6_leavegroup()
+ are shimmed to in6_mc_join() and in6_mc_leave() respectively.
+ * IN6_LOOKUP_MULTI() has been deprecated and removed.
+ * IPv6 relies on MLD for the DAD mechanism. KAME's internal KPIs
+ for MLDv1 have an additional 'timer' argument which is used to
+ jitter the initial membership report for the solicited-node
+ multicast membership on-link.
+ * This is not strictly needed for MLDv2, which already jitters
+ its report transmissions. However, the 'timer' argument is
+ preserved in case MLDv1 is active on the interface.
+
+ * The KAME linked-list based IPv6 membership implementation has
+ been refactored to use a vector similar to that used by the IPv4
+ stack.
+ Code which maintains a list of its own multicast memberships
+ internally, e.g. carp, has been updated to reflect the new
+ semantics.
+
+ * There is a known Lock Order Reversal (LOR) due to in6_setscope()
+ acquiring the IF_AFDATA_LOCK and being called within ip6_output().
+ Whilst MLDv2 tries to avoid this otherwise benign LOR, it is an
+ implementation constraint which needs to be addressed in HEAD.
+
+ For application developers:
+
+ * The changes are broadly similar to those made for the IPv4
+ stack.
+
+ * The use of IPv4 and IPv6 multicast socket options on the same
+ socket, using mapped addresses, HAS NOT been tested or supported.
+
+ * There are a number of issues with the implementation of various
+ IPv6 multicast APIs which need to be resolved in the API surface
+ before the implementation is fully compatible with KAME userland
+ use, and these are mostly to do with interface index treatment.
+
+ * The literature available discusses the use of either the delta / ASM
+ API with setsockopt(2)/getsockopt(2), or the full-state / ASM API
+ using setsourcefilter(3)/getsourcefilter(3). For more information
+ please refer to RFC 3768, 'Socket Interface Extensions for
+ Multicast Source Filters'.
+
+ * Applications which use the published RFC 3678 APIs should be fine.
+
+ For systems administrators:
+
+ * The mtest(8) utility has been refactored to support IPv6, in
+ addition to IPv4. Interface addresses are no longer accepted
+ as arguments, their names must be used instead. The utility
+ will map the interface name to its first IPv4 address as
+ returned by getifaddrs(3).
+
+ * The ifmcstat(8) utility has also been updated to print the MLDv2
+ endpoint state and source filter lists via sysctl(3).
+
+ * The net.inet6.ip6.mcast.loop sysctl may be tuned to 0 to disable
+ loopback of IPv6 multicast datagrams by default; it defaults to 1
+ to preserve the existing behaviour. Disabling multicast loopback is
+ recommended for optimal system performance.
+
+ * The IPv6 MROUTING code has been changed to examine this sysctl
+ instead of attempting to perform a group lookup before looping
+ back forwarded datagrams.
+
+ Bump __FreeBSD_version to 800084.
+
20090422:
Implement low-level Bluetooth HCI API.
Bump __FreeBSD_version to 800083.
Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files Wed Apr 29 18:41:08 2009 (r191671)
+++ head/sys/conf/files Wed Apr 29 19:19:13 2009 (r191672)
@@ -2381,6 +2381,7 @@ netinet6/in6.c optional inet6
netinet6/in6_cksum.c optional inet6
netinet6/in6_gif.c optional gif inet6
netinet6/in6_ifattach.c optional inet6
+netinet6/in6_mcast.c optional inet6
netinet6/in6_pcb.c optional inet6
netinet6/in6_proto.c optional inet6
netinet6/in6_rmx.c optional inet6
Modified: head/sys/kern/subr_witness.c
==============================================================================
--- head/sys/kern/subr_witness.c Wed Apr 29 18:41:08 2009 (r191671)
+++ head/sys/kern/subr_witness.c Wed Apr 29 19:19:13 2009 (r191672)
@@ -512,7 +512,8 @@ static struct witness_order_list_entry o
{ "ifaddr", &lock_class_mtx_sleep },
{ NULL, NULL },
/*
- * Multicast - protocol locks before interface locks, after UDP locks.
+ * IPv4 multicast:
+ * protocol locks before interface locks, after UDP locks.
*/
{ "udpinp", &lock_class_rw },
{ "in_multi_mtx", &lock_class_mtx_sleep },
@@ -520,6 +521,15 @@ static struct witness_order_list_entry o
{ "if_addr_mtx", &lock_class_mtx_sleep },
{ NULL, NULL },
/*
+ * IPv6 multicast:
+ * protocol locks before interface locks, after UDP locks.
+ */
+ { "udpinp", &lock_class_rw },
+ { "in6_multi_mtx", &lock_class_mtx_sleep },
+ { "mld_mtx", &lock_class_mtx_sleep },
+ { "if_addr_mtx", &lock_class_mtx_sleep },
+ { NULL, NULL },
+ /*
* UNIX Domain Sockets
*/
{ "unp_global_rwlock", &lock_class_rw },
Modified: head/sys/netinet/ip_carp.c
==============================================================================
--- head/sys/netinet/ip_carp.c Wed Apr 29 18:41:08 2009 (r191671)
+++ head/sys/netinet/ip_carp.c Wed Apr 29 19:19:13 2009 (r191672)
@@ -400,15 +400,20 @@ carp_clone_create(struct if_clone *ifc,
sc->sc_advskew = 0;
sc->sc_init_counter = 1;
sc->sc_naddrs = sc->sc_naddrs6 = 0; /* M_ZERO? */
-#ifdef INET6
- sc->sc_im6o.im6o_multicast_hlim = CARP_DFLTTL;
-#endif
sc->sc_imo.imo_membership = (struct in_multi **)malloc(
(sizeof(struct in_multi *) * IP_MIN_MEMBERSHIPS), M_CARP,
M_WAITOK);
sc->sc_imo.imo_mfilters = NULL;
sc->sc_imo.imo_max_memberships = IP_MIN_MEMBERSHIPS;
sc->sc_imo.imo_multicast_vif = -1;
+#ifdef INET6
+ sc->sc_im6o.im6o_membership = (struct in6_multi **)malloc(
+ (sizeof(struct in6_multi *) * IPV6_MIN_MEMBERSHIPS), M_CARP,
+ M_WAITOK);
+ sc->sc_im6o.im6o_mfilters = NULL;
+ sc->sc_im6o.im6o_max_memberships = IPV6_MIN_MEMBERSHIPS;
+ sc->sc_im6o.im6o_multicast_hlim = CARP_DFLTTL;
+#endif
callout_init(&sc->sc_ad_tmo, CALLOUT_MPSAFE);
callout_init(&sc->sc_md_tmo, CALLOUT_MPSAFE);
@@ -448,6 +453,9 @@ carp_clone_destroy(struct ifnet *ifp)
if_detach(ifp);
if_free_type(ifp, IFT_ETHER);
free(sc->sc_imo.imo_membership, M_CARP);
+#ifdef INET6
+ free(sc->sc_im6o.im6o_membership, M_CARP);
+#endif
free(sc, M_CARP);
}
@@ -1449,14 +1457,17 @@ static void
carp_multicast6_cleanup(struct carp_softc *sc)
{
struct ip6_moptions *im6o = &sc->sc_im6o;
+ u_int16_t n = im6o->im6o_num_memberships;
- while (!LIST_EMPTY(&im6o->im6o_memberships)) {
- struct in6_multi_mship *imm =
- LIST_FIRST(&im6o->im6o_memberships);
-
- LIST_REMOVE(imm, i6mm_chain);
- in6_leavegroup(imm);
+ while (n-- > 0) {
+ if (im6o->im6o_membership[n] != NULL) {
+ in6_mc_leave(im6o->im6o_membership[n], NULL);
+ im6o->im6o_membership[n] = NULL;
+ }
}
+ KASSERT(im6o->im6o_mfilters == NULL,
+ ("%s: im6o_mfilters != NULL", __func__));
+ im6o->im6o_num_memberships = 0;
im6o->im6o_multicast_ifp = NULL;
}
#endif
@@ -1635,10 +1646,11 @@ carp_set_addr6(struct carp_softc *sc, st
struct carp_if *cif;
struct in6_ifaddr *ia, *ia_if;
struct ip6_moptions *im6o = &sc->sc_im6o;
- struct in6_multi_mship *imm;
struct in6_addr in6;
int own, error;
+ error = 0;
+
if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
if (!(SC2IFP(sc)->if_flags & IFF_UP))
carp_set_state(sc, INIT);
@@ -1686,6 +1698,8 @@ carp_set_addr6(struct carp_softc *sc, st
return (EADDRNOTAVAIL);
if (!sc->sc_naddrs6) {
+ struct in6_multi *in6m;
+
im6o->im6o_multicast_ifp = ifp;
/* join CARP multicast address */
@@ -1694,9 +1708,12 @@ carp_set_addr6(struct carp_softc *sc, st
in6.s6_addr8[15] = 0x12;
if (in6_setscope(&in6, ifp, NULL) != 0)
goto cleanup;
- if ((imm = in6_joingroup(ifp, &in6, &error, 0)) == NULL)
+ in6m = NULL;
+ error = in6_mc_join(ifp, &in6, NULL, &in6m, 0);
+ if (error)
goto cleanup;
- LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
+ im6o->im6o_membership[0] = in6m;
+ im6o->im6o_num_memberships++;
/* join solicited multicast address */
bzero(&in6, sizeof(in6));
@@ -1707,9 +1724,12 @@ carp_set_addr6(struct carp_softc *sc, st
in6.s6_addr8[12] = 0xff;
if (in6_setscope(&in6, ifp, NULL) != 0)
goto cleanup;
- if ((imm = in6_joingroup(ifp, &in6, &error, 0)) == NULL)
+ in6m = NULL;
+ error = in6_mc_join(ifp, &in6, NULL, &in6m, 0);
+ if (error)
goto cleanup;
- LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
+ im6o->im6o_membership[1] = in6m;
+ im6o->im6o_num_memberships++;
}
if (!ifp->if_carp) {
@@ -1781,14 +1801,8 @@ carp_set_addr6(struct carp_softc *sc, st
return (0);
cleanup:
- /* clean up multicast memberships */
- if (!sc->sc_naddrs6) {
- while (!LIST_EMPTY(&im6o->im6o_memberships)) {
- imm = LIST_FIRST(&im6o->im6o_memberships);
- LIST_REMOVE(imm, i6mm_chain);
- in6_leavegroup(imm);
- }
- }
+ if (!sc->sc_naddrs6)
+ carp_multicast6_cleanup(sc);
return (error);
}
@@ -1799,21 +1813,13 @@ carp_del_addr6(struct carp_softc *sc, st
if (!--sc->sc_naddrs6) {
struct carp_if *cif = (struct carp_if *)sc->sc_carpdev->if_carp;
- struct ip6_moptions *im6o = &sc->sc_im6o;
CARP_LOCK(cif);
callout_stop(&sc->sc_ad_tmo);
SC2IFP(sc)->if_flags &= ~IFF_UP;
SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING;
sc->sc_vhid = -1;
- while (!LIST_EMPTY(&im6o->im6o_memberships)) {
- struct in6_multi_mship *imm =
- LIST_FIRST(&im6o->im6o_memberships);
-
- LIST_REMOVE(imm, i6mm_chain);
- in6_leavegroup(imm);
- }
- im6o->im6o_multicast_ifp = NULL;
+ carp_multicast6_cleanup(sc);
TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list);
if (!--cif->vhif_nvrs) {
CARP_LOCK_DESTROY(cif);
Modified: head/sys/netinet6/icmp6.c
==============================================================================
--- head/sys/netinet6/icmp6.c Wed Apr 29 18:41:08 2009 (r191671)
+++ head/sys/netinet6/icmp6.c Wed Apr 29 19:19:13 2009 (r191672)
@@ -147,8 +147,6 @@ icmp6_init(void)
INIT_VNET_INET6(curvnet);
V_icmp6errpps_count = 0;
-
- mld6_init();
}
static void
@@ -429,6 +427,23 @@ icmp6_input(struct mbuf **mp, int *offp,
}
/*
+ * Check multicast group membership.
+ * Note: SSM filters are not applied for ICMPv6 traffic.
+ */
+ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+ struct ifnet *ifp;
+ struct in6_multi *inm;
+
+ ifp = m->m_pkthdr.rcvif;
+ inm = in6m_lookup(ifp, &ip6->ip6_dst);
+ if (inm == NULL) {
+ IP6STAT_INC(ip6s_notmember);
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard);
+ goto freeit;
+ }
+ }
+
+ /*
* calculate the checksum
*/
#ifndef PULLDOWN_TEST
@@ -615,34 +630,20 @@ icmp6_input(struct mbuf **mp, int *offp,
case MLD_LISTENER_QUERY:
case MLD_LISTENER_REPORT:
- if (icmp6len < sizeof(struct mld_hdr))
- goto badlen;
- if (icmp6->icmp6_type == MLD_LISTENER_QUERY) /* XXX: ugly... */
- icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldquery);
- else
- icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldreport);
- if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
- /* give up local */
- mld6_input(m, off);
- m = NULL;
+ case MLD_LISTENER_DONE:
+ case MLDV2_LISTENER_REPORT:
+ /*
+ * Drop MLD traffic which is not link-local.
+ * XXX Should we also sanity check that these messages
+ * were directed to a link-local multicast prefix?
+ */
+ if (ip6->ip6_hlim != 1)
goto freeit;
- }
- mld6_input(n, off);
+ if (mld_input(m, off, icmp6len) != 0)
+ return (IPPROTO_DONE);
/* m stays. */
break;
- case MLD_LISTENER_DONE:
- icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mlddone);
- if (icmp6len < sizeof(struct mld_hdr)) /* necessary? */
- goto badlen;
- break; /* nothing to be done in kernel */
-
- case MLD_MTRACE_RESP:
- case MLD_MTRACE:
- /* XXX: these two are experimental. not officially defined. */
- /* XXX: per-interface statistics? */
- break; /* just pass it to applications */
-
case ICMP6_WRUREQUEST: /* ICMP6_FQDN_QUERY */
{
enum { WRU, FQDN } mode;
@@ -2050,7 +2051,7 @@ icmp6_rip6_input(struct mbuf **mp, int o
INP_RUNLOCK(last);
} else {
m_freem(m);
- V_ip6stat.ip6s_delivered--;
+ IP6STAT_DEC(ip6s_delivered);
}
return IPPROTO_DONE;
}
@@ -2222,7 +2223,14 @@ void
icmp6_fasttimo(void)
{
- return;
+ mld_fasttimo();
+}
+
+void
+icmp6_slowtimo(void)
+{
+
+ mld_slowtimo();
}
static const char *
Modified: head/sys/netinet6/in6.c
==============================================================================
--- head/sys/netinet6/in6.c Wed Apr 29 18:41:08 2009 (r191671)
+++ head/sys/netinet6/in6.c Wed Apr 29 19:19:13 2009 (r191672)
@@ -106,8 +106,6 @@ __FBSDID("$FreeBSD$");
#include <netinet6/in6_pcb.h>
#include <netinet6/vinet6.h>
-MALLOC_DEFINE(M_IP6MADDR, "in6_multi", "internet multicast address");
-
/*
* Definitions of some costant IP6 addresses.
*/
@@ -119,6 +117,8 @@ const struct in6_addr in6addr_linklocal_
IN6ADDR_LINKLOCAL_ALLNODES_INIT;
const struct in6_addr in6addr_linklocal_allrouters =
IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
+const struct in6_addr in6addr_linklocal_allv2routers =
+ IN6ADDR_LINKLOCAL_ALLV2ROUTERS_INIT;
const struct in6_addr in6mask0 = IN6MASK0;
const struct in6_addr in6mask32 = IN6MASK32;
@@ -135,7 +135,6 @@ static int in6_ifinit __P((struct ifnet
struct sockaddr_in6 *, int));
static void in6_unlink_ifa(struct in6_ifaddr *, struct ifnet *);
-struct in6_multihead in6_multihead; /* XXX BSS initialization */
int (*faithprefix_p)(struct in6_addr *);
@@ -1110,10 +1109,12 @@ in6_update_ifa(struct ifnet *ifp, struct
* should be larger than the MLD delay (this could be
* relaxed a bit, but this simple logic is at least
* safe).
+ * XXX: Break data hiding guidelines and look at
+ * state for the solicited multicast group.
*/
mindelay = 0;
if (in6m_sol != NULL &&
- in6m_sol->in6m_state == MLD_REPORTPENDING) {
+ in6m_sol->in6m_state == MLD_REPORTING_MEMBER) {
mindelay = in6m_sol->in6m_timer;
}
maxdelay = MAX_RTR_SOLICITATION_DELAY * hz;
@@ -1590,36 +1591,6 @@ in6_ifinit(struct ifnet *ifp, struct in6
return (error);
}
-struct in6_multi_mship *
-in6_joingroup(struct ifnet *ifp, struct in6_addr *addr,
- int *errorp, int delay)
-{
- struct in6_multi_mship *imm;
-
- imm = malloc(sizeof(*imm), M_IP6MADDR, M_NOWAIT);
- if (!imm) {
- *errorp = ENOBUFS;
- return NULL;
- }
- imm->i6mm_maddr = in6_addmulti(addr, ifp, errorp, delay);
- if (!imm->i6mm_maddr) {
- /* *errorp is alrady set */
- free(imm, M_IP6MADDR);
- return NULL;
- }
- return imm;
-}
-
-int
-in6_leavegroup(struct in6_multi_mship *imm)
-{
-
- if (imm->i6mm_maddr)
- in6_delmulti(imm->i6mm_maddr);
- free(imm, M_IP6MADDR);
- return 0;
-}
-
/*
* Find an IPv6 interface link-local address specific to an interface.
*/
@@ -2328,6 +2299,9 @@ in6_domifattach(struct ifnet *ifp)
ext->lltable->llt_lookup = in6_lltable_lookup;
ext->lltable->llt_dump = in6_lltable_dump;
}
+
+ ext->mld_ifinfo = mld_domifattach(ifp);
+
return ext;
}
@@ -2336,6 +2310,7 @@ in6_domifdetach(struct ifnet *ifp, void
{
struct in6_ifextra *ext = (struct in6_ifextra *)aux;
+ mld_domifdetach(ifp);
scope6_ifdetach(ext->scope6_id);
nd6_ifdetach(ext->nd_ifinfo);
lltable_free(ext->lltable);
Modified: head/sys/netinet6/in6_ifattach.c
==============================================================================
--- head/sys/netinet6/in6_ifattach.c Wed Apr 29 18:41:08 2009 (r191671)
+++ head/sys/netinet6/in6_ifattach.c Wed Apr 29 19:19:13 2009 (r191672)
@@ -63,6 +63,7 @@ __FBSDID("$FreeBSD$");
#include <netinet6/in6_ifattach.h>
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
+#include <netinet6/mld6_var.h>
#include <netinet6/scope6_var.h>
#include <netinet6/vinet6.h>
@@ -918,11 +919,35 @@ in6_tmpaddrtimer(void *ignored_arg)
static void
in6_purgemaddrs(struct ifnet *ifp)
{
- struct in6_multi *in6m;
- struct in6_multi *oin6m;
+ INIT_VNET_INET6(ifp->if_vnet);
+ LIST_HEAD(,in6_multi) purgeinms;
+ struct in6_multi *inm, *tinm;
+ struct ifmultiaddr *ifma;
+
+ LIST_INIT(&purgeinms);
+ IN6_MULTI_LOCK();
- LIST_FOREACH_SAFE(in6m, &in6_multihead, in6m_entry, oin6m) {
- if (in6m->in6m_ifp == ifp)
- in6_delmulti(in6m);
+ /*
+ * Extract list of in6_multi associated with the detaching ifp
+ * which the PF_INET6 layer is about to release.
+ * We need to do this as IF_ADDR_LOCK() may be re-acquired
+ * by code further down.
+ */
+ IF_ADDR_LOCK(ifp);
+ 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;
+ LIST_INSERT_HEAD(&purgeinms, inm, in6m_entry);
}
+ IF_ADDR_UNLOCK(ifp);
+
+ LIST_FOREACH_SAFE(inm, &purgeinms, in6m_entry, tinm) {
+ LIST_REMOVE(inm, in6m_entry);
+ in6m_release_locked(inm);
+ }
+ mld_ifdetach(ifp);
+
+ IN6_MULTI_UNLOCK();
}
Modified: head/sys/netinet6/in6_mcast.c
==============================================================================
--- head/sys/netinet6/in6_mcast.c Wed Apr 29 18:41:08 2009 (r191671)
+++ head/sys/netinet6/in6_mcast.c Wed Apr 29 19:19:13 2009 (r191672)
@@ -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, struc
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, struc
}
/*
+ * 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, struc
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, struc
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, stru
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, stru
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, struc
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, struc
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, struc
/*
* 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, struc
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;
}
Modified: head/sys/netinet6/in6_pcb.c
==============================================================================
--- head/sys/netinet6/in6_pcb.c Wed Apr 29 18:41:08 2009 (r191671)
+++ head/sys/netinet6/in6_pcb.c Wed Apr 29 19:19:13 2009 (r191672)
@@ -733,36 +733,36 @@ in6_pcbpurgeif0(struct inpcbinfo *pcbinf
{
struct inpcb *in6p;
struct ip6_moptions *im6o;
- struct in6_multi_mship *imm, *nimm;
+ int i, gap;
INP_INFO_RLOCK(pcbinfo);
LIST_FOREACH(in6p, pcbinfo->ipi_listhead, inp_list) {
INP_WLOCK(in6p);
im6o = in6p->in6p_moptions;
- if ((in6p->inp_vflag & INP_IPV6) &&
- im6o) {
+ if ((in6p->inp_vflag & INP_IPV6) && im6o != NULL) {
/*
- * Unselect the outgoing interface if it is being
- * detached.
+ * Unselect the outgoing ifp for multicast if it
+ * is being detached.
*/
if (im6o->im6o_multicast_ifp == ifp)
im6o->im6o_multicast_ifp = NULL;
-
/*
* Drop multicast group membership if we joined
* through the interface being detached.
- * XXX controversial - is it really legal for kernel
- * to force this?
*/
- for (imm = im6o->im6o_memberships.lh_first;
- imm != NULL; imm = nimm) {
- nimm = imm->i6mm_chain.le_next;
- if (imm->i6mm_maddr->in6m_ifp == ifp) {
- LIST_REMOVE(imm, i6mm_chain);
- in6_delmulti(imm->i6mm_maddr);
- free(imm, M_IP6MADDR);
+ gap = 0;
+ for (i = 0; i < im6o->im6o_num_memberships; i++) {
+ if (im6o->im6o_membership[i]->in6m_ifp ==
+ ifp) {
+ in6_mc_leave(im6o->im6o_membership[i],
+ NULL);
+ gap++;
+ } else if (gap != 0) {
+ im6o->im6o_membership[i - gap] =
+ im6o->im6o_membership[i];
}
}
+ im6o->im6o_num_memberships -= gap;
}
INP_WUNLOCK(in6p);
}
Modified: head/sys/netinet6/in6_proto.c
==============================================================================
--- head/sys/netinet6/in6_proto.c Wed Apr 29 18:41:08 2009 (r191671)
+++ head/sys/netinet6/in6_proto.c Wed Apr 29 19:19:13 2009 (r191672)
@@ -236,6 +236,7 @@ struct ip6protosw inet6sw[] = {
.pr_ctloutput = rip6_ctloutput,
.pr_init = icmp6_init,
.pr_fasttimo = icmp6_fasttimo,
+ .pr_slowtimo = icmp6_slowtimo,
.pr_usrreqs = &rip6_usrreqs
},
{
Modified: head/sys/netinet6/in6_var.h
==============================================================================
--- head/sys/netinet6/in6_var.h Wed Apr 29 18:41:08 2009 (r191671)
+++ head/sys/netinet6/in6_var.h Wed Apr 29 19:19:13 2009 (r191672)
@@ -64,6 +64,12 @@
#ifndef _NETINET6_IN6_VAR_H_
#define _NETINET6_IN6_VAR_H_
+#include <sys/tree.h>
+
+#ifdef _KERNEL
+#include <sys/libkern.h>
+#endif
+
/*
* Interface address, Internet version. One of these structures
* is allocated for each interface with an Internet address.
@@ -89,12 +95,15 @@ struct in6_addrlifetime {
struct nd_ifinfo;
struct scope6_id;
struct lltable;
+struct mld_ifinfo;
+
struct in6_ifextra {
struct in6_ifstat *in6_ifstat;
struct icmp6_ifstat *icmp6_ifstat;
struct nd_ifinfo *nd_ifinfo;
struct scope6_id *scope6_id;
struct lltable *lltable;
+ struct mld_ifinfo *mld_ifinfo;
};
#define LLTABLE6(ifp) (((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->lltable)
@@ -489,9 +498,6 @@ do { \
extern struct in6_addr zeroin6_addr;
extern u_char inet6ctlerrmap[];
-#ifdef MALLOC_DECLARE
-MALLOC_DECLARE(M_IP6MADDR);
-#endif /* MALLOC_DECLARE */
/*
* Macro for finding the internet address structure (in6_ifaddr) corresponding
@@ -514,94 +520,243 @@ do { \
#endif /* _KERNEL */
/*
- * Multi-cast membership entry. One for each group/ifp that a PCB
- * belongs to.
+ * IPv6 multicast MLD-layer source entry.
+ */
+struct ip6_msource {
+ RB_ENTRY(ip6_msource) im6s_link; /* RB tree links */
+ struct in6_addr im6s_addr;
+ struct im6s_st {
+ uint16_t ex; /* # of exclusive members */
+ uint16_t in; /* # of inclusive members */
+ } im6s_st[2]; /* state at t0, t1 */
+ uint8_t im6s_stp; /* pending query */
+};
+RB_HEAD(ip6_msource_tree, ip6_msource);
+
+/*
+ * IPv6 multicast PCB-layer source entry.
+ *
+ * NOTE: overlapping use of struct ip6_msource fields at start.
+ */
+struct in6_msource {
+ RB_ENTRY(ip6_msource) im6s_link; /* Common field */
+ struct in6_addr im6s_addr; /* Common field */
+ uint8_t im6sl_st[2]; /* state before/at commit */
+};
+
+#ifdef _KERNEL
+/*
+ * IPv6 source tree comparison function.
+ *
+ * An ordered predicate is necessary; bcmp() is not documented to return
+ * an indication of order, memcmp() is, and is an ISO C99 requirement.
+ */
+static __inline int
+ip6_msource_cmp(const struct ip6_msource *a, const struct ip6_msource *b)
+{
+
+ return (memcmp(&a->im6s_addr, &b->im6s_addr, sizeof(struct in6_addr)));
+}
+RB_PROTOTYPE(ip6_msource_tree, ip6_msource, im6s_link, ip6_msource_cmp);
+#endif /* _KERNEL */
+
+/*
+ * IPv6 multicast PCB-layer group filter descriptor.
+ */
+struct in6_mfilter {
+ struct ip6_msource_tree im6f_sources; /* source list for (S,G) */
+ u_long im6f_nsrc; /* # of source entries */
+ uint8_t im6f_st[2]; /* state before/at commit */
+};
+
+/*
+ * Legacy KAME IPv6 multicast membership descriptor.
*/
struct in6_multi_mship {
- struct in6_multi *i6mm_maddr; /* Multicast address pointer */
- LIST_ENTRY(in6_multi_mship) i6mm_chain; /* multicast options chain */
+ struct in6_multi *i6mm_maddr;
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-head
mailing list