Merge IGMPv3 and Source-Specific Multicast (SSM) to the FreeBSD

IPv4 stack.

Diffs are minimized against p4.
PCS has been used for some protocol verification, more widespread
testing of recorded sources in Group-and-Source queries is needed.
sizeof(struct igmpstat) has changed.

__FreeBSD_version is bumped to 800070.
This commit is contained in:
Bruce M Simpson 2009-03-09 17:53:05 +00:00
parent 095b4d2689
commit d10910e6ce
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=189592
23 changed files with 6310 additions and 1483 deletions

View File

@ -22,6 +22,45 @@ 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.)
20090309:
IGMPv3 and Source-Specific Multicast (SSM) have been merged
to the IPv4 stack. VIMAGE hooks are in but not yet used.
For kernel developers, the most important changes are that the
ip_output() and ip_input() paths no longer take the IN_MULTI_LOCK(),
and this lock has been downgraded to a non-recursive mutex.
Transport protocols (UDP, Raw IP) are now responsible for filtering
inbound multicast traffic according to group membership and source
filters. The imo_multicast_filter() KPI exists for this purpose.
Transports which do not use multicast (SCTP, TCP) already reject
multicast by default. Forwarding and receive performance may improve
as a mutex acquisition is no longer needed in the ip_input()
low-level input path. in_addmulti() and in_delmulti() are shimmed
to new KPIs which exist to support SSM in-kernel.
For application developers, it is recommended that loopback of
multicast datagrams be disabled for best performance, as this
will still cause the lock to be taken for each looped-back
datagram transmission. The net.inet.ip.mcast.loop sysctl may
be tuned to 0 to disable loopback by default; it defaults to 1
to preserve the existing behaviour.
For systems administrators, to obtain best performance with
multicast reception and multiple groups, it is always recommended
that a card with a suitably precise hash filter is used. Hash
collisions will still result in the lock being taken within the
transport protocol input path to check group membership.
If deploying FreeBSD in an environment with IGMP snooping switches,
it is recommended that the net.inet.igmp.sendlocal sysctl remain
enabled; this forces 224.0.0.0/24 group membership to be announced
via IGMP.
The size of 'struct igmpstat' has changed; netstat needs to be
recompiled to reflect this.
Bump __FreeBSD_version to 800070.
20090309:
libusb20.so.1 is now installed as libusb.so.1 and the ports system
updated to use it. This requires a buildworld/installworld in order to

View File

@ -128,6 +128,7 @@ MAN= aac.4 \
if_bridge.4 \
ifmib.4 \
igb.4 \
igmp.4 \
iic.4 \
iicbb.4 \
iicbus.4 \

View File

@ -32,7 +32,7 @@
.\" @(#)ip.4 8.2 (Berkeley) 11/30/93
.\" $FreeBSD$
.\"
.Dd February 13, 2009
.Dd March 9, 2009
.Dt IP 4
.Os
.Sh NAME
@ -466,13 +466,19 @@ setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
.Pp
This option
improves performance for applications that may have no more than one
instance on a single host (such as a router daemon), by eliminating
instance on a single host (such as a routing daemon), by eliminating
the overhead of receiving their own transmissions.
It should generally not
be used by applications for which there may be more than one instance on a
single host (such as a conferencing program) or for which the sender does
not belong to the destination group (such as a time querying program).
.Pp
The sysctl setting
.Va net.inet.ip.mcast.loop
controls the default setting of the
.Dv IP_MULTICAST_LOOP
socket option for new sockets.
.Pp
A multicast datagram sent with an initial TTL greater than 1 may be delivered
to the sending host on a different interface from that on which it was sent,
if the host belongs to the destination group on that other interface.
@ -650,6 +656,13 @@ documented in RFC 3678.
For management of source filter lists using this API,
please refer to
.Xr sourcefilter 3 .
.Pp
The sysctl settings
.Va net.inet.ip.mcast.maxsocksrc
and
.Va net.inet.ip.mcast.maxgrpsrc
are used to specify an upper limit on the number of per-socket and per-group
source filter entries which the kernel may allocate.
.\"-----------------------
.Ss "Raw IP Sockets"
.Pp
@ -795,6 +808,7 @@ field was not equal to the length of the datagram written to the socket.
.Xr send 2 ,
.Xr byteorder 3 ,
.Xr icmp 4 ,
.Xr igmp 4 ,
.Xr inet 4 ,
.Xr intro 4 ,
.Xr multicast 4 ,

View File

@ -956,6 +956,7 @@ after the previous upcall.
.Xr socket 2 ,
.Xr sourcefilter 3 ,
.Xr icmp6 4 ,
.Xr igmp 4 ,
.Xr inet 4 ,
.Xr inet6 4 ,
.Xr intro 4 ,

View File

@ -81,7 +81,8 @@ __FBSDID("$FreeBSD$");
#define SIN(s) ((struct sockaddr_in *)s)
#define SDL(s) ((struct sockaddr_dl *)s)
#define LLTABLE(ifp) ((struct lltable *)(ifp)->if_afdata[AF_INET])
#define LLTABLE(ifp) \
((struct in_ifinfo *)(ifp)->if_afdata[AF_INET])->ii_llt
SYSCTL_DECL(_net_link_ether);
SYSCTL_NODE(_net_link_ether, PF_INET, inet, CTLFLAG_RW, 0, "");

File diff suppressed because it is too large Load Diff

View File

