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:
parent
af7bd9a4f4
commit
33cde13046
86
UPDATING
86
UPDATING
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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 *
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
},
|
||||
{
|
||||
|
@ -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 *));
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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 *));
|
||||
|
3455
sys/netinet6/mld6.c
3455
sys/netinet6/mld6.c
File diff suppressed because it is too large
Load Diff
@ -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_ */
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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");
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user