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:
parent
095b4d2689
commit
d10910e6ce
39
UPDATING
39
UPDATING
@ -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
|
||||
|
@ -128,6 +128,7 @@ MAN= aac.4 \
|
||||
if_bridge.4 \
|
||||
ifmib.4 \
|
||||
igb.4 \
|
||||
igmp.4 \
|
||||
iic.4 \
|
||||
iicbb.4 \
|
||||
iicbus.4 \
|
||||
|
@ -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 ,
|
||||
|
@ -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 ,
|
||||
|
@ -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, "");
|
||||
|
3813
sys/netinet/igmp.c
3813
sys/netinet/igmp.c
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
114
sys/netinet/in.c
114
sys/netinet/in.c
@ -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);
|
||||
}
|
||||
|
@ -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
@ -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
|
||||
|
@ -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);
|
||||
|
@ -592,7 +592,6 @@ passin:
|
||||
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 @@ passin:
|
||||
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)
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -4,8 +4,10 @@
|
||||
.include <bsd.own.mk>
|
||||
|
||||
PROG= ifmcstat
|
||||
SRCS= ifmcstat.c printb.c
|
||||
|
||||
MAN= ifmcstat.8
|
||||
BINMODE= 550
|
||||
BINMODE= 555
|
||||
|
||||
WARNS?= 2
|
||||
|
||||
|
@ -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 ,
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user