@ -46,105 +46,166 @@
* MULTICAST Revision: 3.5.1.3
*/
struct igmpstat {
u_int igps_rcv_total; /* total IGMP messages received */
u_int igps_rcv_tooshort; /* received with too few bytes */
u_int igps_rcv_badsum; /* received with bad checksum */
u_int igps_rcv_queries; /* received membership queries */
u_int igps_rcv_badqueries; /* received invalid queries */
u_int igps_rcv_reports; /* received membership reports */
u_int igps_rcv_badreports; /* received invalid reports */
u_int igps_rcv_ourreports; /* received reports for our groups */
u_int igps_snd_reports; /* sent membership reports */
u_int igps_rcv_toolong; /* received with too many bytes */
#ifndef BURN_BRIDGES
/*
* Pre-IGMPV3 igmpstat structure.
*/
struct oigmpstat {
u_int igps_rcv_total; /* total IGMP messages received */
u_int igps_rcv_tooshort; /* received with too few bytes */
u_int igps_rcv_badsum; /* received with bad checksum */
u_int igps_rcv_queries; /* received membership queries */
u_int igps_rcv_badqueries; /* received invalid queries */
u_int igps_rcv_reports; /* received membership reports */
u_int igps_rcv_badreports; /* received invalid reports */
u_int igps_rcv_ourreports; /* received reports for our groups */
u_int igps_snd_reports; /* sent membership reports */
u_int igps_rcv_toolong; /* received with too many bytes */
};
#endif
/*
* IGMPv3 protocol statistics.
*/
struct igmpstat {
/*
* Structure header (to insulate ABI changes).
*/
uint32_t igps_version; /* version of this structure */
uint32_t igps_len; /* length of this structure */
/*
* Message statistics.
*/
uint64_t igps_rcv_total; /* total IGMP messages received */
uint64_t igps_rcv_tooshort; /* received with too few bytes */
uint64_t igps_rcv_badttl; /* received with ttl other than 1 */
uint64_t igps_rcv_badsum; /* received with bad checksum */
/*
* Query statistics.
*/
uint64_t igps_rcv_v1v2_queries; /* received IGMPv1/IGMPv2 queries */
uint64_t igps_rcv_v3_queries; /* received IGMPv3 queries */
uint64_t igps_rcv_badqueries; /* received invalid queries */
uint64_t igps_rcv_gen_queries; /* received general queries */
uint64_t igps_rcv_group_queries;/* received group queries */
uint64_t igps_rcv_gsr_queries; /* received group-source queries */
uint64_t igps_drop_gsr_queries; /* dropped group-source queries */
/*
* Report statistics.
*/
uint64_t igps_rcv_reports; /* received membership reports */
uint64_t igps_rcv_badreports; /* received invalid reports */
uint64_t igps_rcv_ourreports; /* received reports for our groups */
uint64_t igps_rcv_nora; /* received w/o Router Alert option */
uint64_t igps_snd_reports; /* sent membership reports */
/*
* Padding for future additions.
*/
uint64_t __igps_pad[4];
};
#define IGPS_VERSION_3 3 /* as of FreeBSD 8.x */
#define IGPS_VERSION3_LEN 168
#ifdef CTASSERT
CTASSERT(sizeof(struct igmpstat) == 168);
#endif
#ifdef _KERNEL
#define IGMP_RANDOM_DELAY(X) (random() % (X) + 1)
/*
* States for IGMPv2's leave processing
*/
#define IGMP_OTHERMEMBER 0
#define IGMP_IREPORTEDLAST 1
#define IGMP_MAX_STATE_CHANGES 24 /* Max pending changes per group */
/*
* State masks for IGMPv3
* IGMP per-group states.
*/
#define IGMP_V3_NONEXISTENT 0x01
#define IGMP_V3_OTHERMEMBER 0x02
#define IGMP_V3_IREPORTEDLAST 0x04
#define IGMP_NOT_MEMBER 0 /* Can garbage collect in_multi */
#define IGMP_SILENT_MEMBER 1 /* Do not perform IGMP for group */
#define IGMP_REPORTING_MEMBER 2 /* IGMPv1/2/3 we are reporter */
#define IGMP_IDLE_MEMBER 3 /* IGMPv1/2 we reported last */
#define IGMP_LAZY_MEMBER 4 /* IGMPv1/2 other member reporting */
#define IGMP_SLEEPING_MEMBER 5 /* IGMPv1/2 start query response */
#define IGMP_AWAKENING_MEMBER 6 /* IGMPv1/2 group timer will start */
#define IGMP_G_QUERY_PENDING_MEMBER 7 /* IGMPv3 group query pending */
#define IGMP_SG_QUERY_PENDING_MEMBER 8 /* IGMPv3 source query pending */
#define IGMP_LEAVING_MEMBER 9 /* IGMPv3 dying gasp (pending last */
/* retransmission of INCLUDE {}) */
/*
* We must remember what version the subnet's querier is.
* We conveniently use the IGMP message type for the proper
* membership report to keep this state.
* IGMP version tag.
*/
#define IGMP_V1_ROUTER IGMP_V1_MEMBERSHIP_REPORT
#define IGMP_V2_ROUTER IGMP_V2_MEMBERSHIP_REPORT
#define IGMP_V3_ROUTER IGMP_V3_MEMBERSHIP_REPORT
#define IGMP_VERSION_NONE 0 /* Invalid */
#define IGMP_VERSION_1 1
#define IGMP_VERSION_2 2
#define IGMP_VERSION_3 3 /* Default */
/*
* Revert to new router if we haven't heard from an old router in
* this amount of time.
* IGMPv3 protocol control variables.
*/
#define IGMP_AGE_THRESHOLD 540
#define IGMP_RV_INIT 2 /* Robustness Variable */
#define IGMP_RV_MIN 1
#define IGMP_RV_MAX 7
#define IGMP_QI_INIT 125 /* Query Interval (s) */
#define IGMP_QI_MIN 1
#define IGMP_QI_MAX 255
#define IGMP_QRI_INIT 10 /* Query Response Interval (s) */
#define IGMP_QRI_MIN 1
#define IGMP_QRI_MAX 255
#define IGMP_URI_INIT 3 /* Unsolicited Report Interval (s) */
#define IGMP_URI_MIN 0
#define IGMP_URI_MAX 10
#define IGMP_MAX_G_GS_PACKETS 8 /* # of packets to answer G/GS */
#define IGMP_MAX_STATE_CHANGE_PACKETS 8 /* # of packets per state change */
#define IGMP_MAX_RESPONSE_PACKETS 16 /* # of packets for general query */
#define IGMP_MAX_RESPONSE_BURST 4 /* # of responses to send at once */
#define IGMP_RESPONSE_BURST_INTERVAL (PR_FASTHZ / 2) /* 500ms */
/*
* IGMPv3 protocol defaults
* IGMP-specific mbuf flags.
*/
#define IGMP_INIT_ROBVAR 2 /* Robustness */
#define IGMP_MAX_ROBVAR 7
#define IGMP_INIT_QRYINT 125 /* Querier's Query interval */
#define IGMP_MAX_QRYINT 255
#define IGMP_INIT_QRYRSP 10 /* Query Response interval */
#define IGMP_DEF_QRYMRT 10
#define IGMP_UNSOL_INT 1 /* Unsolicited Report interval */
#define M_IGMPV2 M_PROTO1 /* Packet is IGMPv2 */
#define M_IGMPV3_HDR M_PROTO2 /* Packet has IGMPv3 headers */
#define M_GROUPREC M_PROTO3 /* mbuf chain is a group record */
#define M_IGMP_LOOP M_PROTO4 /* transmit on loif, not real ifp */
/*
* IGMPv3 report types
* Default amount of leading space for IGMPv3 to allocate at the
* beginning of its mbuf packet chains, to avoid fragmentation and
* unnecessary allocation of leading mbufs.
*/
#define IGMP_REPORT_MODE_IN 1 /* mode-is-include */
#define IGMP_REPORT_MODE_EX 2 /* mode-is-exclude */
#define IGMP_REPORT_TO_IN 3 /* change-to-include */
#define IGMP_REPORT_TO_EX 4 /* change-to-exclude */
#define IGMP_REPORT_ALLOW_NEW 5 /* allow-new-sources */
#define IGMP_REPORT_BLOCK_OLD 6 /* block-old-sources */
#define RAOPT_LEN 4 /* Length of IP Router Alert option */
#define IGMP_LEADINGSPACE \
(sizeof(struct ip) + RAOPT_LEN + sizeof(struct igmp_report))
/*
* Report types
* Subsystem lock macros.
* The IGMP lock is only taken with IGMP. Currently it is system-wide.
* VIMAGE: The lock could be pushed to per-VIMAGE granularity in future.
*/
#define IGMP_MASK_CUR_STATE 0x01 /* Report current-state */
#define IGMP_MASK_ALLOW_NEW 0x02 /* Report source as allow-new */
#define IGMP_MASK_BLOCK_OLD 0x04 /* Report source as block-old */
#define IGMP_MASK_TO_IN 0x08 /* Report source as to_in */
#define IGMP_MASK_TO_EX 0x10 /* Report source as to_ex */
#define IGMP_MASK_STATE_T1 0x20 /* State at T1 */
#define IGMP_MASK_STATE_T2 0x40 /* State at T2 */
#define IGMP_MASK_IF_STATE 0x80 /* Report current-state per interface */
#define IGMP_LOCK_INIT() mtx_init(&igmp_mtx, "igmp_mtx", NULL, MTX_DEF)
#define IGMP_LOCK_DESTROY() mtx_destroy(&igmp_mtx)
#define IGMP_LOCK() mtx_lock(&igmp_mtx)
#define IGMP_LOCK_ASSERT() mtx_assert(&igmp_mtx, MA_OWNED)
#define IGMP_UNLOCK() mtx_unlock(&igmp_mtx)
#define IGMP_UNLOCK_ASSERT() mtx_assert(&igmp_mtx, MA_NOTOWNED)
#define IGMP_MASK_STATE_TX (IGMP_MASK_STATE_T1 | IGMP_MASK_STATE_T2)
#define IGMP_MASK_PENDING (IGMP_MASK_CUR_STATE | \
IGMP_MASK_ALLOW_NEW | \
IGMP_MASK_BLOCK_OLD)
struct igmp_ifinfo;
/*
* List identifiers
*/
#define IGMP_EXCLUDE_LIST 1 /* exclude list used to tag report */
#define IGMP_INCLUDE_LIST 2 /* include list used to tag report */
#define IGMP_RECORDED_LIST 3 /* recorded list used to tag report */
void igmp_init(void);
void igmp_input(struct mbuf *, int);
void igmp_joingroup(struct in_multi *);
void igmp_leavegroup(struct in_multi *);
int igmp_change_state(struct in_multi *);
void igmp_fasttimo(void);
struct igmp_ifinfo *
igmp_domifattach(struct ifnet *);
void igmp_domifdetach(struct ifnet *);
void igmp_ifdetach(struct ifnet *);
void igmp_input(struct mbuf *, int);
void igmp_slowtimo(void);
SYSCTL_DECL(_net_inet_igmp);
#endif
#endif /* _KERNEL */
/*
* Names for IGMP sysctl objects

View File

@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/vinet.h>
#include <netinet/igmp_var.h>
static int in_mask2len(struct in_addr *);
static void in_len2mask(struct in_addr *, int);
@ -215,12 +216,14 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
struct in_addr allhosts_addr;
struct in_addr dst;
struct in_ifaddr *oia;
struct in_ifinfo *ii;
struct in_aliasreq *ifra = (struct in_aliasreq *)data;
struct sockaddr_in oldaddr;
int error, hostIsNew, iaIsNew, maskIsNew, s;
int iaIsFirst;
ia = NULL;
ii = ((struct in_ifinfo *)ifp->if_afdata[AF_INET]);
iaIsFirst = 0;
iaIsNew = 0;
allhosts_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
@ -425,8 +428,11 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
if (error != 0 && iaIsNew)
break;
if (error == 0) {
if (iaIsFirst && (ifp->if_flags & IFF_MULTICAST) != 0)
in_addmulti(&allhosts_addr, ifp);
if (iaIsFirst &&
(ifp->if_flags & IFF_MULTICAST) != 0) {
error = in_joingroup(ifp, &allhosts_addr,
NULL, &ii->ii_allhosts);
}
EVENTHANDLER_INVOKE(ifaddr_event, ifp);
}
return (0);
@ -472,8 +478,11 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
(ifra->ifra_broadaddr.sin_family == AF_INET))
ia->ia_broadaddr = ifra->ifra_broadaddr;
if (error == 0) {
if (iaIsFirst && (ifp->if_flags & IFF_MULTICAST) != 0)
in_addmulti(&allhosts_addr, ifp);
if (iaIsFirst &&
(ifp->if_flags & IFF_MULTICAST) != 0) {
error = in_joingroup(ifp, &allhosts_addr,
NULL, &ii->ii_allhosts);
}
EVENTHANDLER_INVOKE(ifaddr_event, ifp);
}
return (error);
@ -515,18 +524,18 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
/*
* If this is the last IPv4 address configured on this
* interface, leave the all-hosts group.
* XXX: This is quite ugly because of locking and structure.
* No state-change report need be transmitted.
*/
oia = NULL;
IFP_TO_IA(ifp, oia);
if (oia == NULL) {
struct in_multi *inm;
IFF_LOCKGIANT(ifp);
IN_MULTI_LOCK();
IN_LOOKUP_MULTI(allhosts_addr, ifp, inm);
if (inm != NULL)
in_delmulti_locked(inm);
if (ii->ii_allhosts) {
(void)in_leavegroup_locked(ii->ii_allhosts,
NULL);
ii->ii_allhosts = NULL;
}
IN_MULTI_UNLOCK();
IFF_UNLOCKGIANT(ifp);
}
@ -992,27 +1001,6 @@ in_broadcast(struct in_addr in, struct ifnet *ifp)
#undef ia
}
/*
* Delete all IPv4 multicast address records, and associated link-layer
* multicast address records, associated with ifp.
*/
static void
in_purgemaddrs(struct ifnet *ifp)
{
INIT_VNET_INET(ifp->if_vnet);
struct in_multi *inm;
struct in_multi *oinm;
IFF_LOCKGIANT(ifp);
IN_MULTI_LOCK();
LIST_FOREACH_SAFE(inm, &V_in_multihead, inm_link, oinm) {
if (inm->inm_ifp == ifp)
in_delmulti_locked(inm);
}
IN_MULTI_UNLOCK();
IFF_UNLOCKGIANT(ifp);
}
/*
* On interface removal, clean up IPv4 data structures hung off of the ifnet.
*/
@ -1026,6 +1014,46 @@ in_ifdetach(struct ifnet *ifp)
in_purgemaddrs(ifp);
}
/*
* Delete all IPv4 multicast address records, and associated link-layer
* multicast address records, associated with ifp.
* XXX It looks like domifdetach runs AFTER the link layer cleanup.
*/
static void
in_purgemaddrs(struct ifnet *ifp)
{
INIT_VNET_INET(ifp->if_vnet);
LIST_HEAD(,in_multi) purgeinms;
struct in_multi *inm, *tinm;
struct ifmultiaddr *ifma;
LIST_INIT(&purgeinms);
IN_MULTI_LOCK();
/*
* Extract list of in_multi associated with the detaching ifp
* which the PF_INET 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_INET)
continue;
inm = (struct in_multi *)ifma->ifma_protospec;
LIST_INSERT_HEAD(&purgeinms, inm, inm_link);
}
IF_ADDR_UNLOCK(ifp);
LIST_FOREACH_SAFE(inm, &purgeinms, inm_link, tinm) {
inm_release_locked(inm);
LIST_REMOVE(inm, inm_link);
}
igmp_ifdetach(ifp);
IN_MULTI_UNLOCK();
}
#include <sys/syslog.h>
#include <net/if_dl.h>
#include <netinet/if_ether.h>
@ -1250,9 +1278,13 @@ in_lltable_dump(struct lltable *llt, struct sysctl_req *wr)
void *
in_domifattach(struct ifnet *ifp)
{
struct lltable *llt = lltable_init(ifp, AF_INET);
{
struct in_ifinfo *ii;
struct lltable *llt;
ii = malloc(sizeof(struct in_ifinfo), M_IFADDR, M_WAITOK|M_ZERO);
llt = lltable_init(ifp, AF_INET);
if (llt != NULL) {
llt->llt_new = in_lltable_new;
llt->llt_free = in_lltable_free;
@ -1260,13 +1292,19 @@ in_domifattach(struct ifnet *ifp)
llt->llt_lookup = in_lltable_lookup;
llt->llt_dump = in_lltable_dump;
}
return (llt);
ii->ii_llt = llt;
ii->ii_igmp = igmp_domifattach(ifp);
return ii;
}
void
in_domifdetach(struct ifnet *ifp __unused, void *aux)
in_domifdetach(struct ifnet *ifp, void *aux)
{
struct lltable *llt = (struct lltable *)aux;
struct in_ifinfo *ii = (struct in_ifinfo *)aux;
lltable_free(llt);
igmp_domifdetach(ifp);
lltable_free(ii->ii_llt);
free(ii, M_IFADDR);
}

View File

@ -509,6 +509,7 @@ __END_DECLS
*/
#define IP_MAX_GROUP_SRC_FILTER 512 /* sources per group */
#define IP_MAX_SOCK_SRC_FILTER 128 /* sources per socket/group */
#define IP_MAX_SOCK_MUTE_FILTER 128 /* XXX no longer used */
/*
* Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP.

File diff suppressed because it is too large Load Diff

View File

@ -206,7 +206,6 @@ struct protosw inetsw[] = {
.pr_flags = PR_ATOMIC|PR_ADDR|PR_LASTHDR,
.pr_input = igmp_input,
.pr_ctloutput = rip_ctloutput,
.pr_init = igmp_init,
.pr_fasttimo = igmp_fasttimo,
.pr_slowtimo = igmp_slowtimo,
.pr_usrreqs = &rip_usrreqs

View File

@ -35,6 +35,20 @@
#include <sys/queue.h>
#include <sys/fnv_hash.h>
#include <sys/tree.h>
struct igmp_ifinfo;
struct in_multi;
struct lltable;
/*
* IPv4 per-interface state.
*/
struct in_ifinfo {
struct lltable *ii_llt; /* ARP state */
struct igmp_ifinfo *ii_igmp; /* IGMP state */
struct in_multi *ii_allhosts; /* 224.0.0.1 membership */
};
/*
* Interface address, Internet version. One of these structures
@ -151,77 +165,163 @@ do { \
(((((x) & 0xF) | ((((x) >> 8) & 0xF) << 4)) ^ (y)) & IPREASS_HMASK)
/*
* This information should be part of the ifnet structure but we don't wish
* to change that - as it might break a number of things
* Legacy IPv4 IGMP per-link structure.
*/
struct router_info {
struct ifnet *rti_ifp;
int rti_type; /* type of router which is querier on this interface */
int rti_time; /* # of slow timeouts since last old query */
SLIST_ENTRY(router_info) rti_list;
#ifdef notyet
int rti_timev1; /* IGMPv1 querier present */
int rti_timev2; /* IGMPv2 querier present */
int rti_timer; /* report to general query */
int rti_qrv; /* querier robustness */
#endif
};
/*
* Internet multicast address structure. There is one of these for each IP
* multicast group to which this host belongs on a given network interface.
* For every entry on the interface's if_multiaddrs list which represents
* an IP multicast group, there is one of these structures. They are also
* kept on a system-wide list to make it easier to keep our legacy IGMP code
* compatible with the rest of the world (see IN_FIRST_MULTI et al, below).
* Per-interface IGMP router version information.
*/
struct igmp_ifinfo {
LIST_ENTRY(igmp_ifinfo) igi_link;
struct ifnet *igi_ifp; /* interface this instance belongs to */
uint32_t igi_version; /* IGMPv3 Host Compatibility Mode */
uint32_t igi_v1_timer; /* IGMPv1 Querier Present timer (s) */
uint32_t igi_v2_timer; /* IGMPv2 Querier Present timer (s) */
uint32_t igi_v3_timer; /* IGMPv3 General Query (interface) timer (s)*/
uint32_t igi_flags; /* IGMP per-interface flags */
uint32_t igi_rv; /* IGMPv3 Robustness Variable */
uint32_t igi_qi; /* IGMPv3 Query Interval (s) */
uint32_t igi_qri; /* IGMPv3 Query Response Interval (s) */
uint32_t igi_uri; /* IGMPv3 Unsolicited Report Interval (s) */
SLIST_HEAD(,in_multi) igi_relinmhead; /* released groups */
struct ifqueue igi_gq; /* queue of general query responses */
};
#define IGIF_SILENT 0x00000001 /* Do not use IGMP on this ifp */
#define IGIF_LOOPBACK 0x00000002 /* Send IGMP reports to loopback */
/*
* IPv4 multicast IGMP-layer source entry.
*/
struct ip_msource {
RB_ENTRY(ip_msource) ims_link; /* RB tree links */
in_addr_t ims_haddr; /* host byte order */
struct ims_st {
uint16_t ex; /* # of exclusive members */
uint16_t in; /* # of inclusive members */
} ims_st[2]; /* state at t0, t1 */
uint8_t ims_stp; /* pending query */
};
/*
* IPv4 multicast PCB-layer source entry.
*/
struct in_msource {
RB_ENTRY(ip_msource) ims_link; /* RB tree links */
in_addr_t ims_haddr; /* host byte order */
uint8_t imsl_st[2]; /* state before/at commit */
};
RB_HEAD(ip_msource_tree, ip_msource); /* define struct ip_msource_tree */
static __inline int
ip_msource_cmp(const struct ip_msource *a, const struct ip_msource *b)
{
if (a->ims_haddr < b->ims_haddr)
return (-1);
if (a->ims_haddr == b->ims_haddr)
return (0);
return (1);
}
RB_PROTOTYPE(ip_msource_tree, ip_msource, ims_link, ip_msource_cmp);
/*
* IPv4 multicast PCB-layer group filter descriptor.
*/
struct in_mfilter {
struct ip_msource_tree imf_sources; /* source list for (S,G) */
u_long imf_nsrc; /* # of source entries */
uint8_t imf_st[2]; /* state before/at commit */
};
/*
* IPv4 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 IGMPv3 is active, inm_timer is the response to group query timer.
* The state-change timer inm_sctimer is separate; whenever state changes
* for the group the state change record is generated and transmitted,
* and kept if retransmissions are necessary.
*
* FUTURE: inm_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 in_multi {
LIST_ENTRY(in_multi) inm_link; /* queue macro glue */
LIST_ENTRY(in_multi) inm_link; /* to-be-released by in_ifdetach */
struct in_addr inm_addr; /* IP multicast address, convenience */
struct ifnet *inm_ifp; /* back pointer to ifnet */
struct ifmultiaddr *inm_ifma; /* back pointer to ifmultiaddr */
u_int inm_timer; /* IGMP membership report timer */
u_int inm_state; /* state of the membership */
struct router_info *inm_rti; /* router info*/
u_int inm_timer; /* IGMPv1/v2 group / v3 query timer */
u_int inm_state; /* state of the membership */
void *inm_rti; /* unused, legacy field */
u_int inm_refcount; /* reference count */
#ifdef notyet /* IGMPv3 source-specific multicast fields */
TAILQ_HEAD(, in_msfentry) inm_msf; /* all active source filters */
TAILQ_HEAD(, in_msfentry) inm_msf_record; /* recorded sources */
TAILQ_HEAD(, in_msfentry) inm_msf_exclude; /* exclude sources */
TAILQ_HEAD(, in_msfentry) inm_msf_include; /* include sources */
/* XXX: should this lot go to the router_info structure? */
/* XXX: can/should these be callouts? */
/* IGMP protocol timers */
int32_t inm_ti_curstate; /* current state timer */
int32_t inm_ti_statechg; /* state change timer */
/* IGMP report timers */
uint16_t inm_rpt_statechg; /* state change report timer */
uint16_t inm_rpt_toxx; /* fmode change report timer */
/* IGMP protocol state */
uint16_t inm_fmode; /* filter mode */
uint32_t inm_recsrc_count; /* # of recorded sources */
uint16_t inm_exclude_sock_count; /* # of exclude-mode sockets */
uint16_t inm_gass_count; /* # of g-a-s queries */
#endif
/* New fields for IGMPv3 follow. */
struct igmp_ifinfo *inm_igi; /* IGMP info */
SLIST_ENTRY(in_multi) inm_nrele; /* to-be-released by IGMP */
struct ip_msource_tree inm_srcs; /* tree of sources */
u_long inm_nsrc; /* # of tree entries */
struct ifqueue inm_scq; /* queue of pending
* state-change packets */
struct timeval inm_lastgsrtv; /* Time of last G-S-R query */
uint16_t inm_sctimer; /* state-change timer */
uint16_t inm_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 IGMPv3 state-change reports. Several refcounts
* are maintained here to optimize for common use-cases.
*/
struct inm_st {
uint16_t iss_fmode; /* IGMP 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 */
} inm_st[2]; /* state at t0, t1 */
};
#ifdef notyet
/*
* Internet multicast source filter list. This list is used to store
* IP multicast source addresses for each membership on an interface.
* TODO: Allocate these structures using UMA.
* TODO: Find an easier way of linking the struct into two lists at once.
* 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).
*/
struct in_msfentry {
TAILQ_ENTRY(in_msfentry) isf_link; /* next filter in all-list */
TAILQ_ENTRY(in_msfentry) isf_next; /* next filter in queue */
struct in_addr isf_addr; /* the address of this source */
uint16_t isf_refcount; /* reference count */
uint16_t isf_reporttag; /* what to report to the IGMP router */
uint16_t isf_rexmit; /* retransmission state/count */
};
#endif
static __inline uint8_t
ims_get_mode(const struct in_multi *inm, const struct ip_msource *ims,
uint8_t t)
{
t = !!t;
if (inm->inm_st[t].iss_ex > 0 &&
inm->inm_st[t].iss_ex == ims->ims_st[t].ex)
return (MCAST_EXCLUDE);
else if (ims->ims_st[t].in > 0 && ims->ims_st[t].ex == 0)
return (MCAST_INCLUDE);
return (MCAST_UNDEFINED);
}
#ifdef _KERNEL
@ -231,10 +331,10 @@ SYSCTL_DECL(_net_inet_ip);
SYSCTL_DECL(_net_inet_raw);
#endif
LIST_HEAD(in_multihead, in_multi);
LIST_HEAD(in_multihead, in_multi); /* XXX unused */
#ifdef VIMAGE_GLOBALS
extern struct in_multihead in_multihead;
#endif
#endif /* BURN_BRIDGES */
/*
* Lock macros for IPv4 layer multicast address lists. IPv4 lock goes
@ -246,74 +346,90 @@ extern struct mtx in_multi_mtx;
#define IN_MULTI_LOCK() mtx_lock(&in_multi_mtx)
#define IN_MULTI_UNLOCK() mtx_unlock(&in_multi_mtx)
#define IN_MULTI_LOCK_ASSERT() mtx_assert(&in_multi_mtx, MA_OWNED)
#define IN_MULTI_UNLOCK_ASSERT() mtx_assert(&in_multi_mtx, MA_NOTOWNED)
/*
* Structure used by macros below to remember position when stepping through
* all of the in_multi records.
* Function for looking up an in_multi record for an IPv4 multicast address
* on a given interface. ifp must be valid. If no record found, return NULL.
* The IN_MULTI_LOCK and IF_ADDR_LOCK on ifp must be held.
*/
struct in_multistep {
struct in_multi *i_inm;
};
static __inline struct in_multi *
inm_lookup_locked(struct ifnet *ifp, const struct in_addr ina)
{
struct ifmultiaddr *ifma;
struct in_multi *inm;
IN_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_INET) {
inm = (struct in_multi *)ifma->ifma_protospec;
if (inm->inm_addr.s_addr == ina.s_addr)
break;
inm = NULL;
}
}
return (inm);
}
/*
* Macro for looking up the in_multi record for a given IP multicast address
* on a given interface. If no matching record is found, "inm" is set null.
* Wrapper for inm_lookup_locked().
* The IF_ADDR_LOCK will be taken on ifp and released on return.
*/
#define IN_LOOKUP_MULTI(addr, ifp, inm) \
/* struct in_addr addr; */ \
/* struct ifnet *ifp; */ \
/* struct in_multi *inm; */ \
do { \
struct ifmultiaddr *ifma; \
\
IN_MULTI_LOCK_ASSERT(); \
IF_ADDR_LOCK(ifp); \
TAILQ_FOREACH(ifma, &((ifp)->if_multiaddrs), ifma_link) { \
if (ifma->ifma_addr->sa_family == AF_INET \
&& ((struct sockaddr_in *)ifma->ifma_addr)->sin_addr.s_addr == \
(addr).s_addr) \
break; \
} \
(inm) = ifma ? ifma->ifma_protospec : 0; \
IF_ADDR_UNLOCK(ifp); \
} while(0)
static __inline struct in_multi *
inm_lookup(struct ifnet *ifp, const struct in_addr ina)
{
struct in_multi *inm;
IN_MULTI_LOCK_ASSERT();
IF_ADDR_LOCK(ifp);
inm = inm_lookup_locked(ifp, ina);
IF_ADDR_UNLOCK(ifp);
return (inm);
}
/* Acquire an in_multi record. */
static __inline void
inm_acquire_locked(struct in_multi *inm)
{
IN_MULTI_LOCK_ASSERT();
++inm->inm_refcount;
}
/*
* Macro to step through all of the in_multi records, one at a time.
* The current position is remembered in "step", which the caller must
* provide. IN_FIRST_MULTI(), below, must be called to initialize "step"
* and get the first record. Both macros return a NULL "inm" when there
* are no remaining records.
* Return values for imo_multi_filter().
*/
#define IN_NEXT_MULTI(step, inm) \
/* struct in_multistep step; */ \
/* struct in_multi *inm; */ \
do { \
IN_MULTI_LOCK_ASSERT(); \
if (((inm) = (step).i_inm) != NULL) \
(step).i_inm = LIST_NEXT((step).i_inm, inm_link); \
} while(0)
#define IN_FIRST_MULTI(step, inm) \
/* struct in_multistep step; */ \
/* struct in_multi *inm; */ \
do { \
IN_MULTI_LOCK_ASSERT(); \
(step).i_inm = LIST_FIRST(&V_in_multihead); \
IN_NEXT_MULTI((step), (inm)); \
} while(0)
#define MCAST_PASS 0 /* Pass */
#define MCAST_NOTGMEMBER 1 /* This host not a member of group */
#define MCAST_NOTSMEMBER 2 /* This host excluded source */
#define MCAST_MUTED 3 /* [deprecated] */
struct rtentry;
struct route;
struct ip_moptions;
size_t imo_match_group(struct ip_moptions *, struct ifnet *,
struct sockaddr *);
struct in_msource *imo_match_source(struct ip_moptions *, size_t,
struct sockaddr *);
struct in_multi *in_addmulti(struct in_addr *, struct ifnet *);
int imo_multi_filter(const struct ip_moptions *, const struct ifnet *,
const struct sockaddr *, const struct sockaddr *);
void inm_commit(struct in_multi *);
void inm_clear_recorded(struct in_multi *);
void inm_print(const struct in_multi *);
int inm_record_source(struct in_multi *inm, const in_addr_t);
void inm_release(struct in_multi *);
void inm_release_locked(struct in_multi *);
struct in_multi *
in_addmulti(struct in_addr *, struct ifnet *);
void in_delmulti(struct in_multi *);
void in_delmulti_locked(struct in_multi *);
int in_joingroup(struct ifnet *, const struct in_addr *,
/*const*/ struct in_mfilter *, struct in_multi **);
int in_joingroup_locked(struct ifnet *, const struct in_addr *,
/*const*/ struct in_mfilter *, struct in_multi **);
int in_leavegroup(struct in_multi *, /*const*/ struct in_mfilter *);
int in_leavegroup_locked(struct in_multi *,
/*const*/ struct in_mfilter *);
int in_control(struct socket *, u_long, caddr_t, struct ifnet *,
struct thread *);
void in_rtqdrain(void);

