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.
This commit is contained in:
Bruce M Simpson 2009-04-29 19:19:13 +00:00
parent af7bd9a4f4
commit 33cde13046
22 changed files with 4077 additions and 1190 deletions

View File

@ -22,6 +22,92 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 8.x IS SLOW:
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.

View File

@ -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

View File

@ -512,13 +512,23 @@ static struct witness_order_list_entry order_lists[] = {
{ "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 },
{ "igmp_mtx", &lock_class_mtx_sleep },
{ "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
*/

View File

@ -400,15 +400,20 @@ carp_clone_create(struct if_clone *ifc, int unit, caddr_t params)
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, struct sockaddr_in6 *sin6)
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, struct sockaddr_in6 *sin6)
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, struct sockaddr_in6 *sin6)
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, struct sockaddr_in6 *sin6)
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, struct sockaddr_in6 *sin6)
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, struct sockaddr_in6 *sin6)
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);

View File

@ -147,8 +147,6 @@ icmp6_init(void)
INIT_VNET_INET6(curvnet);
V_icmp6errpps_count = 0;
mld6_init();
}
static void
@ -428,6 +426,23 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
goto freeit;
}
/*
* 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
*/
@ -615,34 +630,20 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
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 off)
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 *

View File

@ -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_allnodes =
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 in6_ifaddr *,
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 in6_aliasreq *ifra,
* 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_ifaddr *ia,
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 *aux)
{
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);

View File

@ -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_FOREACH_SAFE(in6m, &in6_multihead, in6m_entry, oin6m) {
if (in6m->in6m_ifp == ifp)
in6_delmulti(in6m);
LIST_INIT(&purgeinms);
IN6_MULTI_LOCK();
/*
* 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();
}

View File

@ -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 {
@ -1724,8 +1726,58 @@ ip6_getmoptions(struct inpcb *inp, struct sockopt *sopt)
return (error);
}
/*
* 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;
}

View File

@ -733,36 +733,36 @@ in6_pcbpurgeif0(struct inpcbinfo *pcbinfo, struct ifnet *ifp)
{
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);
}

View File

@ -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
},
{

View File

@ -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 in6_multi_mship {
struct in6_multi *i6mm_maddr; /* Multicast address pointer */
LIST_ENTRY(in6_multi_mship) i6mm_chain; /* multicast options chain */
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);
struct in6_multi {
LIST_ENTRY(in6_multi) in6m_entry; /* list glue */
struct in6_addr in6m_addr; /* IP6 multicast address */
struct ifnet *in6m_ifp; /* back pointer to ifnet */
struct ifmultiaddr *in6m_ifma; /* back pointer to ifmultiaddr */
u_int in6m_refcount; /* # membership claims by sockets */
u_int in6m_state; /* state of the membership */
u_int in6m_timer; /* MLD6 listener report timer */
struct timeval in6m_timer_expire; /* when the timer expires */
struct callout *in6m_timer_ch;
/*
* 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 */
};
#define IN6M_TIMER_UNDEF -1
#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;
LIST_ENTRY(in6_multi_mship) i6mm_chain;
};
/*
* IPv6 group descriptor.
*
* For every entry on an ifnet's if_multiaddrs list which represents
* an IP multicast group, there is one of these structures.
*
* If any source filters are present, then a node will exist in the RB-tree
* to permit fast lookup by source whenever an operation takes place.
* This permits pre-order traversal when we issue reports.
* Source filter trees are kept separately from the socket layer to
* greatly simplify locking.
*
* When MLDv2 is active, in6m_timer is the response to group query timer.
* The state-change timer in6m_sctimer is separate; whenever state changes
* for the group the state change record is generated and transmitted,
* and kept if retransmissions are necessary.
*
* FUTURE: in6m_link is now only used when groups are being purged
* on a detaching ifnet. It could be demoted to a SLIST_ENTRY, but
* because it is at the very start of the struct, we can't do this
* w/o breaking the ABI for ifmcstat.
*/
struct in6_multi {
LIST_ENTRY(in6_multi) in6m_entry; /* list glue */
struct in6_addr in6m_addr; /* IPv6 multicast address */
struct ifnet *in6m_ifp; /* back pointer to ifnet */
struct ifmultiaddr *in6m_ifma; /* back pointer to ifmultiaddr */
u_int in6m_refcount; /* reference count */
u_int in6m_state; /* state of the membership */
u_int in6m_timer; /* MLD6 listener report timer */
/* New fields for MLDv2 follow. */
struct mld_ifinfo *in6m_mli; /* MLD info */
SLIST_ENTRY(in6_multi) in6m_nrele; /* to-be-released by MLD */
struct ip6_msource_tree in6m_srcs; /* tree of sources */
u_long in6m_nsrc; /* # of tree entries */
struct ifqueue in6m_scq; /* queue of pending
* state-change packets */
struct timeval in6m_lastgsrtv; /* last G-S-R query */
uint16_t in6m_sctimer; /* state-change timer */
uint16_t in6m_scrv; /* state-change rexmit count */
/*
* SSM state counters which track state at T0 (the time the last
* state-change report's RV timer went to zero) and T1
* (time of pending report, i.e. now).
* Used for computing MLDv2 state-change reports. Several refcounts
* are maintained here to optimize for common use-cases.
*/
struct in6m_st {
uint16_t iss_fmode; /* MLD filter mode */
uint16_t iss_asm; /* # of ASM listeners */
uint16_t iss_ex; /* # of exclusive members */
uint16_t iss_in; /* # of inclusive members */
uint16_t iss_rec; /* # of recorded sources */
} in6m_st[2]; /* state at t0, t1 */
};
/*
* Helper function to derive the filter mode on a source entry
* from its internal counters. Predicates are:
* A source is only excluded if all listeners exclude it.
* A source is only included if no listeners exclude it,
* and at least one listener includes it.
* May be used by ifmcstat(8).
*/
static __inline uint8_t
im6s_get_mode(const struct in6_multi *inm, const struct ip6_msource *ims,
uint8_t t)
{
t = !!t;
if (inm->in6m_st[t].iss_ex > 0 &&
inm->in6m_st[t].iss_ex == ims->im6s_st[t].ex)
return (MCAST_EXCLUDE);
else if (ims->im6s_st[t].in > 0 && ims->im6s_st[t].ex == 0)
return (MCAST_INCLUDE);
return (MCAST_UNDEFINED);
}
#ifdef _KERNEL
/*
* Lock macros for IPv6 layer multicast address lists. IPv6 lock goes
* before link layer multicast locks in the lock order. In most cases,
* consumers of IN_*_MULTI() macros should acquire the locks before
* calling them; users of the in_{add,del}multi() functions should not.
*/
extern struct mtx in6_multi_mtx;
#define IN6_MULTI_LOCK() mtx_lock(&in6_multi_mtx)
#define IN6_MULTI_UNLOCK() mtx_unlock(&in6_multi_mtx)
#define IN6_MULTI_LOCK_ASSERT() mtx_assert(&in6_multi_mtx, MA_OWNED)
#define IN6_MULTI_UNLOCK_ASSERT() mtx_assert(&in6_multi_mtx, MA_NOTOWNED)
/*
* Look up an in6_multi record for an IPv6 multicast address
* on the interface ifp.
* If no record found, return NULL.
*
* SMPng: The IN6_MULTI_LOCK and IF_ADDR_LOCK on ifp must be held.
*/
static __inline struct in6_multi *
in6m_lookup_locked(struct ifnet *ifp, const struct in6_addr *mcaddr)
{
struct ifmultiaddr *ifma;
struct in6_multi *inm;
IN6_MULTI_LOCK_ASSERT();
IF_ADDR_LOCK_ASSERT(ifp);
inm = NULL;
TAILQ_FOREACH(ifma, &((ifp)->if_multiaddrs), ifma_link) {
if (ifma->ifma_addr->sa_family == AF_INET6) {
inm = (struct in6_multi *)ifma->ifma_protospec;
if (IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, mcaddr))
break;
inm = NULL;
}
}
return (inm);
}
/*
* Wrapper for in6m_lookup_locked().
*
* SMPng: Assumes that neithr the IN6_MULTI_LOCK() or IF_ADDR_LOCK() are held.
*/
static __inline struct in6_multi *
in6m_lookup(struct ifnet *ifp, const struct in6_addr *mcaddr)
{
struct in6_multi *inm;
IN6_MULTI_LOCK();
IF_ADDR_LOCK(ifp);
inm = in6m_lookup_locked(ifp, mcaddr);
IF_ADDR_UNLOCK(ifp);
IN6_MULTI_UNLOCK();
return (inm);
}
/* Acquire an in6_multi record. */
static __inline void
in6m_acquire_locked(struct in6_multi *inm)
{
IN6_MULTI_LOCK_ASSERT();
++inm->in6m_refcount;
}
struct ip6_moptions;
struct sockopt;
/* Multicast KPIs. */
int im6o_mc_filter(const struct ip6_moptions *, const struct ifnet *,
const struct sockaddr *, const struct sockaddr *);
int in6_mc_join(struct ifnet *, const struct in6_addr *,
struct in6_mfilter *, struct in6_multi **, int);
int in6_mc_join_locked(struct ifnet *, const struct in6_addr *,
struct in6_mfilter *, struct in6_multi **, int);
int in6_mc_leave(struct in6_multi *, struct in6_mfilter *);
int in6_mc_leave_locked(struct in6_multi *, struct in6_mfilter *);
void in6m_clear_recorded(struct in6_multi *);
void in6m_commit(struct in6_multi *);
void in6m_print(const struct in6_multi *);
int in6m_record_source(struct in6_multi *, const struct in6_addr *);
void in6m_release_locked(struct in6_multi *);
void ip6_freemoptions(struct ip6_moptions *);
int ip6_getmoptions(struct inpcb *, struct sockopt *);
int ip6_setmoptions(struct inpcb *, struct sockopt *);
/* Legacy KAME multicast KPIs. */
struct in6_multi_mship *
in6_joingroup(struct ifnet *, struct in6_addr *, int *, int);
int in6_leavegroup(struct in6_multi_mship *);
/* flags to in6_update_ifa */
#define IN6_IFAUPDATE_DADDELAY 0x1 /* first time to configure an address */
extern LIST_HEAD(in6_multihead, in6_multi) in6_multihead;
/*
* Structure used by macros below to remember position when stepping through
* all of the in6_multi records.
*/
struct in6_multistep {
struct in6_ifaddr *i_ia;
struct in6_multi *i_in6m;
};
/*
* Macros for looking up the in6_multi record for a given IP6 multicast
* address on a given interface. If no matching record is found, "in6m"
* returns NULL.
*/
#define IN6_LOOKUP_MULTI(addr, ifp, in6m) \
/* struct in6_addr addr; */ \
/* struct ifnet *ifp; */ \
/* struct in6_multi *in6m; */ \
do { \
struct ifmultiaddr *ifma; \
IF_ADDR_LOCK(ifp); \
TAILQ_FOREACH(ifma, &(ifp)->if_multiaddrs, ifma_link) { \
if (ifma->ifma_addr->sa_family == AF_INET6 \
&& IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)ifma->ifma_addr)->sin6_addr, \
&(addr))) \
break; \
} \
(in6m) = (struct in6_multi *)(ifma ? ifma->ifma_protospec : 0); \
IF_ADDR_UNLOCK(ifp); \
} while(0)
/*
* Macro to step through all of the in6_multi records, one at a time.
* The current position is remembered in "step", which the caller must
* provide. IN6_FIRST_MULTI(), below, must be called to initialize "step"
* and get the first record. Both macros return a NULL "in6m" when there
* are no remaining records.
*/
#define IN6_NEXT_MULTI(step, in6m) \
/* struct in6_multistep step; */ \
/* struct in6_multi *in6m; */ \
do { \
if (((in6m) = (step).i_in6m) != NULL) \
(step).i_in6m = LIST_NEXT((step).i_in6m, in6m_entry); \
} while(0)
#define IN6_FIRST_MULTI(step, in6m) \
/* struct in6_multistep step; */ \
/* struct in6_multi *in6m */ \
do { \
(step).i_in6m = LIST_FIRST(&in6_multihead); \
IN6_NEXT_MULTI((step), (in6m)); \
} while(0)
struct in6_multi *in6_addmulti __P((struct in6_addr *, struct ifnet *,
int *, int));
void in6_delmulti __P((struct in6_multi *));
struct in6_multi_mship *in6_joingroup(struct ifnet *, struct in6_addr *, int *, int);
int in6_leavegroup(struct in6_multi_mship *);
int in6_mask2len __P((struct in6_addr *, u_char *));
int in6_control __P((struct socket *, u_long, caddr_t, struct ifnet *,
struct thread *));
@ -615,8 +770,6 @@ void *in6_domifattach __P((struct ifnet *));
void in6_domifdetach __P((struct ifnet *, void *));
void in6_setmaxmtu __P((void));
int in6_if2idlen __P((struct ifnet *));
void in6_restoremkludge __P((struct in6_ifaddr *, struct ifnet *));
void in6_purgemkludge __P((struct ifnet *));
struct in6_ifaddr *in6ifa_ifpforlinklocal __P((struct ifnet *, int));
struct in6_ifaddr *in6ifa_ifpwithaddr __P((struct ifnet *, struct in6_addr *));
char *ip6_sprintf __P((char *, const struct in6_addr *));