View File

@ -592,7 +592,6 @@ ip_input(struct mbuf *m)
return;
}
if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
struct in_multi *inm;
if (V_ip_mrouter) {
/*
* If we are acting as a multicast router, all
@ -619,17 +618,10 @@ ip_input(struct mbuf *m)
V_ipstat.ips_forward++;
}
/*
* See if we belong to the destination multicast group on the
* arrival interface.
* Assume the packet is for us, to avoid prematurely taking
* a lock on the in_multi hash. Protocols must perform
* their own filtering and update statistics accordingly.
*/
IN_MULTI_LOCK();
IN_LOOKUP_MULTI(ip->ip_dst, m->m_pkthdr.rcvif, inm);
IN_MULTI_UNLOCK();
if (inm == NULL) {
V_ipstat.ips_notmember++;
m_freem(m);
return;
}
goto ours;
}
if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST)

View File

@ -81,25 +81,6 @@ struct ipoption {
char ipopt_list[MAX_IPOPTLEN]; /* options proper */
};
/*
* Multicast source list entry.
*/
struct in_msource {
TAILQ_ENTRY(in_msource) ims_next; /* next source */
struct sockaddr_storage ims_addr; /* address of this source */
};
/*
* Multicast filter descriptor; there is one instance per group membership
* on a socket, allocated as an expandable vector hung off ip_moptions.
* struct in_multi contains separate IPv4-stack-wide state for IGMPv3.
*/
struct in_mfilter {
uint16_t imf_fmode; /* filter mode for this socket/group */
uint16_t imf_nsources; /* # of sources for this socket/group */
TAILQ_HEAD(, in_msource) imf_sources; /* source list */
};
/*
* Structure attached to inpcb.ip_moptions and
* passed to ip_output when IP multicast options are in use.

View File

@ -251,6 +251,7 @@ void
rip_input(struct mbuf *m, int off)
{
INIT_VNET_INET(curvnet);
struct ifnet *ifp;
struct ip *ip = mtod(m, struct ip *);
int proto = ip->ip_p;
struct inpcb *inp, *last;
@ -262,6 +263,9 @@ rip_input(struct mbuf *m, int off)
ripsrc.sin_family = AF_INET;
ripsrc.sin_addr = ip->ip_src;
last = NULL;
ifp = m->m_pkthdr.rcvif;
hash = INP_PCBHASH_RAW(proto, ip->ip_src.s_addr,
ip->ip_dst.s_addr, V_ripcbinfo.ipi_hashmask);
INP_INFO_RLOCK(&V_ripcbinfo);
@ -277,8 +281,14 @@ rip_input(struct mbuf *m, int off)
continue;
if (inp->inp_faddr.s_addr != ip->ip_src.s_addr)
continue;
if (prison_check_ip4(inp->inp_cred, &ip->ip_dst) != 0)
continue;
if (jailed(inp->inp_cred)) {
/*
* XXX: If faddr was bound to multicast group,
* jailed raw socket will drop datagram.
*/
if (prison_check_ip4(inp->inp_cred, &ip->ip_dst) != 0)
continue;
}
if (last != NULL) {
struct mbuf *n;
@ -299,14 +309,46 @@ rip_input(struct mbuf *m, int off)
if ((inp->inp_vflag & INP_IPV4) == 0)
continue;
#endif
if (inp->inp_laddr.s_addr &&
inp->inp_laddr.s_addr != ip->ip_dst.s_addr)
if (!in_nullhost(inp->inp_laddr) &&
!in_hosteq(inp->inp_laddr, ip->ip_dst))
continue;
if (inp->inp_faddr.s_addr &&
inp->inp_faddr.s_addr != ip->ip_src.s_addr)
continue;
if (prison_check_ip4(inp->inp_cred, &ip->ip_dst) != 0)
if (!in_nullhost(inp->inp_faddr) &&
!in_hosteq(inp->inp_faddr, ip->ip_src))
continue;
if (jailed(inp->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 (!IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) &&
prison_check_ip4(inp->inp_cred, &ip->ip_dst) != 0)
continue;
}
/*
* 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 (inp->inp_moptions != NULL &&
IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
struct sockaddr_in group;
int blocked;
bzero(&group, sizeof(struct sockaddr_in));
group.sin_len = sizeof(struct sockaddr_in);
group.sin_family = AF_INET;
group.sin_addr = ip->ip_dst;
blocked = imo_multi_filter(inp->inp_moptions, ifp,
(struct sockaddr *)&group,
(struct sockaddr *)&ripsrc);
if (blocked != MCAST_PASS) {
V_ipstat.ips_notmember++;
continue;
}
}
if (last != NULL) {
struct mbuf *n;

View File

@ -413,12 +413,6 @@ udp_input(struct mbuf *m, int off)
if (inp->inp_faddr.s_addr != INADDR_ANY &&
inp->inp_faddr.s_addr != ip->ip_src.s_addr)
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;
@ -432,54 +426,23 @@ udp_input(struct mbuf *m, int off)
imo = inp->inp_moptions;
if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) &&
imo != NULL) {
struct sockaddr_in sin;
struct in_msource *ims;
int blocked, mode;
size_t idx;
struct sockaddr_in group;
int blocked;
bzero(&sin, sizeof(struct sockaddr_in));
sin.sin_len = sizeof(struct sockaddr_in);
sin.sin_family = AF_INET;
sin.sin_addr = ip->ip_dst;
bzero(&group, sizeof(struct sockaddr_in));
group.sin_len = sizeof(struct sockaddr_in);
group.sin_family = AF_INET;
group.sin_addr = ip->ip_dst;
blocked = 0;
idx = imo_match_group(imo, ifp,
(struct sockaddr *)&sin);
if (idx == -1) {
/*
* No group membership for this socket.
* Do not bump udps_noportbcast, as
* this will happen further down.
*/
blocked++;
} else {
/*
* Check for a multicast source filter
* entry on this socket for this group.
* MCAST_EXCLUDE is the default
* behaviour. It means default accept;
* entries, if present, denote sources
* to be excluded from delivery.
*/
ims = imo_match_source(imo, idx,
(struct sockaddr *)&udp_in);
mode = imo->imo_mfilters[idx].imf_fmode;
if ((ims != NULL &&
mode == MCAST_EXCLUDE) ||
(ims == NULL &&
mode == MCAST_INCLUDE)) {
#ifdef DIAGNOSTIC
if (bootverbose) {
printf("%s: blocked by"
" source filter\n",
__func__);
}
#endif
blocked = imo_multi_filter(imo, ifp,
(struct sockaddr *)&group,
(struct sockaddr *)&udp_in);
if (blocked != MCAST_PASS) {
if (blocked == MCAST_NOTGMEMBER)
V_ipstat.ips_notmember++;
if (blocked == MCAST_NOTSMEMBER ||
blocked == MCAST_MUTED)
V_udpstat.udps_filtermcast++;
blocked++;
}
}
if (blocked != 0) {
INP_RUNLOCK(inp);
continue;
}

View File

@ -54,7 +54,7 @@ struct vnet_inet {
struct in_ifaddrhashhead *_in_ifaddrhashtbl;
struct in_ifaddrhead _in_ifaddrhead;
u_long _in_ifaddrhmask;
struct in_multihead _in_multihead;
struct in_multihead _in_multihead; /* XXX unused */
int _arpt_keep;
int _arp_maxtries;
@ -157,9 +157,21 @@ struct vnet_inet {
struct icmpstat _icmpstat;
struct ipstat _ipstat;
struct igmpstat _igmpstat;
SLIST_HEAD(, router_info) _router_info_head;
LIST_HEAD(, igmp_ifinfo) _igi_head;
struct igmpstat _igmpstat;
int _interface_timers_running;
int _state_change_timers_running;
int _current_state_timers_running;
int _igmp_recvifkludge;
int _igmp_sendra;
int _igmp_sendlocal;
int _igmp_v1enable;
int _igmp_v2enable;
int _igmp_legacysupp;
int _igmp_sgalloc;
int _igmp_default_version;
struct timeval _igmp_gsrdelay;
int _rtq_timeout;
int _rtq_reallyold;
@ -231,7 +243,23 @@ extern struct vnet_inet vnet_inet_0;
#define V_icmpmaskfake VNET_INET(icmpmaskfake)
#define V_icmpmaskrepl VNET_INET(icmpmaskrepl)
#define V_icmpstat VNET_INET(icmpstat)
#define V_igi_head VNET_INET(igi_head)
#define V_igmpstat VNET_INET(igmpstat)
#define V_interface_timers_running \
VNET_INET(interface_timers_running)
#define V_state_change_timers_running \
VNET_INET(state_change_timers_running)
#define V_current_state_timers_running \
VNET_INET(current_state_timers_running)
#define V_igmp_recvifkludge VNET_INET(igmp_recvifkludge)
#define V_igmp_sendra VNET_INET(igmp_sendra)
#define V_igmp_sendlocal VNET_INET(igmp_sendlocal)
#define V_igmp_v1enable VNET_INET(igmp_v1enable)
#define V_igmp_v2enable VNET_INET(igmp_v2enable)
#define V_igmp_legacysupp VNET_INET(igmp_legacysupp)
#define V_igmp_sgalloc VNET_INET(igmp_sgalloc)
#define V_igmp_default_version VNET_INET(igmp_default_version)
#define V_igmp_gsrdelay VNET_INET(igmp_gsrdelay)
#define V_in_ifaddrhashtbl VNET_INET(in_ifaddrhashtbl)
#define V_in_ifaddrhead VNET_INET(in_ifaddrhead)
#define V_in_ifaddrhmask VNET_INET(in_ifaddrhmask)

View File

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

View File

@ -120,49 +120,49 @@ void vnet_mod_register(const struct vnet_modinfo *);
#ifdef __amd64__
#define SIZEOF_vnet_net 464
#define SIZEOF_vnet_net_LINT 5144
#define SIZEOF_vnet_inet 4160
#define SIZEOF_vnet_inet 4352
#define SIZEOF_vnet_inet6 8800
#define SIZEOF_vnet_ipsec 31160
#endif
#ifdef __arm__
#define SIZEOF_vnet_net 236
#define SIZEOF_vnet_net_LINT 1 /* No LINT kernel yet. */
#define SIZEOF_vnet_inet 2396
#define SIZEOF_vnet_inet 2580
#define SIZEOF_vnet_inet6 8536
#define SIZEOF_vnet_ipsec 1
#endif
#ifdef __i386__ /* incl. pc98 */
#define SIZEOF_vnet_net 236
#define SIZEOF_vnet_net_LINT 2576
#define SIZEOF_vnet_inet 2396
#define SIZEOF_vnet_inet 2576
#define SIZEOF_vnet_inet6 8528
#define SIZEOF_vnet_ipsec 31016
#endif
#ifdef __ia64__
#define SIZEOF_vnet_net 464
#define SIZEOF_vnet_net_LINT 5144
#define SIZEOF_vnet_inet 4160
#define SIZEOF_vnet_inet 4352
#define SIZEOF_vnet_inet6 8800
#define SIZEOF_vnet_ipsec 31160
#endif
#ifdef __mips__
#define SIZEOF_vnet_net 236
#define SIZEOF_vnet_net_LINT 1 /* No LINT kernel yet. */
#define SIZEOF_vnet_inet 2432
#define SIZEOF_vnet_inet 2624
#define SIZEOF_vnet_inet6 8552
#define SIZEOF_vnet_ipsec 1
#endif
#ifdef __powerpc__
#define SIZEOF_vnet_net 236
#define SIZEOF_vnet_net_LINT 2576
#define SIZEOF_vnet_inet 2432
#define SIZEOF_vnet_inet 2616
#define SIZEOF_vnet_inet6 8536
#define SIZEOF_vnet_ipsec 31048
#endif
#ifdef __sparc64__ /* incl. sun4v */
#define SIZEOF_vnet_net 464
#define SIZEOF_vnet_net_LINT 5144
#define SIZEOF_vnet_inet 4160
#define SIZEOF_vnet_inet 4352
#define SIZEOF_vnet_inet6 8800
#define SIZEOF_vnet_ipsec 31160
#endif

View File

@ -997,32 +997,30 @@ icmp_stats(u_long off, const char *name, int af1 __unused, int proto __unused)
}
}
#ifndef BURN_BRIDGES
/*
* Dump IGMP statistics structure.
* Dump IGMP statistics structure (pre 8.x kernel).
*/
void
igmp_stats(u_long off, const char *name, int af1 __unused, int proto __unused)
static void
igmp_stats_live_old(u_long off, const char *name)
{
struct igmpstat igmpstat, zerostat;
size_t len = sizeof igmpstat;
struct oigmpstat oigmpstat, zerostat;
size_t len = sizeof(oigmpstat);
if (live) {
if (zflag)
memset(&zerostat, 0, len);
if (sysctlbyname("net.inet.igmp.stats", &igmpstat, &len,
zflag ? &zerostat : NULL, zflag ? len : 0) < 0) {
warn("sysctl: net.inet.igmp.stats");
return;
}
} else
kread(off, &igmpstat, len);
if (zflag)
memset(&zerostat, 0, len);
if (sysctlbyname("net.inet.igmp.stats", &oigmpstat, &len,
zflag ? &zerostat : NULL, zflag ? len : 0) < 0) {
warn("sysctl: net.inet.igmp.stats");
return;
}
printf("%s:\n", name);
#define p(f, m) if (igmpstat.f || sflag <= 1) \
printf(m, igmpstat.f, plural(igmpstat.f))
#define py(f, m) if (igmpstat.f || sflag <= 1) \
printf(m, igmpstat.f, igmpstat.f != 1 ? "ies" : "y")
#define p(f, m) if (oigmpstat.f || sflag <= 1) \
printf(m, oigmpstat.f, plural(oigmpstat.f))
#define py(f, m) if (oigmpstat.f || sflag <= 1) \
printf(m, oigmpstat.f, oigmpstat.f != 1 ? "ies" : "y")
p(igps_rcv_total, "\t%u message%s received\n");
p(igps_rcv_tooshort, "\t%u message%s received with too few bytes\n");
p(igps_rcv_badsum, "\t%u message%s received with bad checksum\n");
@ -1038,6 +1036,89 @@ igmp_stats(u_long off, const char *name, int af1 __unused, int proto __unused)
#undef p
#undef py
}
#endif /* !BURN_BRIDGES */
/*
* Dump IGMP statistics structure.
*/
void
igmp_stats(u_long off, const char *name, int af1 __unused, int proto __unused)
{
struct igmpstat igmpstat, zerostat;
size_t len;
#ifndef BURN_BRIDGES
if (live) {
/*
* Detect if we are being run against a pre-IGMPv3 kernel.
* We cannot do this for a core file as the legacy
* struct igmpstat has no size field, nor does it
* export it in any readily-available symbols.
*/
len = 0;
if (sysctlbyname("net.inet.igmp.stats", NULL, &len, NULL,
0) < 0) {
warn("sysctl: net.inet.igmp.stats");
return;
}
if (len < sizeof(igmpstat)) {
igmp_stats_live_old(off, name);
return;
}
}
#endif /* !BURN_BRIDGES */
len = sizeof(igmpstat);
if (live) {
if (zflag)
memset(&zerostat, 0, len);
if (sysctlbyname("net.inet.igmp.stats", &igmpstat, &len,
zflag ? &zerostat : NULL, zflag ? len : 0) < 0) {
warn("sysctl: net.inet.igmp.stats");
return;
}
} else {
len = sizeof(igmpstat);
kread(off, &igmpstat, len);
}
if (igmpstat.igps_version != IGPS_VERSION_3) {
warnx("%s: version mismatch (%d != %d)", __func__,
igmpstat.igps_version, IGPS_VERSION_3);
}
if (igmpstat.igps_len != IGPS_VERSION3_LEN) {
warnx("%s: size mismatch (%d != %d)", __func__,
igmpstat.igps_len, IGPS_VERSION3_LEN);
}
printf("%s:\n", name);
#define p64(f, m) if (igmpstat.f || sflag <= 1) \
printf(m, (uintmax_t) igmpstat.f, plural(igmpstat.f))
#define py64(f, m) if (igmpstat.f || sflag <= 1) \
printf(m, (uintmax_t) igmpstat.f, pluralies(igmpstat.f))
p64(igps_rcv_total, "\t%ju message%s received\n");
p64(igps_rcv_tooshort, "\t%ju message%s received with too few bytes\n");
p64(igps_rcv_badttl, "\t%ju message%s received with wrong TTL\n");
p64(igps_rcv_badsum, "\t%ju message%s received with bad checksum\n");
py64(igps_rcv_v1v2_queries, "\t%ju V1/V2 membership quer%s received\n");
py64(igps_rcv_v3_queries, "\t%ju V3 membership quer%s received\n");
py64(igps_rcv_badqueries,
"\t%ju membership quer%s received with invalid field(s)\n");
py64(igps_rcv_gen_queries, "\t%ju general quer%s received\n");
py64(igps_rcv_group_queries, "\t%ju group quer%s received\n");
py64(igps_rcv_gsr_queries, "\t%ju group-source quer%s received\n");
py64(igps_drop_gsr_queries, "\t%ju group-source quer%s dropped\n");
p64(igps_rcv_reports, "\t%ju membership report%s received\n");
p64(igps_rcv_badreports,
"\t%ju membership report%s received with invalid field(s)\n");
p64(igps_rcv_ourreports,
"\t%ju membership report%s received for groups to which we belong\n");
p64(igps_rcv_nora, "\t%ju V3 report%s received without Router Alert\n");
p64(igps_snd_reports, "\t%ju membership report%s sent\n");
#undef p64
#undef py64
}
/*
* Dump PIM statistics structure.

View File

@ -4,8 +4,10 @@
.include <bsd.own.mk>
PROG= ifmcstat
SRCS= ifmcstat.c printb.c
MAN= ifmcstat.8
BINMODE= 550
BINMODE= 555
WARNS?= 2

View File

@ -30,7 +30,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd February 15, 2009
.Dd February 28, 2009
.Dt IFMCSTAT 8
.Os
.Sh NAME
@ -41,6 +41,7 @@
.Op Fl i Ar interface
.Op Fl f Ar address-family
.Op Fl v
.Op Fl K
.Op Fl M Ar core
.Op Fl N Ar system
.\"
@ -66,6 +67,13 @@ specifies that link-layer memberships should be printed;
they are suppressed by default.
It may not be specified for
.Fl f Ar link .
Source lists for each group will also be printed.
.Pp
If specified twice, and
.Xr kvm 3
is in use, the IGMP timers for each interface
and the IGMP source list counters for each group
will also be printed.
.El
.Pp
The following options are only available if
@ -73,6 +81,10 @@ The following options are only available if
has been built with support for
.Xr kvm 3 :
.Bl -tag -width Fl
.It Fl K
forces the use of
.Xr kvm 3
to be disabled.
.It Fl M Ar core
extracts values associated with the name list from the specified core,
instead of the default
@ -106,10 +118,14 @@ support, the information displayed by
is more limited.
This support is recommended for debugging purposes.
It requires super-user privilege if used to inspect a running kernel.
.Pp
The
.Xr kvm 3
will be used by default if
back-end will be used by default if
.Nm
is run with super-user privileges.
is run with super-user privileges, unless the
.Fl K
option is specified.
.Sh SEE ALSO
.Xr getifaddrs 3 ,
.Xr getifmaddrs 3 ,

View File

@ -35,8 +35,10 @@ __FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <sys/tree.h>
#include <net/if.h>
#include <net/if_var.h>
@ -49,15 +51,13 @@ __FBSDID("$FreeBSD$");
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/igmp.h>
#ifdef HAVE_IGMPV3
# include <netinet/in_msf.h>
#endif
#define KERNEL
# include <netinet/if_ether.h>
#undef KERNEL
#define _KERNEL
# include <sys/sysctl.h>
#define SYSCTL_DECL(x)
# include <netinet/igmp_var.h>
#undef SYSCTL_DECL
#undef _KERNEL
#ifdef INET6
@ -80,6 +80,7 @@ __FBSDID("$FreeBSD$");
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <kvm.h>
#include <limits.h>
@ -93,6 +94,8 @@ __FBSDID("$FreeBSD$");
#define INET
#endif
extern void printb(const char *, unsigned int, const char *);
union sockunion {
struct sockaddr_storage ss;
struct sockaddr sa;
@ -108,6 +111,9 @@ typedef union sockunion sockunion_t;
uint32_t ifindex = 0;
int af = AF_UNSPEC;
#ifdef WITH_KVM
int Kflag = 0;
#endif
int vflag = 0;
#define sa_equal(a1, a2) \
@ -130,9 +136,6 @@ int vflag = 0;
static void if_addrlist(struct ifaddr *);
static struct in_multi *
in_multientry(struct in_multi *);
#ifdef HAVE_IGMPV3
static void in_addr_slistentry(struct in_addr_slist *, char *);
#endif
#endif /* INET */
#ifdef INET6
@ -159,6 +162,10 @@ struct nlist nl[] = {
#endif /* WITH_KVM */
static int ifmcstat_getifmaddrs(void);
#ifdef INET
static void in_ifinfo(struct igmp_ifinfo *);
static const char * inm_mode(u_int mode);
#endif
#ifdef INET6
static const char * inet6_n2a(struct in6_addr *);
#endif
@ -172,12 +179,18 @@ usage()
"usage: ifmcstat [-i interface] [-f address family]"
" [-v]"
#ifdef WITH_KVM
" [-M core] [-N system]"
" [-K] [-M core] [-N system]"
#endif
"\n");
exit(EX_USAGE);
}
static const char *options = "i:f:vM:N:"
#ifdef WITH_KVM
"K"
#endif
;
int
main(int argc, char **argv)
{
@ -187,7 +200,7 @@ main(int argc, char **argv)
const char *core = NULL;
#endif
while ((c = getopt(argc, argv, "i:f:vM:N:")) != -1) {
while ((c = getopt(argc, argv, options)) != -1) {
switch (c) {
case 'i':
if ((ifindex = if_nametoindex(optarg)) == 0) {
@ -219,8 +232,14 @@ main(int argc, char **argv)
/*NOTREACHED*/
break;
#ifdef WITH_KVM
case 'K':
++Kflag;
break;
#endif
case 'v':
vflag = 1;
++vflag;
break;
#ifdef WITH_KVM
@ -244,12 +263,13 @@ main(int argc, char **argv)
usage();
#ifdef WITH_KVM
error = ifmcstat_kvm(kernel, core);
if (!Kflag)
error = ifmcstat_kvm(kernel, core);
/*
* If KVM failed, and user did not explicitly specify a core file,
* try the sysctl backend.
* or force KVM backend to be disabled, try the sysctl backend.
*/
if (error != 0 && (core == NULL && kernel == NULL))
if (Kflag || (error != 0 && (core == NULL && kernel == NULL)))
#endif
error = ifmcstat_getifmaddrs();
if (error != 0)
@ -259,6 +279,52 @@ main(int argc, char **argv)
/*NOTREACHED*/
}
#ifdef INET
static void
in_ifinfo(struct igmp_ifinfo *igi)
{
printf("\t");
switch (igi->igi_version) {
case IGMP_VERSION_1:
case IGMP_VERSION_2:
case IGMP_VERSION_3:
printf("igmpv%d", igi->igi_version);
break;
default:
printf("igmpv?(%d)", igi->igi_version);
break;
}
printb(" flags", igi->igi_flags, "\020\1SILENT\2LOOPBACK");
if (igi->igi_version == IGMP_VERSION_3) {
printf(" rv %u qi %u qri %u uri %u",
igi->igi_rv, igi->igi_qi, igi->igi_qri, igi->igi_uri);
}
if (vflag >= 2) {
printf(" v1timer %u v2timer %u v3timer %u",
igi->igi_v1_timer, igi->igi_v2_timer, igi->igi_v3_timer);
}
printf("\n");
}
static const char *inm_modes[] = {
"undefined",
"include",
"exclude",
};
static const char *
inm_mode(u_int mode)
{
if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE)
return (inm_modes[mode]);
return (NULL);
}
#endif /* INET */
#ifdef WITH_KVM
static int
@ -447,6 +513,7 @@ static void
if_addrlist(struct ifaddr *ifap)
{
struct ifaddr ifa;
struct ifnet ifnet;
struct sockaddr sa;
struct in_ifaddr ia;
struct ifaddr *ifap0;
@ -463,11 +530,24 @@ if_addrlist(struct ifaddr *ifap)
goto nextifap;
KREAD(ifap, &ia, struct in_ifaddr);
printf("\tinet %s\n", inet_ntoa(ia.ia_addr.sin_addr));
/*
* Print per-link IGMP information, if available.
*/
if (ifa.ifa_ifp != NULL) {
struct in_ifinfo ii;
struct igmp_ifinfo igi;
KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
KREAD(ifnet.if_afdata[AF_INET], &ii, struct in_ifinfo);
if (ii.ii_igmp != NULL) {
KREAD(ii.ii_igmp, &igi, struct igmp_ifinfo);
in_ifinfo(&igi);
}
}
nextifap:
ifap = ifa.ifa_link.tqe_next;
}
if (ifap0) {
struct ifnet ifnet;
struct ifmultiaddr ifm, *ifmp = 0;
struct sockaddr_dl sdl;
@ -496,96 +576,145 @@ if_addrlist(struct ifaddr *ifap)
}
}
static struct in_multi *
in_multientry(struct in_multi *mc)
static const char *inm_states[] = {
"not-member",
"silent",
"idle",
"lazy",
"sleeping",
"awakening",
"query-pending",
"sg-query-pending",
"leaving"
};
static const char *
inm_state(u_int state)
{
struct in_multi multi;
struct router_info rti;
#ifdef HAVE_IGMPV3
struct in_multi_source src;
#endif
KREAD(mc, &multi, struct in_multi);
printf("\t\tgroup %s\n", inet_ntoa(multi.inm_addr));
if (multi.inm_rti != NULL) {
KREAD(multi.inm_rti, &rti, struct router_info);
printf("\t\t\t");
switch (rti.rti_type) {
case IGMP_V1_ROUTER:
printf("igmpv1");
break;
case IGMP_V2_ROUTER:
printf("igmpv2");
break;
#ifdef HAVE_IGMPV3
case IGMP_V3_ROUTER:
printf("igmpv3");
break;
#endif
default:
printf("igmpv?(%d)", rti.rti_type);
break;
}
#ifdef HAVE_IGMPV3
if (multi.inm_source == NULL) {
printf("\n");
return (multi.inm_list.le_next);
}
KREAD(multi.inm_source, &src, struct in_multi_source);
printf(" mode=%s grpjoin=%d\n",
src.ims_mode == MCAST_INCLUDE ? "include" :
src.ims_mode == MCAST_EXCLUDE ? "exclude" :
"???",
src.ims_grpjoin);
in_addr_slistentry(src.ims_cur, "current");
in_addr_slistentry(src.ims_rec, "recorded");
in_addr_slistentry(src.ims_in, "included");
in_addr_slistentry(src.ims_ex, "excluded");
in_addr_slistentry(src.ims_alw, "allowed");
in_addr_slistentry(src.ims_blk, "blocked");
in_addr_slistentry(src.ims_toin, "to-include");
in_addr_slistentry(src.ims_ex, "to-exclude");
#else
printf("\n");
#endif
}
if (state >= IGMP_NOT_MEMBER && state <= IGMP_LEAVING_MEMBER)
return (inm_states[state]);
return (NULL);
}
#ifdef HAVE_IGMPV3
static void
in_addr_slistentry(struct in_addr_slist *ias, char *heading)
#if 0
static struct ip_msource *
ims_min_kvm(struct in_multi *pinm)
{
struct in_addr_slist slist;
struct ias_head head;
struct in_addr_source src;
struct ip_msource ims0;
struct ip_msource *tmp, *parent;
if (ias == NULL) {
printf("\t\t\t\t%s (none)\n", heading);
return;
parent = NULL;
tmp = RB_ROOT(&pinm->inm_srcs);
while (tmp) {
parent = tmp;
KREAD(tmp, &ims0, struct ip_msource);
tmp = RB_LEFT(&ims0, ims_link);
}
memset(&slist, 0, sizeof(slist));
KREAD(ias, &slist, struct in_addr_source);
printf("\t\t\t\t%s (entry num=%d)\n", heading, slist.numsrc);
if (slist.numsrc == 0) {
return;
}
KREAD(slist.head, &head, struct ias_head);
return (parent); /* kva */
}
KREAD(head.lh_first, &src, struct in_addr_source);
while (1) {
printf("\t\t\t\t\tsource %s (ref=%d)\n",
inet_ntoa(src.ias_addr.sin_addr), src.ias_refcount);
if (src.ias_list.le_next == NULL)
break;
KREAD(src.ias_list.le_next, &src, struct in_addr_source);
/* XXX This routine is buggy. See RB_NEXT in sys/tree.h. */
static struct ip_msource *
ims_next_kvm(struct ip_msource *ims)
{
struct ip_msource ims0, ims1;
struct ip_msource *tmp;
KREAD(ims, &ims0, struct ip_msource);
if (RB_RIGHT(&ims0, ims_link)) {
ims = RB_RIGHT(&ims0, ims_link);
KREAD(ims, &ims1, struct ip_msource);
while ((tmp = RB_LEFT(&ims1, ims_link))) {
KREAD(tmp, &ims0, struct ip_msource);
ims = RB_LEFT(&ims0, ims_link);
}
} else {
tmp = RB_PARENT(&ims0, ims_link);
if (tmp) {
KREAD(tmp, &ims1, struct ip_msource);
if (ims == RB_LEFT(&ims1, ims_link))
ims = tmp;
} else {
while ((tmp = RB_PARENT(&ims0, ims_link))) {
KREAD(tmp, &ims1, struct ip_msource);
if (ims == RB_RIGHT(&ims1, ims_link)) {
ims = tmp;
KREAD(ims, &ims0, struct ip_msource);
} else
break;
}
ims = RB_PARENT(&ims0, ims_link);
}
}
return (ims); /* kva */
}
static void
inm_print_sources_kvm(struct in_multi *pinm)
{
struct ip_msource ims0;
struct ip_msource *ims;
struct in_addr src;
int cnt;
uint8_t fmode;
cnt = 0;
fmode = pinm->inm_st[1].iss_fmode;
if (fmode == MCAST_UNDEFINED)
return;
for (ims = ims_min_kvm(pinm); ims != NULL; ims = ims_next_kvm(ims)) {
if (cnt == 0)
printf(" srcs ");
KREAD(ims, &ims0, struct ip_msource);
/* Only print sources in-mode at t1. */
if (fmode != ims_get_mode(pinm, ims, 1))
continue;
src.s_addr = htonl(ims0.ims_haddr);
printf("%s%s", (cnt++ == 0 ? "" : ","), inet_ntoa(src));
}
}
#endif /* HAVE_IGMPV3 */
#endif
static struct in_multi *
in_multientry(struct in_multi *pinm)
{
struct in_multi inm;
const char *state, *mode;
KREAD(pinm, &inm, struct in_multi);
printf("\t\tgroup %s", inet_ntoa(inm.inm_addr));
printf(" refcnt %u", inm.inm_refcount);
state = inm_state(inm.inm_state);
if (state)
printf(" state %s", state);
else
printf(" state (%d)", inm.inm_state);
mode = inm_mode(inm.inm_st[1].iss_fmode);
if (mode)
printf(" mode %s", mode);
else
printf(" mode (%d)", inm.inm_st[1].iss_fmode);
if (vflag >= 2) {
printf(" asm %u ex %u in %u rec %u",
(u_int)inm.inm_st[1].iss_asm,
(u_int)inm.inm_st[1].iss_ex,
(u_int)inm.inm_st[1].iss_in,
(u_int)inm.inm_st[1].iss_rec);
}
#if 0
/* Buggy. */
if (vflag)
inm_print_sources_kvm(&inm);
#endif
printf("\n");
return (NULL);
}
#endif /* INET */
@ -622,6 +751,97 @@ inet6_n2a(struct in6_addr *p)
}
#endif /* INET6 */
#ifdef INET
/*
* Retrieve per-group source filter mode and lists via sysctl.
*/
static void
inm_print_sources_sysctl(uint32_t ifindex, struct in_addr gina)
{
#define MAX_SYSCTL_TRY 5
int mib[7];
int ntry = 0;
size_t mibsize;
size_t len;
size_t needed;
size_t cnt;
int i;
char *buf;
struct in_addr *pina;
uint32_t *p;
uint32_t fmode;
const char *modestr;
mibsize = sizeof(mib) / sizeof(mib[0]);
if (sysctlnametomib("net.inet.ip.mcast.filters", mib, &mibsize) == -1) {
perror("sysctlnametomib");
return;
}
needed = 0;
mib[5] = ifindex;
mib[6] = gina.s_addr; /* 32 bits wide */
mibsize = sizeof(mib) / sizeof(mib[0]);
do {
if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) {
perror("sysctl net.inet.ip.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 in_addr);
pina = (struct in_addr *)p;
for (i = 0; i < cnt; i++) {
if (i == 0)
printf(" srcs ");
fprintf(stdout, "%s%s", (i == 0 ? "" : ","),
inet_ntoa(*pina++));
len -= sizeof(struct in_addr);
}
if (len > 0) {
fprintf(stderr, "warning: %u trailing bytes from %s\n",
(unsigned int)len, "net.inet.ip.mcast.filters");
}
out_free:
free(buf);
#undef MAX_SYSCTL_TRY
}
#endif /* INET */
static int
ifmcstat_getifmaddrs(void)
{
@ -771,6 +991,32 @@ ifmcstat_getifmaddrs(void)
}
fprintf(stdout, "\t%s %s\n", pafname, addrbuf);
#ifdef INET
/*
* Print per-link IGMP information, if available.
*/
if (pifasa->sa.sa_family == AF_INET) {
struct igmp_ifinfo igi;
size_t mibsize, len;
int mib[5];
mibsize = sizeof(mib) / sizeof(mib[0]);
if (sysctlnametomib("net.inet.igmp.ifinfo",
mib, &mibsize) == -1) {
perror("sysctlnametomib");
goto next_ifnet;
}
mib[mibsize] = thisifindex;
len = sizeof(struct igmp_ifinfo);
if (sysctl(mib, mibsize + 1, &igi, &len, NULL,
0) == -1) {
perror("sysctl net.inet.igmp.ifinfo");
goto next_ifnet;
}
in_ifinfo(&igi);
}
next_ifnet:
#endif
lastifasa = *pifasa;
}
@ -788,7 +1034,14 @@ ifmcstat_getifmaddrs(void)
perror("getnameinfo");
}
fprintf(stdout, "\t\tgroup %s\n", addrbuf);
fprintf(stdout, "\t\tgroup %s", addrbuf);
#ifdef INET
if (pgsa->sa.sa_family == AF_INET) {
inm_print_sources_sysctl(thisifindex,
pgsa->sin.sin_addr);
}
#endif
fprintf(stdout, "\n");
/* Link-layer mapping, if present. */
pllsa = (sockunion_t *)ifma->ifma_lladdr;