View File

@ -555,25 +555,12 @@ passin:
}
/*
* Multicast check
* Multicast check. Assume packet is for us to avoid
* prematurely taking locks.
*/
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
struct in6_multi *in6m = 0;
ours = 1;
in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mcast);
/*
* See if we belong to the destination multicast group on the
* arrival interface.
*/
IN6_LOOKUP_MULTI(ip6->ip6_dst, m->m_pkthdr.rcvif, in6m);
if (in6m)
ours = 1;
else if (!ip6_mrouter) {
V_ip6stat.ip6s_notmember++;
V_ip6stat.ip6s_cantforward++;
in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard);
goto bad;
}
deliverifp = m->m_pkthdr.rcvif;
goto hbhcheck;
}
@ -823,7 +810,8 @@ passin:
/*
* Forward if desirable.
*/
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
if (V_ip6_mrouter &&
IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
/*
* If we are acting as a multicast router, all
* incoming multicast packets are passed to the
@ -832,13 +820,12 @@ passin:
* ip6_mforward() returns a non-zero value, the packet
* must be discarded, else it may be accepted below.
*/
if (ip6_mrouter && ip6_mforward &&
if (ip6_mforward &&
ip6_mforward(ip6, m->m_pkthdr.rcvif, m)) {
V_ip6stat.ip6s_cantforward++;
IP6STAT_INC(ip6s_cantforward);
in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard);
goto bad;
}
if (!ours)
goto bad;
} else if (!ours) {
ip6_forward(m, srcrt);
goto out;

View File

@ -93,6 +93,7 @@ __FBSDID("$FreeBSD$");
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/module.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/signalvar.h>
#include <sys/socket.h>
@ -140,6 +141,7 @@ static int set_pim6(int *);
static int socket_send(struct socket *, struct mbuf *,
struct sockaddr_in6 *);
extern int in6_mcast_loop;
extern struct domain inet6domain;
static const struct encaptab *pim6_encap_cookie;
@ -367,7 +369,7 @@ X_ip6_mrouter_set(struct socket *so, struct sockopt *sopt)
struct mf6cctl mfcc;
mifi_t mifi;
if (so != ip6_mrouter && sopt->sopt_name != MRT6_INIT)
if (so != V_ip6_mrouter && sopt->sopt_name != MRT6_INIT)
return (EACCES);
switch (sopt->sopt_name) {
@ -432,7 +434,7 @@ X_ip6_mrouter_get(struct socket *so, struct sockopt *sopt)
INIT_VNET_INET6(curvnet);
int error = 0;
if (so != ip6_mrouter)
if (so != V_ip6_mrouter)
return (EACCES);
switch (sopt->sopt_name) {
@ -560,12 +562,12 @@ ip6_mrouter_init(struct socket *so, int v, int cmd)
MROUTER6_LOCK();
if (ip6_mrouter != NULL) {
if (V_ip6_mrouter != NULL) {
MROUTER6_UNLOCK();
return (EADDRINUSE);
}
ip6_mrouter = so;
V_ip6_mrouter = so;
V_ip6_mrouter_ver = cmd;
bzero((caddr_t)mf6ctable, sizeof(mf6ctable));
@ -601,7 +603,7 @@ X_ip6_mrouter_done(void)
MROUTER6_LOCK();
if (ip6_mrouter == NULL) {
if (V_ip6_mrouter == NULL) {
MROUTER6_UNLOCK();
return (EINVAL);
}
@ -657,7 +659,7 @@ X_ip6_mrouter_done(void)
multicast_register_if6 = NULL;
}
ip6_mrouter = NULL;
V_ip6_mrouter = NULL;
V_ip6_mrouter_ver = 0;
MROUTER6_UNLOCK();
@ -1293,7 +1295,7 @@ X_ip6_mforward(struct ip6_hdr *ip6, struct ifnet *ifp, struct mbuf *m)
break;
}
if (socket_send(ip6_mrouter, mm, &sin6) < 0) {
if (socket_send(V_ip6_mrouter, mm, &sin6) < 0) {
log(LOG_WARNING, "ip6_mforward: ip6_mrouter "
"socket queue full\n");
mrt6stat.mrt6s_upq_sockfull++;
@ -1531,7 +1533,7 @@ ip6_mdq(struct mbuf *m, struct ifnet *ifp, struct mf6c *rt)
mrt6stat.mrt6s_upcalls++;
if (socket_send(ip6_mrouter, mm, &sin6) < 0) {
if (socket_send(V_ip6_mrouter, mm, &sin6) < 0) {
#ifdef MRT6DEBUG
if (V_mrt6debug)
log(LOG_WARNING, "mdq, ip6_mrouter socket queue full\n");
@ -1603,10 +1605,11 @@ phyint_send(struct ip6_hdr *ip6, struct mif6 *mifp, struct mbuf *m)
struct mbuf *mb_copy;
struct ifnet *ifp = mifp->m6_ifp;
int error = 0;
struct in6_multi *in6m;
struct sockaddr_in6 *dst6;
u_long linkmtu;
dst6 = &mifp->m6_route.ro_dst;
/*
* Make a new reference to the packet; make sure that
* the IPv6 header is actually copied, not just referenced,
@ -1648,17 +1651,16 @@ phyint_send(struct ip6_hdr *ip6, struct mif6 *mifp, struct mbuf *m)
}
/*
* If we belong to the destination multicast group
* on the outgoing interface, loop back a copy.
* If configured to loop back multicasts by default,
* loop back a copy now.
*/
dst6 = &mifp->m6_route.ro_dst;
IN6_LOOKUP_MULTI(ip6->ip6_dst, ifp, in6m);
if (in6m != NULL) {
if (in6_mcast_loop) {
dst6->sin6_len = sizeof(struct sockaddr_in6);
dst6->sin6_family = AF_INET6;
dst6->sin6_addr = ip6->ip6_dst;
ip6_mloopback(ifp, m, &mifp->m6_route.ro_dst);
}
/*
* Put the packet into the sending queue of the outgoing interface
* if it would fit in the MTU of the interface.
@ -1759,7 +1761,7 @@ register_send(struct ip6_hdr *ip6, struct mif6 *mif, struct mbuf *m)
/* iif info is not given for reg. encap.n */
mrt6stat.mrt6s_upcalls++;
if (socket_send(ip6_mrouter, mm, &sin6) < 0) {
if (socket_send(V_ip6_mrouter, mm, &sin6) < 0) {
#ifdef MRT6DEBUG
if (V_mrt6debug)
log(LOG_WARNING,
@ -2056,7 +2058,7 @@ ip6_mroute_modevent(module_t mod, int type, void *unused)
break;
case MOD_UNLOAD:
if (ip6_mrouter != NULL)
if (V_ip6_mrouter != NULL)
return EINVAL;
if (pim6_encap_cookie) {

View File

@ -110,7 +110,7 @@ __FBSDID("$FreeBSD$");
#include <netinet6/scope6_var.h>
#include <netinet6/vinet6.h>
static MALLOC_DEFINE(M_IP6MOPTS, "ip6_moptions", "internet multicast options");
extern int in6_mcast_loop;
struct ip6_exthdrs {
struct mbuf *ip6e_ip6;
@ -128,8 +128,6 @@ static int ip6_getpcbopt(struct ip6_pktopts *, int, struct sockopt *);
static int ip6_setpktopt __P((int, u_char *, int, struct ip6_pktopts *,
struct ucred *, int, int, int));
static int ip6_setmoptions(int, struct ip6_moptions **, struct mbuf *);
static int ip6_getmoptions(int, struct ip6_moptions *, struct mbuf **);
static int ip6_copyexthdr(struct mbuf **, caddr_t, int);
static int ip6_insertfraghdr __P((struct mbuf *, struct mbuf *, int,
struct ip6_frag **));
@ -692,12 +690,8 @@ again:
if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */
} else {
struct in6_multi *in6m;
m->m_flags = (m->m_flags & ~M_BCAST) | M_MCAST;
in6_ifstat_inc(ifp, ifs6_out_mcast);
/*
* Confirm that the outgoing interface supports multicast.
*/
@ -707,13 +701,14 @@ again:
error = ENETUNREACH;
goto bad;
}
IN6_LOOKUP_MULTI(ip6->ip6_dst, ifp, in6m);
if (in6m != NULL &&
(im6o == NULL || im6o->im6o_multicast_loop)) {
if ((im6o == NULL && in6_mcast_loop) ||
(im6o && im6o->im6o_multicast_loop)) {
/*
* If we belong to the destination multicast group
* on the outgoing interface, and the caller did not
* forbid loopback, loop back a copy.
* Loop back multicast datagram if not expressly
* forbidden to do so, even if we have not joined
* the address; protocols will filter it later,
* thus deferring a hash lookup and lock acquisition
* at the expense of an m_copym().
*/
ip6_mloopback(ifp, m, dst);
} else {
@ -729,7 +724,7 @@ again:
* above, will be forwarded by the ip6_input() routine,
* if necessary.
*/
if (ip6_mrouter && (flags & IPV6_FORWARDING) == 0) {
if (V_ip6_mrouter && (flags & IPV6_FORWARDING) == 0) {
/*
* XXX: ip6_mforward expects that rcvif is NULL
* when it is called from the originating path.
@ -1702,47 +1697,14 @@ do { \
case IPV6_MULTICAST_LOOP:
case IPV6_JOIN_GROUP:
case IPV6_LEAVE_GROUP:
{
if (sopt->sopt_valsize > MLEN) {
error = EMSGSIZE;
break;
}
/* XXX */
}
/* FALLTHROUGH */
{
struct mbuf *m;
if (sopt->sopt_valsize > MCLBYTES) {
error = EMSGSIZE;
break;
}
/* XXX */
MGET(m, sopt->sopt_td ? M_WAIT : M_DONTWAIT, MT_DATA);
if (m == 0) {
error = ENOBUFS;
break;
}
if (sopt->sopt_valsize > MLEN) {
MCLGET(m, sopt->sopt_td ? M_WAIT : M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_free(m);
error = ENOBUFS;
break;
}
}
m->m_len = sopt->sopt_valsize;
error = sooptcopyin(sopt, mtod(m, char *),
m->m_len, m->m_len);
if (error) {
(void)m_free(m);
break;
}
error = ip6_setmoptions(sopt->sopt_name,
&in6p->in6p_moptions,
m);
(void)m_free(m);
}
case IPV6_MSFILTER:
case MCAST_BLOCK_SOURCE:
case MCAST_UNBLOCK_SOURCE:
case MCAST_JOIN_GROUP:
case MCAST_LEAVE_GROUP:
case MCAST_JOIN_SOURCE_GROUP:
case MCAST_LEAVE_SOURCE_GROUP:
error = ip6_setmoptions(in6p, sopt);
break;
case IPV6_PORTRANGE:
@ -1974,17 +1936,8 @@ do { \
case IPV6_MULTICAST_IF:
case IPV6_MULTICAST_HOPS:
case IPV6_MULTICAST_LOOP:
case IPV6_JOIN_GROUP:
case IPV6_LEAVE_GROUP:
{
struct mbuf *m;
error = ip6_getmoptions(sopt->sopt_name,
in6p->in6p_moptions, &m);
if (error == 0)
error = sooptcopyout(sopt,
mtod(m, char *), m->m_len);
m_freem(m);
}
case IPV6_MSFILTER:
error = ip6_getmoptions(in6p, sopt);
break;
#ifdef IPSEC
@ -2404,374 +2357,6 @@ ip6_freepcbopts(struct ip6_pktopts *pktopt)
free(pktopt, M_IP6OPT);
}
/*
* Set the IP6 multicast options in response to user setsockopt().
*/
static int
ip6_setmoptions(int optname, struct ip6_moptions **im6op, struct mbuf *m)
{
INIT_VNET_NET(curvnet);
INIT_VNET_INET6(curvnet);
int error = 0;
u_int loop, ifindex;
struct ipv6_mreq *mreq;
struct ifnet *ifp;
struct ip6_moptions *im6o = *im6op;
struct route_in6 ro;
struct in6_multi_mship *imm;
if (im6o == NULL) {
/*
* No multicast option buffer attached to the pcb;
* allocate one and initialize to default values.
*/
im6o = (struct ip6_moptions *)
malloc(sizeof(*im6o), M_IP6MOPTS, M_WAITOK);
if (im6o == NULL)
return (ENOBUFS);
*im6op = im6o;
im6o->im6o_multicast_ifp = NULL;
im6o->im6o_multicast_hlim = V_ip6_defmcasthlim;
im6o->im6o_multicast_loop = IPV6_DEFAULT_MULTICAST_LOOP;
LIST_INIT(&im6o->im6o_memberships);
}
switch (optname) {
case IPV6_MULTICAST_IF:
/*
* Select the interface for outgoing multicast packets.
*/
if (m == NULL || m->m_len != sizeof(u_int)) {
error = EINVAL;
break;
}
bcopy(mtod(m, u_int *), &ifindex, sizeof(ifindex));
if (ifindex < 0 || V_if_index < ifindex) {
error = ENXIO; /* XXX EINVAL? */
break;
}
ifp = ifnet_byindex(ifindex);
if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
error = EADDRNOTAVAIL;
break;
}
im6o->im6o_multicast_ifp = ifp;
break;
case IPV6_MULTICAST_HOPS:
{
/*
* Set the IP6 hoplimit for outgoing multicast packets.
*/
int optval;
if (m == NULL || m->m_len != sizeof(int)) {
error = EINVAL;
break;
}
bcopy(mtod(m, u_int *), &optval, sizeof(optval));
if (optval < -1 || optval >= 256)
error = EINVAL;
else if (optval == -1)
im6o->im6o_multicast_hlim = V_ip6_defmcasthlim;
else
im6o->im6o_multicast_hlim = optval;
break;
}
case IPV6_MULTICAST_LOOP:
/*
* Set the loopback flag for outgoing multicast packets.
* Must be zero or one.
*/
if (m == NULL || m->m_len != sizeof(u_int)) {
error = EINVAL;
break;
}
bcopy(mtod(m, u_int *), &loop, sizeof(loop));
if (loop > 1) {
error = EINVAL;
break;
}
im6o->im6o_multicast_loop = loop;
break;
case IPV6_JOIN_GROUP:
/*
* Add a multicast group membership.
* Group must be a valid IP6 multicast address.
*/
if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) {
error = EINVAL;
break;
}
mreq = mtod(m, struct ipv6_mreq *);
if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
/*
* 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 */
error = priv_check(curthread, PRIV_NETINET_MROUTE);
if (error)
break;
} else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
error = EINVAL;
break;
}
/*
* If no interface was explicitly specified, choose an
* appropriate one according to the given multicast address.
*/
if (mreq->ipv6mr_interface == 0) {
struct sockaddr_in6 *dst;
/*
* Look up the routing table for the
* address, and choose the outgoing interface.
* XXX: is it a good approach?
*/
ro.ro_rt = NULL;
dst = (struct sockaddr_in6 *)&ro.ro_dst;
bzero(dst, sizeof(*dst));
dst->sin6_family = AF_INET6;
dst->sin6_len = sizeof(*dst);
dst->sin6_addr = mreq->ipv6mr_multiaddr;
rtalloc((struct route *)&ro);
if (ro.ro_rt == NULL) {
error = EADDRNOTAVAIL;
break;
}
ifp = ro.ro_rt->rt_ifp;
RTFREE(ro.ro_rt);
} else {
/*
* If the interface is specified, validate it.
*/
if (mreq->ipv6mr_interface < 0 ||
V_if_index < mreq->ipv6mr_interface) {
error = ENXIO; /* XXX EINVAL? */
break;
}
ifp = ifnet_byindex(mreq->ipv6mr_interface);
if (!ifp) {
error = ENXIO; /* XXX EINVAL? */
break;
}
}
/*
* See if we found an interface, and confirm that it
* supports multicast
*/
if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
error = EADDRNOTAVAIL;
break;
}
if (in6_setscope(&mreq->ipv6mr_multiaddr, ifp, NULL)) {
error = EADDRNOTAVAIL; /* XXX: should not happen */
break;
}
/*
* See if the membership already exists.
*/
for (imm = im6o->im6o_memberships.lh_first;
imm != NULL; imm = imm->i6mm_chain.le_next)
if (imm->i6mm_maddr->in6m_ifp == ifp &&
IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr,
&mreq->ipv6mr_multiaddr))
break;
if (imm != NULL) {
error = EADDRINUSE;
break;
}
/*
* Everything looks good; add a new record to the multicast
* address list for the given interface.
*/
imm = in6_joingroup(ifp, &mreq->ipv6mr_multiaddr, &error, 0);
if (imm == NULL)
break;
LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
break;
case IPV6_LEAVE_GROUP:
/*
* Drop a multicast group membership.
* Group must be a valid IP6 multicast address.
*/
if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) {
error = EINVAL;
break;
}
mreq = mtod(m, struct ipv6_mreq *);
/*
* If an interface address was specified, get a pointer
* to its ifnet structure.
*/
if (mreq->ipv6mr_interface < 0 ||
V_if_index < mreq->ipv6mr_interface) {
error = ENXIO; /* XXX EINVAL? */
break;
}
if (mreq->ipv6mr_interface == 0)
ifp = NULL;
else
ifp = ifnet_byindex(mreq->ipv6mr_interface);
/* Fill in the scope zone ID */
if (ifp) {
if (in6_setscope(&mreq->ipv6mr_multiaddr, ifp, NULL)) {
/* XXX: should not happen */
error = EADDRNOTAVAIL;
break;
}
} else if (mreq->ipv6mr_interface != 0) {
/*
* This case happens when the (positive) index is in
* the valid range, but the corresponding interface has
* been detached dynamically (XXX).
*/
error = EADDRNOTAVAIL;
break;
} else { /* ipv6mr_interface == 0 */
struct sockaddr_in6 sa6_mc;
/*
* The API spec says as follows:
* If the interface index is specified as 0, the
* system may choose a multicast group membership to
* drop by matching the multicast address only.
* On the other hand, we cannot disambiguate the scope
* zone unless an interface is provided. Thus, we
* check if there's ambiguity with the default scope
* zone as the last resort.
*/
bzero(&sa6_mc, sizeof(sa6_mc));
sa6_mc.sin6_family = AF_INET6;
sa6_mc.sin6_len = sizeof(sa6_mc);
sa6_mc.sin6_addr = mreq->ipv6mr_multiaddr;
error = sa6_embedscope(&sa6_mc, V_ip6_use_defzone);
if (error != 0)
break;
mreq->ipv6mr_multiaddr = sa6_mc.sin6_addr;
}
/*
* Find the membership in the membership list.
*/
for (imm = im6o->im6o_memberships.lh_first;
imm != NULL; imm = imm->i6mm_chain.le_next) {
if ((ifp == NULL || imm->i6mm_maddr->in6m_ifp == ifp) &&
IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr,
&mreq->ipv6mr_multiaddr))
break;
}
if (imm == NULL) {
/* Unable to resolve interface */
error = EADDRNOTAVAIL;
break;
}
/*
* Give up the multicast address record to which the
* membership points.
*/
LIST_REMOVE(imm, i6mm_chain);
in6_delmulti(imm->i6mm_maddr);
free(imm, M_IP6MADDR);
break;
default:
error = EOPNOTSUPP;
break;
}
/*
* If all options have default values, no need to keep the mbuf.
*/
if (im6o->im6o_multicast_ifp == NULL &&
im6o->im6o_multicast_hlim == V_ip6_defmcasthlim &&
im6o->im6o_multicast_loop == IPV6_DEFAULT_MULTICAST_LOOP &&
im6o->im6o_memberships.lh_first == NULL) {
free(*im6op, M_IP6MOPTS);
*im6op = NULL;
}
return (error);
}
/*
* Return the IP6 multicast options in response to user getsockopt().
*/
static int
ip6_getmoptions(int optname, struct ip6_moptions *im6o, struct mbuf **mp)
{
INIT_VNET_INET6(curvnet);
u_int *hlim, *loop, *ifindex;
*mp = m_get(M_WAIT, MT_HEADER); /* XXX */
switch (optname) {
case IPV6_MULTICAST_IF:
ifindex = mtod(*mp, u_int *);
(*mp)->m_len = sizeof(u_int);
if (im6o == NULL || im6o->im6o_multicast_ifp == NULL)
*ifindex = 0;
else
*ifindex = im6o->im6o_multicast_ifp->if_index;
return (0);
case IPV6_MULTICAST_HOPS:
hlim = mtod(*mp, u_int *);
(*mp)->m_len = sizeof(u_int);
if (im6o == NULL)
*hlim = V_ip6_defmcasthlim;
else
*hlim = im6o->im6o_multicast_hlim;
return (0);
case IPV6_MULTICAST_LOOP:
loop = mtod(*mp, u_int *);
(*mp)->m_len = sizeof(u_int);
if (im6o == NULL)
*loop = V_ip6_defmcasthlim;
else
*loop = im6o->im6o_multicast_loop;
return (0);
default:
return (EOPNOTSUPP);
}
}
/*
* Discard the IP6 multicast options.
*/
void
ip6_freemoptions(struct ip6_moptions *im6o)
{
struct in6_multi_mship *imm;
if (im6o == NULL)
return;
while ((imm = im6o->im6o_memberships.lh_first) != NULL) {
LIST_REMOVE(imm, i6mm_chain);
if (imm->i6mm_maddr)
in6_delmulti(imm->i6mm_maddr);
free(imm, M_IP6MADDR);
}
free(im6o, M_IP6MOPTS);
}
/*
* Set IPv6 outgoing packet options based on advanced API.
*/

View File

@ -98,11 +98,19 @@ struct ip6asfrag {
#define IP6_REASS_MBUF(ip6af) (*(struct mbuf **)&((ip6af)->ip6af_m))
struct ip6_moptions {
/*
* Structure attached to inpcb.in6p_moptions and
* passed to ip6_output when IPv6 multicast options are in use.
* This structure is lazy-allocated.
*/
struct ip6_moptions {
struct ifnet *im6o_multicast_ifp; /* ifp for outgoing multicasts */
u_char im6o_multicast_hlim; /* hoplimit for outgoing multicasts */
u_char im6o_multicast_loop; /* 1 >= hear sends if a member */
LIST_HEAD(, in6_multi_mship) im6o_memberships;
u_short im6o_num_memberships; /* no. memberships this socket */
u_short im6o_max_memberships; /* max memberships this socket */
struct in6_multi **im6o_membership; /* group memberships */
struct in6_mfilter *im6o_mfilters; /* source filters */
};
/*
@ -233,6 +241,13 @@ struct ip6stat {
u_quad_t ip6s_sources_rule[16];
};
#ifdef _KERNEL
#define IP6STAT_ADD(name, val) V_ip6stat.name += (val)
#define IP6STAT_SUB(name, val) V_ip6stat.name -= (val)
#define IP6STAT_INC(name) IP6STAT_ADD(name, 1)
#define IP6STAT_DEC(name) IP6STAT_SUB(name, 1)
#endif
#ifdef _KERNEL
/*
* IPv6 onion peeling state.
@ -287,10 +302,7 @@ extern int ip6_rr_prune; /* router renumbering prefix
* walk list every 5 sec. */
extern int ip6_mcast_pmtu; /* enable pMTU discovery for multicast? */
extern int ip6_v6only;
#endif /* VIMAGE_GLOBALS */
extern struct socket *ip6_mrouter; /* multicast routing daemon */
#ifdef VIMAGE_GLOBALS
extern int ip6_sendredirects; /* send IP redirects when forwarding? */
extern int ip6_maxfragpackets; /* Maximum packets in reassembly queue */
extern int ip6_maxfrags; /* Maximum fragments in reassembly queue */
@ -330,7 +342,7 @@ void ip6_init __P((void));
void ip6_input __P((struct mbuf *));
struct in6_ifaddr *ip6_getdstifaddr __P((struct mbuf *));
void ip6_freepcbopts __P((struct ip6_pktopts *));
void ip6_freemoptions __P((struct ip6_moptions *));
int ip6_unknown_opt __P((u_int8_t *, struct mbuf *, int));
char * ip6_get_prevhdr __P((struct mbuf *, int));
int ip6_nexthdr __P((struct mbuf *, int, int, int *));

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,5 @@
/*-
* Copyright (C) 1998 WIDE Project.
* All rights reserved.
* Copyright (c) 2009 Bruce Simpson.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -10,14 +9,14 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@ -26,29 +25,139 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $KAME: mld6_var.h,v 1.4 2000/03/25 07:23:54 sumikawa Exp $
* $FreeBSD$
*/
#ifndef _NETINET6_MLD6_VAR_H_
#define _NETINET6_MLD6_VAR_H_
/*
* Multicast Listener Discovery (MLD)
* implementation-specific definitions.
*/
#ifdef _KERNEL
#define MLD_RANDOM_DELAY(X) (arc4random() % (X) + 1)
/*
* Per-link MLD state.
*/
struct mld_ifinfo {
LIST_ENTRY(mld_ifinfo) mli_link;
struct ifnet *mli_ifp; /* interface this instance belongs to */
uint32_t mli_version; /* MLDv1 Host Compatibility Mode */
uint32_t mli_v1_timer; /* MLDv1 Querier Present timer (s) */
uint32_t mli_v2_timer; /* MLDv2 General Query (interface) timer (s)*/
uint32_t mli_flags; /* MLD per-interface flags */
uint32_t mli_rv; /* MLDv2 Robustness Variable */
uint32_t mli_qi; /* MLDv2 Query Interval (s) */
uint32_t mli_qri; /* MLDv2 Query Response Interval (s) */
uint32_t mli_uri; /* MLDv2 Unsolicited Report Interval (s) */
SLIST_HEAD(,in6_multi) mli_relinmhead; /* released groups */
struct ifqueue mli_gq; /* queue of general query responses */
};
#define MLIF_SILENT 0x00000001 /* Do not use MLD on this ifp */
#define MLD_RANDOM_DELAY(X) (arc4random() % (X) + 1)
#define MLD_MAX_STATE_CHANGES 24 /* Max pending changes per group */
/*
* States for MLD stop-listening processing
* MLD per-group states.
*/
#define MLD_OTHERLISTENER 0
#define MLD_IREPORTEDLAST 1
#define MLD_REPORTPENDING 2 /* implementation specific */
#define MLD_NOT_MEMBER 0 /* Can garbage collect group */
#define MLD_SILENT_MEMBER 1 /* Do not perform MLD for group */
#define MLD_REPORTING_MEMBER 2 /* MLDv1 we are reporter */
#define MLD_IDLE_MEMBER 3 /* MLDv1 we reported last */
#define MLD_LAZY_MEMBER 4 /* MLDv1 other member reporting */
#define MLD_SLEEPING_MEMBER 5 /* MLDv1 start query response */
#define MLD_AWAKENING_MEMBER 6 /* MLDv1 group timer will start */
#define MLD_G_QUERY_PENDING_MEMBER 7 /* MLDv2 group query pending */
#define MLD_SG_QUERY_PENDING_MEMBER 8 /* MLDv2 source query pending */
#define MLD_LEAVING_MEMBER 9 /* MLDv2 dying gasp (pending last */
/* retransmission of INCLUDE {}) */
/*
* MLD version tag.
*/
#define MLD_VERSION_NONE 0 /* Invalid */
#define MLD_VERSION_1 1
#define MLD_VERSION_2 2 /* Default */
/*
* MLDv2 protocol control variables.
*/
#define MLD_RV_INIT 2 /* Robustness Variable */
#define MLD_RV_MIN 1
#define MLD_RV_MAX 7
#define MLD_QI_INIT 125 /* Query Interval (s) */
#define MLD_QI_MIN 1
#define MLD_QI_MAX 255
#define MLD_QRI_INIT 10 /* Query Response Interval (s) */
#define MLD_QRI_MIN 1
#define MLD_QRI_MAX 255
#define MLD_URI_INIT 3 /* Unsolicited Report Interval (s) */
#define MLD_URI_MIN 0
#define MLD_URI_MAX 10
#define MLD_MAX_GS_SOURCES 256 /* # of sources in rx GS query */
#define MLD_MAX_G_GS_PACKETS 8 /* # of packets to answer G/GS */
#define MLD_MAX_STATE_CHANGE_PACKETS 8 /* # of packets per state change */
#define MLD_MAX_RESPONSE_PACKETS 16 /* # of packets for general query */
#define MLD_MAX_RESPONSE_BURST 4 /* # of responses to send at once */
#define MLD_RESPONSE_BURST_INTERVAL (PR_FASTHZ / 2) /* 500ms */
/*
* MLD-specific mbuf flags.
*/
#define M_MLDV1 M_PROTO1 /* Packet is MLDv1 */
#define M_GROUPREC M_PROTO3 /* mbuf chain is a group record */
/*
* Leading space for MLDv2 reports inside MTU.
*
* NOTE: This differs from IGMPv3 significantly. KAME IPv6 requires
* that a fully formed mbuf chain *without* the Router Alert option
* is passed to ip6_output(), however we must account for it in the
* MTU if we need to split an MLDv2 report into several packets.
*
* We now put the MLDv2 report header in the initial mbuf containing
* the IPv6 header.
*/
#define MLD_MTUSPACE (sizeof(struct ip6_hdr) + sizeof(struct mld_raopt) + \
sizeof(struct icmp6_hdr))
/*
* Subsystem lock macros.
* The MLD lock is only taken with MLD. Currently it is system-wide.
* VIMAGE: The lock could be pushed to per-VIMAGE granularity in future.
*/
#define MLD_LOCK_INIT() mtx_init(&mld_mtx, "mld_mtx", NULL, MTX_DEF)
#define MLD_LOCK_DESTROY() mtx_destroy(&mld_mtx)
#define MLD_LOCK() mtx_lock(&mld_mtx)
#define MLD_LOCK_ASSERT() mtx_assert(&mld_mtx, MA_OWNED)
#define MLD_UNLOCK() mtx_unlock(&mld_mtx)
#define MLD_UNLOCK_ASSERT() mtx_assert(&mld_mtx, MA_NOTOWNED)
/*
* Per-link MLD context.
*/
#define MLD_IFINFO(ifp) \
(((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->mld_ifinfo)
int mld_change_state(struct in6_multi *, const int);
struct mld_ifinfo *
mld_domifattach(struct ifnet *);
void mld_domifdetach(struct ifnet *);
void mld_fasttimo(void);
void mld_ifdetach(struct ifnet *);
int mld_input(struct mbuf *, int, int);
void mld_slowtimo(void);
#ifdef SYSCTL_DECL
SYSCTL_DECL(_net_inet6_mld);
#endif
void mld6_init(void);
void mld6_input(struct mbuf *, int);
void mld6_start_listening(struct in6_multi *);
void mld6_stop_listening(struct in6_multi *);
void mld6_fasttimeo(void);
#endif /* _KERNEL */
#endif /* _NETINET6_MLD6_VAR_H_ */

View File

@ -128,9 +128,20 @@ extern u_long rip_sendspace;
extern u_long rip_recvspace;
/*
* Hooks for multicast forwarding.
* Hooks for multicast routing. They all default to NULL, so leave them not
* initialized and rely on BSS being set to 0.
*/
/*
* The socket used to communicate with the multicast routing daemon.
*/
#ifdef VIMAGE_GLOBALS
struct socket *ip6_mrouter;
#endif
/*
* The various mrouter functions.
*/
struct socket *ip6_mrouter = NULL;
int (*ip6_mrouter_set)(struct socket *, struct sockopt *);
int (*ip6_mrouter_get)(struct socket *, struct sockopt *);
int (*ip6_mrouter_done)(void);
@ -149,6 +160,7 @@ rip6_input(struct mbuf **mp, int *offp, int proto)
#ifdef IPSEC
INIT_VNET_IPSEC(curvnet);
#endif
struct ifnet *ifp;
struct mbuf *m = *mp;
register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
register struct inpcb *in6p;
@ -166,6 +178,8 @@ rip6_input(struct mbuf **mp, int *offp, int proto)
init_sin6(&fromsa, m); /* general init */
ifp = m->m_pkthdr.rcvif;
INP_INFO_RLOCK(&V_ripcbinfo);
LIST_FOREACH(in6p, &V_ripcb, inp_list) {
/* XXX inp locking */
@ -180,9 +194,17 @@ rip6_input(struct mbuf **mp, int *offp, int proto)
if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) &&
!IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src))
continue;
if (prison_check_ip6(in6p->inp_cred, &ip6->ip6_dst) != 0)
continue;
INP_RLOCK(in6p);
if (jailed(in6p->inp_cred)) {
/*
* Allow raw socket in jail to receive multicast;
* assume process had PRIV_NETINET_RAW at attach,
* and fall through into normal filter path if so.
*/
if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) &&
prison_check_ip6(in6p->inp_cred,
&ip6->ip6_dst) != 0)
continue;
}
if (in6p->in6p_cksum != -1) {
V_rip6stat.rip6s_isum++;
if (in6_cksum(m, proto, *offp,
@ -192,6 +214,31 @@ rip6_input(struct mbuf **mp, int *offp, int proto)
continue;
}
}
INP_RLOCK(in6p);
/*
* If this raw socket has multicast state, and we
* have received a multicast, check if this socket
* should receive it, as multicast filtering is now
* the responsibility of the transport layer.
*/
if (in6p->in6p_moptions &&
IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
struct sockaddr_in6 mcaddr;
int blocked;
bzero(&mcaddr, sizeof(struct sockaddr_in6));
mcaddr.sin6_len = sizeof(struct sockaddr_in6);
mcaddr.sin6_family = AF_INET6;
mcaddr.sin6_addr = ip6->ip6_dst;
blocked = im6o_mc_filter(in6p->in6p_moptions, ifp,
(struct sockaddr *)&mcaddr,
(struct sockaddr *)&fromsa);
if (blocked != MCAST_PASS) {
IP6STAT_INC(ip6s_notmember);
continue;
}
}
if (last != NULL) {
struct mbuf *n = m_copy(m, 0, (int)M_COPYALL);
@ -604,13 +651,13 @@ rip6_attach(struct socket *so, int proto, struct thread *td)
static void
rip6_detach(struct socket *so)
{
INIT_VNET_INET(so->so_vnet);
INIT_VNET_INET6(so->so_vnet);
struct inpcb *inp;
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("rip6_detach: inp == NULL"));
if (so == ip6_mrouter && ip6_mrouter_done)
if (so == V_ip6_mrouter && ip6_mrouter_done)
ip6_mrouter_done();
/* xxx: RSVP */
INP_INFO_WLOCK(&V_ripcbinfo);

View File

@ -177,6 +177,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
INIT_VNET_INET(curvnet);
INIT_VNET_INET6(curvnet);
struct mbuf *m = *mp;
struct ifnet *ifp;
struct ip6_hdr *ip6;
struct udphdr *uh;
struct inpcb *inp;
@ -184,6 +185,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
int plen, ulen;
struct sockaddr_in6 fromsa;
ifp = m->m_pkthdr.rcvif;
ip6 = mtod(m, struct ip6_hdr *);
if (faithprefix_p != NULL && (*faithprefix_p)(&ip6->ip6_dst)) {
@ -239,6 +241,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
INP_INFO_RLOCK(&V_udbinfo);
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
struct inpcb *last;
struct ip6_moptions *imo;
/*
* In the event that laddr should be set to the link-local
@ -261,12 +264,6 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
continue;
if (inp->inp_lport != uh->uh_dport)
continue;
/*
* XXX: Do not check source port of incoming datagram
* unless inp_connect() has been called to bind the
* fport part of the 4-tuple; the source could be
* trying to talk to us with an ephemeral port.
*/
if (inp->inp_fport != 0 &&
inp->inp_fport != uh->uh_sport)
continue;
@ -282,6 +279,35 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
continue;
}
INP_RLOCK(inp);
/*
* Handle socket delivery policy for any-source
* and source-specific multicast. [RFC3678]
*/
imo = inp->in6p_moptions;
if (imo && IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
struct sockaddr_in6 mcaddr;
int blocked;
bzero(&mcaddr, sizeof(struct sockaddr_in6));
mcaddr.sin6_len = sizeof(struct sockaddr_in6);
mcaddr.sin6_family = AF_INET6;
mcaddr.sin6_addr = ip6->ip6_dst;
blocked = im6o_mc_filter(imo, ifp,
(struct sockaddr *)&mcaddr,
(struct sockaddr *)&fromsa);
if (blocked != MCAST_PASS) {
if (blocked == MCAST_NOTGMEMBER)
IP6STAT_INC(ip6s_notmember);
if (blocked == MCAST_NOTSMEMBER ||
blocked == MCAST_MUTED)
UDPSTAT_INC(udps_filtermcast);
INP_RUNLOCK(inp);
continue;
}
}
if (last != NULL) {
struct mbuf *n;
@ -397,6 +423,8 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
return (IPPROTO_DONE);
badheadlocked:
if (inp)
INP_RUNLOCK(inp);
INP_INFO_RUNLOCK(&V_udbinfo);
badunlocked:
if (m)

View File

@ -145,6 +145,7 @@ struct vnet_inet6 {
u_int32_t _ip6_temp_preferred_lifetime;
u_int32_t _ip6_temp_valid_lifetime;
struct socket * _ip6_mrouter;
int _ip6_mrouter_ver;
int _pim6;
u_int _mrt6debug;
@ -153,6 +154,12 @@ struct vnet_inet6 {
int _ip6_use_defzone;
struct ip6_pktopts _ip6_opts;
struct timeval _mld_gsrdelay;
LIST_HEAD(, mld_ifinfo) _mli_head;
int _interface_timers_running6;
int _state_change_timers_running6;
int _current_state_timers_running6;
};
/* Size guard. See sys/vimage.h. */
@ -173,6 +180,8 @@ extern struct vnet_inet6 vnet_inet6_0;
* Symbol translation macros
*/
#define V_addrsel_policytab VNET_INET6(addrsel_policytab)
#define V_current_state_timers_running6 \
VNET_INET6(current_state_timers_running6)
#define V_dad_ignore_ns VNET_INET6(dad_ignore_ns)
#define V_dad_init VNET_INET6(dad_init)
#define V_dad_maxtry VNET_INET6(dad_maxtry)
@ -190,6 +199,8 @@ extern struct vnet_inet6 vnet_inet6_0;
#define V_in6_ifaddr VNET_INET6(in6_ifaddr)
#define V_in6_maxmtu VNET_INET6(in6_maxmtu)
#define V_in6_tmpaddrtimer_ch VNET_INET6(in6_tmpaddrtimer_ch)
#define V_interface_timers_running6 \
VNET_INET6(interface_timers_running6)
#define V_ip6_accept_rtadv VNET_INET6(ip6_accept_rtadv)
#define V_ip6_auto_flowlabel VNET_INET6(ip6_auto_flowlabel)
#define V_ip6_auto_linklocal VNET_INET6(ip6_auto_linklocal)
@ -205,6 +216,7 @@ extern struct vnet_inet6 vnet_inet6_0;
#define V_ip6_maxfragpackets VNET_INET6(ip6_maxfragpackets)
#define V_ip6_maxfrags VNET_INET6(ip6_maxfrags)
#define V_ip6_mcast_pmtu VNET_INET6(ip6_mcast_pmtu)
#define V_ip6_mrouter VNET_INET6(ip6_mrouter)
#define V_ip6_mrouter_ver VNET_INET6(ip6_mrouter_ver)
#define V_ip6_opts VNET_INET6(ip6_opts)
#define V_ip6_prefer_tempaddr VNET_INET6(ip6_prefer_tempaddr)
@ -223,6 +235,8 @@ extern struct vnet_inet6 vnet_inet6_0;
#define V_ip6stealth VNET_INET6(ip6stealth)
#define V_llinfo_nd6 VNET_INET6(llinfo_nd6)
#define V_mrt6debug VNET_INET6(mrt6debug)
#define V_mld_gsrdelay VNET_INET6(mld_gsrdelay)
#define V_mli_head VNET_INET6(mli_head)
#define V_nd6_allocated VNET_INET6(nd6_allocated)
#define V_nd6_debug VNET_INET6(nd6_debug)
#define V_nd6_defifindex VNET_INET6(nd6_defifindex)
@ -256,6 +270,8 @@ extern struct vnet_inet6 vnet_inet6_0;
#define V_rtq_timer6 VNET_INET6(rtq_timer6)
#define V_rtq_toomany6 VNET_INET6(rtq_toomany6)
#define V_sid_default VNET_INET6(sid_default)
#define V_state_change_timers_running6 \
VNET_INET6(state_change_timers_running6)
#define V_udp6_recvspace VNET_INET6(udp6_recvspace)
#define V_udp6_sendspace VNET_INET6(udp6_sendspace)

View File

@ -57,7 +57,7 @@
* is created, otherwise 1.
*/
#undef __FreeBSD_version
#define __FreeBSD_version 800083 /* Master, propagated to newvers */
#define __FreeBSD_version 800084 /* Master, propagated to newvers */
#ifndef LOCORE
#include <sys/types.h>

View File

@ -167,6 +167,7 @@ static void in_ifinfo(struct igmp_ifinfo *);
static const char * inm_mode(u_int mode);
#endif
#ifdef INET6
static void in6_ifinfo(struct mld_ifinfo *);
static const char * inet6_n2a(struct in6_addr *);
#endif
int main(int, char **);
@ -440,9 +441,36 @@ ll_addrlist(struct ifaddr *ifap)
#ifdef INET6
static void
in6_ifinfo(struct mld_ifinfo *mli)
{
printf("\t");
switch (mli->mli_version) {
case MLD_VERSION_1:
case MLD_VERSION_2:
printf("mldv%d", mli->mli_version);
break;
default:
printf("mldv?(%d)", mli->mli_version);
break;
}
printb(" flags", mli->mli_flags, "\020\1SILENT");
if (mli->mli_version == MLD_VERSION_2) {
printf(" rv %u qi %u qri %u uri %u",
mli->mli_rv, mli->mli_qi, mli->mli_qri, mli->mli_uri);
}
if (vflag >= 2) {
printf(" v1timer %u v2timer %u", mli->mli_v1_timer,
mli->mli_v2_timer);
}
printf("\n");
}
static void
if6_addrlist(struct ifaddr *ifap)
{
struct ifnet ifnet;
struct ifaddr ifa;
struct sockaddr sa;
struct in6_ifaddr if6a;
@ -460,6 +488,21 @@ if6_addrlist(struct ifaddr *ifap)
goto nextifap;
KREAD(ifap, &if6a, struct in6_ifaddr);
printf("\tinet6 %s\n", inet6_n2a(&if6a.ia_addr.sin6_addr));
/*
* Print per-link MLD information, if available.
*/
if (ifa.ifa_ifp != NULL) {
struct in6_ifextra ie;
struct mld_ifinfo mli;
KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
KREAD(ifnet.if_afdata[AF_INET6], &ie,
struct in6_ifextra);
if (ie.mld_ifinfo != NULL) {
KREAD(ie.mld_ifinfo, &mli, struct mld_ifinfo);
in6_ifinfo(&mli);
}
}
nextifap:
ifap = ifa.ifa_link.tqe_next;
}
@ -842,6 +885,110 @@ out_free:
#endif /* INET */
#ifdef INET6
/*
* Retrieve MLD per-group source filter mode and lists via sysctl.
*
* Note: The 128-bit IPv6 group addres needs to be segmented into
* 32-bit pieces for marshaling to sysctl. So the MIB name ends
* up looking like this:
* a.b.c.d.e.ifindex.g[0].g[1].g[2].g[3]
* Assumes that pgroup originated from the kernel, so its components
* are already in network-byte order.
*/
static void
in6m_print_sources_sysctl(uint32_t ifindex, struct in6_addr *pgroup)
{
#define MAX_SYSCTL_TRY 5
char addrbuf[INET6_ADDRSTRLEN];
int mib[10];
int ntry = 0;
int *pi;
size_t mibsize;
size_t len;
size_t needed;
size_t cnt;
int i;
char *buf;
struct in6_addr *pina;
uint32_t *p;
uint32_t fmode;
const char *modestr;
mibsize = sizeof(mib) / sizeof(mib[0]);
if (sysctlnametomib("net.inet6.ip6.mcast.filters", mib,
&mibsize) == -1) {
perror("sysctlnametomib");
return;
}
needed = 0;
mib[5] = ifindex;
pi = (int *)pgroup;
for (i = 0; i < 4; i++)
mib[6 + i] = *pi++;
mibsize = sizeof(mib) / sizeof(mib[0]);
do {
if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) {
perror("sysctl net.inet6.ip6.mcast.filters");
return;
}
if ((buf = malloc(needed)) == NULL) {
perror("malloc");
return;
}
if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) {
if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
perror("sysctl");
goto out_free;
}
free(buf);
buf = NULL;
}
} while (buf == NULL);
len = needed;
if (len < sizeof(uint32_t)) {
perror("sysctl");
goto out_free;
}
p = (uint32_t *)buf;
fmode = *p++;
len -= sizeof(uint32_t);
modestr = inm_mode(fmode);
if (modestr)
printf(" mode %s", modestr);
else
printf(" mode (%u)", fmode);
if (vflag == 0)
goto out_free;
cnt = len / sizeof(struct in6_addr);
pina = (struct in6_addr *)p;
for (i = 0; i < cnt; i++) {
if (i == 0)
printf(" srcs ");
inet_ntop(AF_INET6, (const char *)pina++, addrbuf,
INET6_ADDRSTRLEN);
fprintf(stdout, "%s%s", (i == 0 ? "" : ","), addrbuf);
len -= sizeof(struct in6_addr);
}
if (len > 0) {
fprintf(stderr, "warning: %u trailing bytes from %s\n",
(unsigned int)len, "net.inet6.ip6.mcast.filters");
}
out_free:
free(buf);
#undef MAX_SYSCTL_TRY
}
#endif /* INET6 */
static int
ifmcstat_getifmaddrs(void)
{
@ -1015,6 +1162,33 @@ ifmcstat_getifmaddrs(void)
}
in_ifinfo(&igi);
}
#endif /* INET */
#ifdef INET6
/*
* Print per-link MLD information, if available.
*/
if (pifasa->sa.sa_family == AF_INET6) {
struct mld_ifinfo mli;
size_t mibsize, len;
int mib[5];
mibsize = sizeof(mib) / sizeof(mib[0]);
if (sysctlnametomib("net.inet6.mld.ifinfo",
mib, &mibsize) == -1) {
perror("sysctlnametomib");
goto next_ifnet;
}
mib[mibsize] = thisifindex;
len = sizeof(struct mld_ifinfo);
if (sysctl(mib, mibsize + 1, &mli, &len, NULL,
0) == -1) {
perror("sysctl net.inet6.mld.ifinfo");
goto next_ifnet;
}
in6_ifinfo(&mli);
}
#endif /* INET6 */
#if defined(INET) || defined(INET6)
next_ifnet:
#endif
lastifasa = *pifasa;
@ -1040,6 +1214,12 @@ next_ifnet:
inm_print_sources_sysctl(thisifindex,
pgsa->sin.sin_addr);
}
#endif
#ifdef INET6
if (pgsa->sa.sa_family == AF_INET6) {
in6m_print_sources_sysctl(thisifindex,
&pgsa->sin6.sin6_addr);
}
#endif
fprintf(stdout, "\n");