Import rewrite of IPv4 socket multicast layer to support source-specific

and protocol-independent host mode multicast. The code is written to
accomodate IPv6, IGMPv3 and MLDv2 with only a little additional work.

This change only pertains to FreeBSD's use as a multicast end-station and
does not concern multicast routing; for an IGMPv3/MLDv2 router
implementation, consider the XORP project.

The work is based on Wilbert de Graaf's IGMPv3 code drop for FreeBSD 4.6,
which is available at: http://www.kloosterhof.com/wilbert/igmpv3.html

Summary
 * IPv4 multicast socket processing is now moved out of ip_output.c
   into a new module, in_mcast.c.
 * The in_mcast.c module implements the IPv4 legacy any-source API in
   terms of the protocol-independent source-specific API.
 * Source filters are lazy allocated as the common case does not use them.
   They are part of per inpcb state and are covered by the inpcb lock.
 * struct ip_mreqn is now supported to allow applications to specify
   multicast joins by interface index in the legacy IPv4 any-source API.
 * In UDP, an incoming multicast datagram only requires that the source
   port matches the 4-tuple if the socket was already bound by source port.
   An unbound socket SHOULD be able to receive multicasts sent from an
   ephemeral source port.
 * The UDP socket multicast filter mode defaults to exclusive, that is,
   sources present in the per-socket list will be blocked from delivery.
 * The RFC 3678 userland functions have been added to libc: setsourcefilter,
   getsourcefilter, setipv4sourcefilter, getipv4sourcefilter.
 * Definitions for IGMPv3 are merged but not yet used.
 * struct sockaddr_storage is now referenced from <netinet/in.h>. It
   is therefore defined there if not already declared in the same way
   as for the C99 types.
 * The RFC 1724 hack (specify 0.0.0.0/8 addresses to IP_MULTICAST_IF
   which are then interpreted as interface indexes) is now deprecated.
 * A patch for the Rhyolite.com routed in the FreeBSD base system
   is available in the -net archives. This only affects individuals
   running RIPv1 or RIPv2 via point-to-point and/or unnumbered interfaces.
 * Make IPv6 detach path similar to IPv4's in code flow; functionally same.
 * Bump __FreeBSD_version to 700048; see UPDATING.

This work was financially supported by another FreeBSD committer.

Obtained from:  p4://bms_netdev
Submitted by:   Wilbert de Graaf (original work)
Reviewed by:    rwatson (locking), silence from fenner,
		net@ (but with encouragement)
This commit is contained in:
Bruce M Simpson 2007-06-12 16:24:56 +00:00
parent 645016c0e4
commit 71498f308b
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=170613
26 changed files with 2310 additions and 725 deletions

View File

@ -21,6 +21,25 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 7.x IS SLOW:
developers choose to disable these features on build machines
to maximize performance.
20070612:
The IPv4 multicast socket code has been considerably modified, and
moved to the file sys/netinet/in_mcast.c. Initial support for the
RFC 3678 Source-Specific Multicast Socket API has been added to
the IPv4 network stack.
Strict multicast and broadcast reception is now the default for
UDP/IPv4 sockets; the net.inet.udp.strict_mcast_mship sysctl variable
has now been removed.
The RFC 1724 hack for interface selection has been removed; the use
of the Linux-derived ip_mreqn structure with IP_MULTICAST_IF has
been added to replace it. Consumers such as routed will soon be
updated to reflect this.
These changes affect users who are running routed(8) or rdisc(8)
from the FreeBSD base system on point-to-point or unnumbered
interfaces.
20070610:
The net80211 layer has changed significantly and all wireless
drivers that depend on it need to be recompiled. Further these

View File

@ -14,7 +14,7 @@ SRCS+= base64.c ether_addr.c eui64.c \
ip6opt.c linkaddr.c map_v4v6.c name6.c ntoh.c \
nsdispatch.c nslexer.c nsparser.c nss_compat.c \
rcmd.c rcmdsh.c recv.c rthdr.c sctp_sys_calls.c send.c \
sockatmark.c vars.c
sockatmark.c sourcefilter.c vars.c
.if ${MK_NS_CACHING} != "no"
SRCS+= nscache.c nscachedcli.c
@ -52,6 +52,7 @@ MAN+= byteorder.3 ethers.3 eui64.3 \
inet6_opt_init.3 inet6_option_space.3 inet6_rth_space.3 \
inet6_rthdr_space.3 linkaddr.3 \
nsdispatch.3 rcmd.3 rcmdsh.3 resolver.3 sockatmark.3 \
setsourcefilter.3 \
sctp_bindx.3 sctp_connectx.3 sctp_freepaddrs.3 \
sctp_getaddrlen.3 sctp_getassocid.3 sctp_getpaddrs.3 \
sctp_opt_info.3 sctp_recvmsg.3 sctp_send.3 sctp_sendmsg.3 \
@ -121,6 +122,8 @@ MLINKS+=resolver.3 dn_comp.3 resolver.3 dn_expand.3 resolver.3 res_init.3 \
resolver.3 res_search.3 resolver.3 res_send.3 resolver.3 dn_skipname.3 \
resolver.3 ns_get16.3 resolver.3 ns_get32.3 \
resolver.3 ns_put16.3 resolver.3 ns_put32.3
MLINKS+=sourcefilter.3 setipv4sourcefilter.3 getipv4sourcefilter.3 \
sourcefilter.3 setsourcefilter.3 getsourcefilter.3
.if ${MK_HESIOD} != "no"
SRCS+= hesiod.c

View File

@ -137,6 +137,10 @@ FBSD_1.0 {
sctp_send;
sctp_sendx;
sctp_recvmsg;
setipv4sourcefilter;
getipv4sourcefilter;
getsourcefilter;
setsourcefilter;
};
FBSDprivate_1.0 {

View File

@ -32,7 +32,7 @@
.\" @(#)ip.4 8.2 (Berkeley) 11/30/93
.\" $FreeBSD$
.\"
.Dd March 18, 2007
.Dd April 9, 2007
.Dt IP 4
.Os
.Sh NAME
@ -420,6 +420,16 @@ where "addr" is the local
address of the desired interface or
.Dv INADDR_ANY
to specify the default interface.
.Pp
To specify an interface by index, an instance of
.Vt ip_mreqn
should be passed instead.
The
.Vt imr_ifindex
member should be set to the index of the desired interface,
or 0 to specify the default interface.
The kernel differentiates between these two structures by their size.
.\"
An interface's local IP address and multicast capability can
be obtained via the
.Dv SIOCGIFCONF
@ -672,3 +682,7 @@ The
.Nm
protocol appeared in
.Bx 4.2 .
The
.Vt ip_mreqn
structure appeared in
.Tn Linux 2.4 .

View File

@ -1801,6 +1801,7 @@ netinet/ip_carp.c optional carp
netinet/in_gif.c optional gif inet
netinet/ip_gre.c optional gre inet
netinet/ip_id.c optional inet
netinet/in_mcast.c optional inet
netinet/in_pcb.c optional inet
netinet/in_proto.c optional inet \
compile-with "${NORMAL_C} -I$S/contrib/pf"

View File

@ -55,7 +55,42 @@ struct igmp {
struct in_addr igmp_group; /* group address being reported */
}; /* (zero for queries) */
#define IGMP_MINLEN 8
struct igmpv3 {
u_char igmp_type; /* version & type of IGMP message */
u_char igmp_code; /* subtype for routing msgs */
u_short igmp_cksum; /* IP-style checksum */
struct in_addr igmp_group; /* group address being reported */
/* (zero for queries) */
u_char igmp_misc; /* reserved/suppress/robustness */
u_char igmp_qqi; /* querier's query interval */
u_short igmp_numsrc; /* number of sources */
/*struct in_addr igmp_sources[1];*/ /* source addresses */
};
struct igmp_grouprec {
u_char ig_type; /* record type */
u_char ig_datalen; /* length of auxiliary data */
u_short ig_numsrc; /* number of sources */
struct in_addr ig_group; /* group address being reported */
/*struct in_addr ig_sources[1];*/ /* source addresses */
};
struct igmp_report {
u_char ir_type; /* record type */
u_char ir_rsv1; /* reserved */
u_short ir_cksum; /* checksum */
u_short ir_rsv2; /* reserved */
u_short ir_numgrps; /* number of group records */
struct igmp_grouprec ir_groups[1]; /* group records */
};
#define IGMP_MINLEN 8
#define IGMP_HDRLEN 8
#define IGMP_GRPREC_HDRLEN 8
#define IGMP_PREPEND 0
#define IGMP_QRV(pigmp) ((pigmp)->igmp_misc & (0x07)) /* XXX */
#define IGMP_MAXSOURCES(len) (((len) - 12) >> 2) /* XXX */
/*
* Message types, including version number.
@ -71,6 +106,8 @@ struct igmp {
#define IGMP_MTRACE_RESP 0x1e /* traceroute resp.(to sender)*/
#define IGMP_MTRACE 0x1f /* mcast traceroute messages */
#define IGMP_V3_MEMBERSHIP_REPORT 0x22 /* Ver. 3 membership report */
#define IGMP_MAX_HOST_REPORT_DELAY 10 /* max delay for response to */
/* query (in seconds) according */
/* to RFC1112 */

View File

@ -56,6 +56,7 @@ struct igmpstat {
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 */
};
#ifdef _KERNEL
@ -67,6 +68,13 @@ struct igmpstat {
#define IGMP_OTHERMEMBER 0
#define IGMP_IREPORTEDLAST 1
/*
* State masks for IGMPv3
*/
#define IGMP_V3_NONEXISTENT 0x01
#define IGMP_V3_OTHERMEMBER 0x02
#define IGMP_V3_IREPORTEDLAST 0x04
/*
* We must remember what version the subnet's querier is.
* We conveniently use the IGMP message type for the proper
@ -74,6 +82,7 @@ struct igmpstat {
*/
#define IGMP_V1_ROUTER IGMP_V1_MEMBERSHIP_REPORT
#define IGMP_V2_ROUTER IGMP_V2_MEMBERSHIP_REPORT
#define IGMP_V3_ROUTER IGMP_V3_MEMBERSHIP_REPORT
/*
* Revert to new router if we haven't heard from an old router in
@ -81,6 +90,51 @@ struct igmpstat {
*/
#define IGMP_AGE_THRESHOLD 540
/*
* IGMPv3 protocol defaults
*/
#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 */
/*
* IGMPv3 report types
*/
#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 */
/*
* Report types
*/
#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_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)
/*
* 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 *);
@ -100,6 +154,6 @@ SYSCTL_DECL(_net_inet_igmp);
#define IGMPCTL_NAMES { \
{ 0, 0 }, \
{ "stats", CTLTYPE_STRUCT }, \
{ "stats", CTLTYPE_STRUCT } \
}
#endif

View File

@ -49,10 +49,7 @@
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/in_pcb.h>
#include <netinet/igmp_var.h>
static MALLOC_DEFINE(M_IPMADDR, "in_multi", "internet multicast address");
#include <netinet/ip_var.h>
static int in_mask2len(struct in_addr *);
static void in_len2mask(struct in_addr *, int);
@ -74,17 +71,6 @@ SYSCTL_INT(_net_inet_ip, OID_AUTO, same_prefix_carp_only, CTLFLAG_RW,
&sameprefixcarponly, 0,
"Refuse to create same prefixes on different interfaces");
/*
* The IPv4 multicast list (in_multihead and associated structures) are
* protected by the global in_multi_mtx. See in_var.h for more details. For
* now, in_multi_mtx is marked as recursible due to IGMP's calling back into
* ip_output() to send IGMP packets while holding the lock; this probably is
* not quite desirable.
*/
struct in_multihead in_multihead; /* XXX BSS initialization */
struct mtx in_multi_mtx;
MTX_SYSINIT(in_multi_mtx, &in_multi_mtx, "in_multi_mtx", MTX_DEF | MTX_RECURSE);
extern struct inpcbinfo ripcbinfo;
extern struct inpcbinfo udbinfo;
@ -976,155 +962,6 @@ in_broadcast(struct in_addr in, struct ifnet *ifp)
#undef ia
}
/*
* Add an address to the list of IP multicast addresses for a given interface.
*/
struct in_multi *
in_addmulti(struct in_addr *ap, struct ifnet *ifp)
{
struct in_multi *inm;
inm = NULL;
IFF_LOCKGIANT(ifp);
IN_MULTI_LOCK();
IN_LOOKUP_MULTI(*ap, ifp, inm);
if (inm != NULL) {
/*
* If we already joined this group, just bump the
* refcount and return it.
*/
KASSERT(inm->inm_refcount >= 1,
("%s: bad refcount %d", __func__, inm->inm_refcount));
++inm->inm_refcount;
} else do {
struct sockaddr_in sin;
struct ifmultiaddr *ifma;
struct in_multi *ninm;
int error;
bzero(&sin, sizeof sin);
sin.sin_family = AF_INET;
sin.sin_len = sizeof(struct sockaddr_in);
sin.sin_addr = *ap;
/*
* Check if a link-layer group is already associated
* with this network-layer group on the given ifnet.
* If so, bump the refcount on the existing network-layer
* group association and return it.
*/
error = if_addmulti(ifp, (struct sockaddr *)&sin, &ifma);
if (error)
break;
if (ifma->ifma_protospec != NULL) {
inm = (struct in_multi *)ifma->ifma_protospec;
#ifdef INVARIANTS
if (inm->inm_ifma != ifma || inm->inm_ifp != ifp ||
inm->inm_addr.s_addr != ap->s_addr)
panic("%s: ifma is inconsistent", __func__);
#endif
++inm->inm_refcount;
break;
}
/*
* A new membership is needed; construct it and
* perform the IGMP join.
*/
ninm = malloc(sizeof(*ninm), M_IPMADDR, M_NOWAIT | M_ZERO);
if (ninm == NULL) {
if_delmulti_ifma(ifma);
break;
}
ninm->inm_addr = *ap;
ninm->inm_ifp = ifp;
ninm->inm_ifma = ifma;
ninm->inm_refcount = 1;
ifma->ifma_protospec = ninm;
LIST_INSERT_HEAD(&in_multihead, ninm, inm_link);
igmp_joingroup(ninm);
inm = ninm;
} while (0);
IN_MULTI_UNLOCK();
IFF_UNLOCKGIANT(ifp);
return (inm);
}
/*
* Delete a multicast address record.
* It is OK to call this routine if the underlying ifnet went away.
*
* XXX: To deal with the ifp going away, we cheat; the link-layer code in net
* will set ifma_ifp to NULL when the associated ifnet instance is detached
* from the system.
* The only reason we need to violate layers and check ifma_ifp here at all
* is because certain hardware drivers still require Giant to be held,
* and it must always be taken before other locks.
*/
void
in_delmulti(struct in_multi *inm)
{
struct ifnet *ifp;
KASSERT(inm->inm_ifma != NULL, ("%s: no ifma", __func__));
ifp = inm->inm_ifma->ifma_ifp;
if (ifp != NULL) {
/*
* Sanity check that netinet's notion of ifp is the
* same as net's.
*/
KASSERT(inm->inm_ifp == ifp, ("%s: bad ifp", __func__));
IFF_LOCKGIANT(ifp);
}
IN_MULTI_LOCK();
in_delmulti_locked(inm);
IN_MULTI_UNLOCK();
if (ifp != NULL)
IFF_UNLOCKGIANT(ifp);
}
/*
* Delete a multicast address record, with locks held.
*
* It is OK to call this routine if the ifp went away.
* Assumes that caller holds the IN_MULTI lock, and that
* Giant was taken before other locks if required by the hardware.
*/
void
in_delmulti_locked(struct in_multi *inm)
{
struct ifmultiaddr *ifma;
IN_MULTI_LOCK_ASSERT();
KASSERT(inm->inm_refcount >= 1, ("%s: freeing freed inm", __func__));
if (--inm->inm_refcount == 0) {
igmp_leavegroup(inm);
ifma = inm->inm_ifma;
#ifdef DIAGNOSTIC
printf("%s: purging ifma %p\n", __func__, ifma);
#endif
KASSERT(ifma->ifma_protospec == inm,
("%s: ifma_protospec != inm", __func__));
ifma->ifma_protospec = NULL;
LIST_REMOVE(inm, inm_link);
free(inm, M_IPMADDR);
if_delmulti_ifma(ifma);
}
}
/*
* Delete all IPv4 multicast address records, and associated link-layer
* multicast address records, associated with ifp.

View File

@ -84,6 +84,33 @@ struct in_addr {
#define _STRUCT_IN_ADDR_DECLARED
#endif
#ifndef _SOCKLEN_T_DECLARED
typedef __socklen_t socklen_t;
#define _SOCKLEN_T_DECLARED
#endif
/* Avoid collision with original definition in sys/socket.h. */
#ifndef _STRUCT_SOCKADDR_STORAGE_DECLARED
/*
* RFC 2553: protocol-independent placeholder for socket addresses
*/
#define _SS_MAXSIZE 128U
#define _SS_ALIGNSIZE (sizeof(__int64_t))
#define _SS_PAD1SIZE (_SS_ALIGNSIZE - sizeof(unsigned char) - \
sizeof(sa_family_t))
#define _SS_PAD2SIZE (_SS_MAXSIZE - sizeof(unsigned char) - \
sizeof(sa_family_t) - _SS_PAD1SIZE - _SS_ALIGNSIZE)
struct sockaddr_storage {
unsigned char ss_len; /* address length */
sa_family_t ss_family; /* address family */
char __ss_pad1[_SS_PAD1SIZE];
__int64_t __ss_align; /* force desired struct alignment */
char __ss_pad2[_SS_PAD2SIZE];
};
#define _STRUCT_SOCKADDR_STORAGE_DECLARED
#endif
/* Socket address, internet style. */
struct sockaddr_in {
uint8_t sin_len;
@ -390,7 +417,8 @@ __END_DECLS
#define IP_RECVDSTADDR 7 /* bool; receive IP dst addr w/dgram */
#define IP_SENDSRCADDR IP_RECVDSTADDR /* cmsg_type to set src addr */
#define IP_RETOPTS 8 /* ip_opts; set/get IP options */
#define IP_MULTICAST_IF 9 /* u_char; set/get IP multicast i/f */
#define IP_MULTICAST_IF 9 /* struct in_addr *or* struct ip_mreqn;
* set/get IP multicast i/f */
#define IP_MULTICAST_TTL 10 /* u_char; set/get IP multicast ttl */
#define IP_MULTICAST_LOOP 11 /* u_char; set/get IP multicast loopback */
#define IP_ADD_MEMBERSHIP 12 /* ip_mreq; add an IP group membership */
@ -435,6 +463,23 @@ __END_DECLS
#define IP_MINTTL 66 /* minimum TTL for packet or drop */
#define IP_DONTFRAG 67 /* don't fragment packet */
/* IPv4 Source Filter Multicast API [RFC3678] */
#define IP_ADD_SOURCE_MEMBERSHIP 70 /* join a source-specific group */
#define IP_DROP_SOURCE_MEMBERSHIP 71 /* drop a single source */
#define IP_BLOCK_SOURCE 72 /* block a source */
#define IP_UNBLOCK_SOURCE 73 /* unblock a source */
/* The following option is private; do not use it from user applications. */
#define IP_MSFILTER 74 /* set/get filter list */
/* Protocol Independent Multicast API [RFC3678] */
#define MCAST_JOIN_GROUP 80 /* join an any-source group */
#define MCAST_LEAVE_GROUP 81 /* leave all sources for group */
#define MCAST_JOIN_SOURCE_GROUP 82 /* join a source-specific group */
#define MCAST_LEAVE_SOURCE_GROUP 83 /* leave a single source */
#define MCAST_BLOCK_SOURCE 84 /* block a source */
#define MCAST_UNBLOCK_SOURCE 85 /* unblock a source */
/*
* Defaults and limits for options
*/
@ -448,6 +493,7 @@ __END_DECLS
*/
#define IP_MIN_MEMBERSHIPS 31
#define IP_MAX_MEMBERSHIPS 4095
#define IP_MAX_SOURCE_FILTER 1024 /* # of filters per socket, per group */
/*
* Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP.
@ -457,6 +503,82 @@ struct ip_mreq {
struct in_addr imr_interface; /* local IP address of interface */
};
/*
* Modified argument structure for IP_MULTICAST_IF, obtained from Linux.
* This is used to specify an interface index for multicast sends, as
* the IPv4 legacy APIs do not support this (unless IP_SENDIF is available).
*/
struct ip_mreqn {
struct in_addr imr_multiaddr; /* IP multicast address of group */
struct in_addr imr_address; /* local IP address of interface */
int imr_ifindex; /* Interface index; cast to uint32_t */
};
/*
* Argument structure for IPv4 Multicast Source Filter APIs. [RFC3678]
*/
struct ip_mreq_source {
struct in_addr imr_multiaddr; /* IP multicast address of group */
struct in_addr imr_sourceaddr; /* IP address of source */
struct in_addr imr_interface; /* local IP address of interface */
};
/*
* Argument structures for Protocol-Independent Multicast Source
* Filter APIs. [RFC3678]
*/
struct group_req {
uint32_t gr_interface; /* interface index */
struct sockaddr_storage gr_group; /* group address */
};
struct group_source_req {
uint32_t gsr_interface; /* interface index */
struct sockaddr_storage gsr_group; /* group address */
struct sockaddr_storage gsr_source; /* source address */
};
#ifndef __MSFILTERREQ_DEFINED
#define __MSFILTERREQ_DEFINED
/*
* The following structure is private; do not use it from user applications.
* It is used to communicate IP_MSFILTER/IPV6_MSFILTER information between
* the RFC 3678 libc functions and the kernel.
*/
struct __msfilterreq {
uint32_t msfr_ifindex; /* interface index */
uint32_t msfr_fmode; /* filter mode for group */
uint32_t msfr_nsrcs; /* # of sources in msfr_srcs */
struct sockaddr_storage msfr_group; /* group address */
struct sockaddr_storage *msfr_srcs; /* pointer to the first member
* of a contiguous array of
* sources to filter in full.
*/
};
#endif
struct sockaddr;
/*
* Advanced (Full-state) APIs [RFC3678]
* The RFC specifies uint_t for the 6th argument to [sg]etsourcefilter().
* We use uint32_t here to be consistent.
*/
int setipv4sourcefilter(int, struct in_addr, struct in_addr, uint32_t,
uint32_t, struct in_addr *);
int getipv4sourcefilter(int, struct in_addr, struct in_addr, uint32_t *,
uint32_t *, struct in_addr *);
int setsourcefilter(int, uint32_t, struct sockaddr *, socklen_t,
uint32_t, uint32_t, struct sockaddr_storage *);
int getsourcefilter(int, uint32_t, struct sockaddr *, socklen_t,
uint32_t *, uint32_t *, struct sockaddr_storage *);
/*
* Filter modes; also used to represent per-socket filter mode internally.
*/
#define MCAST_INCLUDE 1 /* fmode: include these source(s) */
#define MCAST_EXCLUDE 2 /* fmode: exclude these source(s) */
/*
* Argument for IP_PORTRANGE:
* - which range to search when port is unspecified at bind() or connect()

1786
sys/netinet/in_mcast.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -735,7 +735,8 @@ in_pcbfree(struct inpcb *inp)
in_pcbremlists(inp);
if (inp->inp_options)
(void)m_free(inp->inp_options);
ip_freemoptions(inp->inp_moptions);
if (inp->inp_moptions != NULL)
inp_freemoptions(inp->inp_moptions);
inp->inp_vflag = 0;
#ifdef MAC

View File

@ -147,6 +147,12 @@ struct router_info {
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
};
/*
@ -166,8 +172,44 @@ struct in_multi {
u_int inm_state; /* state of the membership */
struct router_info *inm_rti; /* router info*/
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
};
#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.
*/
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
#ifdef _KERNEL
#ifdef SYSCTL_DECL
@ -246,6 +288,12 @@ do { \
} while(0)
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 *);
void in_delmulti(struct in_multi *);
void in_delmulti_locked(struct in_multi *);

View File

@ -380,6 +380,7 @@ carp_clone_create(struct if_clone *ifc, int unit, caddr_t params)
sc->sc_imo.imo_membership = (struct in_multi **)malloc(
(sizeof(struct in_multi *) * IP_MIN_MEMBERSHIPS), M_CARP,
M_WAITOK);
sc->sc_imo.imo_mfilters = NULL;
sc->sc_imo.imo_max_memberships = IP_MIN_MEMBERSHIPS;
sc->sc_imo.imo_multicast_vif = -1;
@ -1397,6 +1398,8 @@ carp_multicast_cleanup(struct carp_softc *sc)
imo->imo_membership[n] = NULL;
}
}
KASSERT(imo->imo_mfilters == NULL,
("%s: imo_mfilters != NULL", __func__));
imo->imo_num_memberships = 0;
imo->imo_multicast_ifp = NULL;
}

View File

@ -73,8 +73,6 @@
#include <security/mac/mac_framework.h>
static MALLOC_DEFINE(M_IPMOPTS, "ip_moptions", "internet multicast options");
#define print_ip(x, a, y) printf("%s %d.%d.%d.%d%s",\
x, (ntohl(a.s_addr)>>24)&0xFF,\
(ntohl(a.s_addr)>>16)&0xFF,\
@ -89,11 +87,8 @@ SYSCTL_INT(_net_inet_ip, OID_AUTO, mbuf_frag_size, CTLFLAG_RW,
&mbuf_frag_size, 0, "Fragment outgoing mbufs to this size");
#endif
static struct ifnet *ip_multicast_if(struct in_addr *, int *);
static void ip_mloopback
(struct ifnet *, struct mbuf *, struct sockaddr_in *, int);
static int ip_getmoptions(struct inpcb *, struct sockopt *);
static int ip_setmoptions(struct inpcb *, struct sockopt *);
extern struct protosw inetsw[];
@ -930,13 +925,28 @@ ip_ctloutput(struct socket *so, struct sockopt *sopt)
break;
#undef OPTSET
/*
* Multicast socket options are processed by the in_mcast
* module.
*/
case IP_MULTICAST_IF:
case IP_MULTICAST_VIF:
case IP_MULTICAST_TTL:
case IP_MULTICAST_LOOP:
case IP_ADD_MEMBERSHIP:
case IP_DROP_MEMBERSHIP:
error = ip_setmoptions(inp, sopt);
case IP_ADD_SOURCE_MEMBERSHIP:
case IP_DROP_SOURCE_MEMBERSHIP:
case IP_BLOCK_SOURCE:
case IP_UNBLOCK_SOURCE:
case IP_MSFILTER:
case MCAST_JOIN_GROUP:
case MCAST_LEAVE_GROUP:
case MCAST_JOIN_SOURCE_GROUP:
case MCAST_LEAVE_SOURCE_GROUP:
case MCAST_BLOCK_SOURCE:
case MCAST_UNBLOCK_SOURCE:
error = inp_setmoptions(inp, sopt);
break;
case IP_PORTRANGE:
@ -1095,11 +1105,16 @@ ip_ctloutput(struct socket *so, struct sockopt *sopt)
error = sooptcopyout(sopt, &optval, sizeof optval);
break;
/*
* Multicast socket options are processed by the in_mcast
* module.
*/
case IP_MULTICAST_IF:
case IP_MULTICAST_VIF:
case IP_MULTICAST_TTL:
case IP_MULTICAST_LOOP:
error = ip_getmoptions(inp, sopt);
case IP_MSFILTER:
error = inp_getmoptions(inp, sopt);
break;
#if defined(IPSEC) || defined(FAST_IPSEC)
@ -1131,477 +1146,6 @@ ip_ctloutput(struct socket *so, struct sockopt *sopt)
return (error);
}
/*
* XXX
* The whole multicast option thing needs to be re-thought.
* Several of these options are equally applicable to non-multicast
* transmission, and one (IP_MULTICAST_TTL) totally duplicates a
* standard option (IP_TTL).
*/
/*
* following RFC1724 section 3.3, 0.0.0.0/8 is interpreted as interface index.
*/
static struct ifnet *
ip_multicast_if(struct in_addr *a, int *ifindexp)
{
int ifindex;
struct ifnet *ifp;
if (ifindexp)
*ifindexp = 0;
if (ntohl(a->s_addr) >> 24 == 0) {
ifindex = ntohl(a->s_addr) & 0xffffff;
if (ifindex < 0 || if_index < ifindex)
return NULL;
ifp = ifnet_byindex(ifindex);
if (ifindexp)
*ifindexp = ifindex;
} else {
INADDR_TO_IFP(*a, ifp);
}
return ifp;
}
/*
* Given an inpcb, return its multicast options structure pointer. Accepts
* an unlocked inpcb pointer, but will return it locked. May sleep.
*/
static struct ip_moptions *
ip_findmoptions(struct inpcb *inp)
{
struct ip_moptions *imo;
struct in_multi **immp;
INP_LOCK(inp);
if (inp->inp_moptions != NULL)
return (inp->inp_moptions);
INP_UNLOCK(inp);
imo = (struct ip_moptions*)malloc(sizeof(*imo), M_IPMOPTS, M_WAITOK);
immp = (struct in_multi **)malloc((sizeof(*immp) * IP_MIN_MEMBERSHIPS),
M_IPMOPTS, M_WAITOK);
imo->imo_multicast_ifp = NULL;
imo->imo_multicast_addr.s_addr = INADDR_ANY;
imo->imo_multicast_vif = -1;
imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
imo->imo_multicast_loop = IP_DEFAULT_MULTICAST_LOOP;
imo->imo_num_memberships = 0;
imo->imo_max_memberships = IP_MIN_MEMBERSHIPS;
imo->imo_membership = immp;
INP_LOCK(inp);
if (inp->inp_moptions != NULL) {
free(immp, M_IPMOPTS);
free(imo, M_IPMOPTS);
return (inp->inp_moptions);
}
inp->inp_moptions = imo;
return (imo);
}
/*
* Set the IP multicast options in response to user setsockopt().
*/
static int
ip_setmoptions(struct inpcb *inp, struct sockopt *sopt)
{
int error = 0;
int i;
struct in_addr addr;
struct ip_mreq mreq;
struct ifnet *ifp;
struct ip_moptions *imo;
struct route ro;
struct sockaddr_in *dst;
int ifindex;
int s;
switch (sopt->sopt_name) {
/* store an index number for the vif you wanna use in the send */
case IP_MULTICAST_VIF:
if (legal_vif_num == 0) {
error = EOPNOTSUPP;
break;
}
error = sooptcopyin(sopt, &i, sizeof i, sizeof i);
if (error)
break;
if (!legal_vif_num(i) && (i != -1)) {
error = EINVAL;
break;
}
imo = ip_findmoptions(inp);
imo->imo_multicast_vif = i;
INP_UNLOCK(inp);
break;
case IP_MULTICAST_IF:
/*
* Select the interface for outgoing multicast packets.
*/
error = sooptcopyin(sopt, &addr, sizeof addr, sizeof addr);
if (error)
break;
/*
* INADDR_ANY is used to remove a previous selection.
* When no interface is selected, a default one is
* chosen every time a multicast packet is sent.
*/
imo = ip_findmoptions(inp);
if (addr.s_addr == INADDR_ANY) {
imo->imo_multicast_ifp = NULL;
INP_UNLOCK(inp);
break;
}
/*
* The selected interface is identified by its local
* IP address. Find the interface and confirm that
* it supports multicasting.
*/
s = splimp();
ifp = ip_multicast_if(&addr, &ifindex);
if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
INP_UNLOCK(inp);
splx(s);
error = EADDRNOTAVAIL;
break;
}
imo->imo_multicast_ifp = ifp;
if (ifindex)
imo->imo_multicast_addr = addr;
else
imo->imo_multicast_addr.s_addr = INADDR_ANY;
INP_UNLOCK(inp);
splx(s);
break;
case IP_MULTICAST_TTL:
/*
* Set the IP time-to-live for outgoing multicast packets.
* The original multicast API required a char argument,
* which is inconsistent with the rest of the socket API.
* We allow either a char or an int.
*/
if (sopt->sopt_valsize == 1) {
u_char ttl;
error = sooptcopyin(sopt, &ttl, 1, 1);
if (error)
break;
imo = ip_findmoptions(inp);
imo->imo_multicast_ttl = ttl;
INP_UNLOCK(inp);
} else {
u_int ttl;
error = sooptcopyin(sopt, &ttl, sizeof ttl,
sizeof ttl);
if (error)
break;
if (ttl > 255)
error = EINVAL;
else {
imo = ip_findmoptions(inp);
imo->imo_multicast_ttl = ttl;
INP_UNLOCK(inp);
}
}
break;
case IP_MULTICAST_LOOP:
/*
* Set the loopback flag for outgoing multicast packets.
* Must be zero or one. The original multicast API required a
* char argument, which is inconsistent with the rest
* of the socket API. We allow either a char or an int.
*/
if (sopt->sopt_valsize == 1) {
u_char loop;
error = sooptcopyin(sopt, &loop, 1, 1);
if (error)
break;
imo = ip_findmoptions(inp);
imo->imo_multicast_loop = !!loop;
INP_UNLOCK(inp);
} else {
u_int loop;
error = sooptcopyin(sopt, &loop, sizeof loop,
sizeof loop);
if (error)
break;
imo = ip_findmoptions(inp);
imo->imo_multicast_loop = !!loop;
INP_UNLOCK(inp);
}
break;
case IP_ADD_MEMBERSHIP:
/*
* Add a multicast group membership.
* Group must be a valid IP multicast address.
*/
error = sooptcopyin(sopt, &mreq, sizeof mreq, sizeof mreq);
if (error)
break;
if (!IN_MULTICAST(ntohl(mreq.imr_multiaddr.s_addr))) {
error = EINVAL;
break;
}
s = splimp();
/*
* If no interface address was provided, use the interface of
* the route to the given multicast address.
*/
if (mreq.imr_interface.s_addr == INADDR_ANY) {
bzero((caddr_t)&ro, sizeof(ro));
dst = (struct sockaddr_in *)&ro.ro_dst;
dst->sin_len = sizeof(*dst);
dst->sin_family = AF_INET;
dst->sin_addr = mreq.imr_multiaddr;
rtalloc_ign(&ro, RTF_CLONING);
if (ro.ro_rt == NULL) {
error = EADDRNOTAVAIL;
splx(s);
break;
}
ifp = ro.ro_rt->rt_ifp;
RTFREE(ro.ro_rt);
}
else {
ifp = ip_multicast_if(&mreq.imr_interface, NULL);
}
/*
* See if we found an interface, and confirm that it
* supports multicast.
*/
if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
error = EADDRNOTAVAIL;
splx(s);
break;
}
/*
* See if the membership already exists or if all the
* membership slots are full.
*/
imo = ip_findmoptions(inp);
for (i = 0; i < imo->imo_num_memberships; ++i) {
if (imo->imo_membership[i]->inm_ifp == ifp &&
imo->imo_membership[i]->inm_addr.s_addr
== mreq.imr_multiaddr.s_addr)
break;
}
if (i < imo->imo_num_memberships) {
INP_UNLOCK(inp);
error = EADDRINUSE;
splx(s);
break;
}
if (imo->imo_num_memberships == imo->imo_max_memberships) {
struct in_multi **nmships, **omships;
size_t newmax;
/*
* Resize the vector to next power-of-two minus 1. If the
* size would exceed the maximum then we know we've really
* run out of entries. Otherwise, we realloc() the vector
* with the INP lock held to avoid introducing a race.
*/
nmships = NULL;
omships = imo->imo_membership;
newmax = ((imo->imo_max_memberships + 1) * 2) - 1;
if (newmax <= IP_MAX_MEMBERSHIPS) {
nmships = (struct in_multi **)realloc(omships,
sizeof(*nmships) * newmax, M_IPMOPTS, M_NOWAIT);
if (nmships != NULL) {
imo->imo_membership = nmships;
imo->imo_max_memberships = newmax;
}
}
if (nmships == NULL) {
INP_UNLOCK(inp);
error = ETOOMANYREFS;
splx(s);
break;
}
}
/*
* Everything looks good; add a new record to the multicast
* address list for the given interface.
*/
if ((imo->imo_membership[i] =
in_addmulti(&mreq.imr_multiaddr, ifp)) == NULL) {
INP_UNLOCK(inp);
error = ENOBUFS;
splx(s);
break;
}
++imo->imo_num_memberships;
INP_UNLOCK(inp);
splx(s);
break;
case IP_DROP_MEMBERSHIP:
/*
* Drop a multicast group membership.
* Group must be a valid IP multicast address.
*/
error = sooptcopyin(sopt, &mreq, sizeof mreq, sizeof mreq);
if (error)
break;
if (!IN_MULTICAST(ntohl(mreq.imr_multiaddr.s_addr))) {
error = EINVAL;
break;
}
s = splimp();
/*
* If an interface address was specified, get a pointer
* to its ifnet structure.
*/
if (mreq.imr_interface.s_addr == INADDR_ANY)
ifp = NULL;
else {
ifp = ip_multicast_if(&mreq.imr_interface, NULL);
if (ifp == NULL) {
error = EADDRNOTAVAIL;
splx(s);
break;
}
}
/*
* Find the membership in the membership array.
*/
imo = ip_findmoptions(inp);
for (i = 0; i < imo->imo_num_memberships; ++i) {
if ((ifp == NULL ||
imo->imo_membership[i]->inm_ifp == ifp) &&
imo->imo_membership[i]->inm_addr.s_addr ==
mreq.imr_multiaddr.s_addr)
break;
}
if (i == imo->imo_num_memberships) {
INP_UNLOCK(inp);
error = EADDRNOTAVAIL;
splx(s);
break;
}
/*
* Give up the multicast address record to which the
* membership points.
*/
in_delmulti(imo->imo_membership[i]);
/*
* Remove the gap in the membership array.
*/
for (++i; i < imo->imo_num_memberships; ++i)
imo->imo_membership[i-1] = imo->imo_membership[i];
--imo->imo_num_memberships;
INP_UNLOCK(inp);
splx(s);
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
/*
* Return the IP multicast options in response to user getsockopt().
*/
static int
ip_getmoptions(struct inpcb *inp, struct sockopt *sopt)
{
struct ip_moptions *imo;
struct in_addr addr;
struct in_ifaddr *ia;
int error, optval;
u_char coptval;
INP_LOCK(inp);
imo = inp->inp_moptions;
error = 0;
switch (sopt->sopt_name) {
case IP_MULTICAST_VIF:
if (imo != NULL)
optval = imo->imo_multicast_vif;
else
optval = -1;
INP_UNLOCK(inp);
error = sooptcopyout(sopt, &optval, sizeof optval);
break;
case IP_MULTICAST_IF:
if (imo == NULL || imo->imo_multicast_ifp == NULL)
addr.s_addr = INADDR_ANY;
else if (imo->imo_multicast_addr.s_addr) {
/* return the value user has set */
addr = imo->imo_multicast_addr;
} else {
IFP_TO_IA(imo->imo_multicast_ifp, ia);
addr.s_addr = (ia == NULL) ? INADDR_ANY
: IA_SIN(ia)->sin_addr.s_addr;
}
INP_UNLOCK(inp);
error = sooptcopyout(sopt, &addr, sizeof addr);
break;
case IP_MULTICAST_TTL:
if (imo == 0)
optval = coptval = IP_DEFAULT_MULTICAST_TTL;
else
optval = coptval = imo->imo_multicast_ttl;
INP_UNLOCK(inp);
if (sopt->sopt_valsize == 1)
error = sooptcopyout(sopt, &coptval, 1);
else
error = sooptcopyout(sopt, &optval, sizeof optval);
break;
case IP_MULTICAST_LOOP:
if (imo == 0)
optval = coptval = IP_DEFAULT_MULTICAST_LOOP;
else
optval = coptval = imo->imo_multicast_loop;
INP_UNLOCK(inp);
if (sopt->sopt_valsize == 1)
error = sooptcopyout(sopt, &coptval, 1);
else
error = sooptcopyout(sopt, &optval, sizeof optval);
break;
default:
INP_UNLOCK(inp);
error = ENOPROTOOPT;
break;
}
INP_UNLOCK_ASSERT(inp);
return (error);
}
/*
* Discard the IP multicast options.
*/
void
ip_freemoptions(struct ip_moptions *imo)
{
register int i;
if (imo != NULL) {
for (i = 0; i < imo->imo_num_memberships; ++i)
in_delmulti(imo->imo_membership[i]);
free(imo->imo_membership, M_IPMOPTS);
free(imo, M_IPMOPTS);
}
}
/*
* Routine called from ip_output() to loop back a copy of an IP multicast
* packet to the input queue of a specified interface. Note that this

View File

@ -78,9 +78,29 @@ 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.
* This structure is lazy-allocated.
*/
struct ip_moptions {
struct ifnet *imo_multicast_ifp; /* ifp for outgoing multicasts */
@ -91,6 +111,7 @@ struct ip_moptions {
u_short imo_num_memberships; /* no. memberships this socket */
u_short imo_max_memberships; /* max memberships this socket */
struct in_multi **imo_membership; /* group memberships */
struct in_mfilter *imo_mfilters; /* source filters */
};
struct ipstat {
@ -127,12 +148,11 @@ struct ipstat {
#ifdef _KERNEL
/*
* Flags passed to ip_output as last parameter.
*/
#define IP_FORWARDING 0x01 /* most of ip header exists */
#define IP_RAWOUTPUT 0x02 /* raw ip header exists */
#define IP_SENDONES 0x04 /* send all-ones broadcast */
/* flags passed to ip_output as last parameter */
#define IP_FORWARDING 0x1 /* most of ip header exists */
#define IP_RAWOUTPUT 0x2 /* raw ip header exists */
#define IP_SENDONES 0x4 /* send all-ones broadcast */
#define IP_SENDTOIF 0x8 /* send on specific ifnet */
#define IP_ROUTETOIF SO_DONTROUTE /* 0x10 bypass routing tables */
#define IP_ALLOWBROADCAST SO_BROADCAST /* 0x20 can send broadcast packets */
@ -167,12 +187,15 @@ extern u_long (*ip_mcast_src)(int);
extern int rsvp_on;
extern struct pr_usrreqs rip_usrreqs;
void inp_freemoptions(struct ip_moptions *);
int inp_getmoptions(struct inpcb *, struct sockopt *);
int inp_setmoptions(struct inpcb *, struct sockopt *);
int ip_ctloutput(struct socket *, struct sockopt *sopt);
void ip_drain(void);
void ip_fini(void *xtp);
int ip_fragment(struct ip *ip, struct mbuf **m_frag, int mtu,
u_long if_hwassist_flags, int sw_csum);
void ip_freemoptions(struct ip_moptions *);
void ip_forward(struct mbuf *m, int srcrt);
void ip_init(void);
extern int

View File

@ -2791,7 +2791,7 @@ sctp_inpcb_free(struct sctp_inpcb *inp, int immediate, int from)
ip_pcb->inp_options = 0;
}
if (ip_pcb->inp_moptions) {
ip_freemoptions(ip_pcb->inp_moptions);
inp_freemoptions(ip_pcb->inp_moptions);
ip_pcb->inp_moptions = 0;
}
#ifdef INET6

View File

@ -113,10 +113,6 @@ static int blackhole = 0;
SYSCTL_INT(_net_inet_udp, OID_AUTO, blackhole, CTLFLAG_RW, &blackhole, 0,
"Do not send port unreachables for refused connects");
static int strict_mcast_mship = 0;
SYSCTL_INT(_net_inet_udp, OID_AUTO, strict_mcast_mship, CTLFLAG_RW,
&strict_mcast_mship, 0, "Only send multicast to member sockets");
struct inpcbhead udb; /* from udp_var.h */
struct inpcbinfo udbinfo;
@ -176,6 +172,7 @@ udp_input(struct mbuf *m, int off)
int iphlen = off;
struct ip *ip;
struct udphdr *uh;
struct ifnet *ifp;
struct inpcb *inp;
int len;
struct ip save_ip;
@ -184,6 +181,7 @@ udp_input(struct mbuf *m, int off)
struct m_tag *fwd_tag;
#endif
ifp = m->m_pkthdr.rcvif;
udpstat.udps_ipackets++;
/*
@ -301,25 +299,10 @@ udp_input(struct mbuf *m, int off)
INP_INFO_RLOCK(&udbinfo);
if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif)) {
in_broadcast(ip->ip_dst, ifp)) {
struct inpcb *last;
struct ip_moptions *imo;
/*
* Deliver a multicast or broadcast datagram to *all* sockets
* for which the local and remote addresses and ports match
* those of the incoming datagram. This allows more than one
* process to receive multi/broadcasts on the same port.
* (This really ought to be done for unicast datagrams as
* well, but that would cause problems with existing
* applications that open both address-specific sockets and a
* wildcard socket listening to the same port -- they would
* end up receiving duplicates of every unicast datagram.
* Those applications open the multiple sockets to overcome
* an inadequacy of the UDP socket interface, but for
* backwards compatibility we avoid the problem here rather
* than fixing the interface. Maybe 4.5BSD will remedy
* this?)
*/
last = NULL;
LIST_FOREACH(inp, &udb, inp_list) {
if (inp->inp_lport != uh->uh_dport)
@ -328,45 +311,83 @@ udp_input(struct mbuf *m, int off)
if ((inp->inp_vflag & INP_IPV4) == 0)
continue;
#endif
if (inp->inp_laddr.s_addr != INADDR_ANY) {
if (inp->inp_laddr.s_addr != ip->ip_dst.s_addr)
if (inp->inp_laddr.s_addr != INADDR_ANY &&
inp->inp_laddr.s_addr != ip->ip_dst.s_addr)
continue;
}
if (inp->inp_faddr.s_addr != INADDR_ANY) {
if (inp->inp_faddr.s_addr !=
ip->ip_src.s_addr ||
inp->inp_fport != uh->uh_sport)
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;
INP_LOCK(inp);
/*
* Check multicast packets to make sure they are only
* sent to sockets with multicast memberships for the
* packet's destination address and arrival interface
* Handle socket delivery policy for any-source
* and source-specific multicast. [RFC3678]
*/
#define MSHIP(_inp, n) ((_inp)->inp_moptions->imo_membership[(n)])
#define NMSHIPS(_inp) ((_inp)->inp_moptions->imo_num_memberships)
INP_LOCK(inp);
if (strict_mcast_mship && inp->inp_moptions != NULL) {
int mship, foundmship = 0;
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;
for (mship = 0; mship < NMSHIPS(inp);
mship++) {
if (MSHIP(inp, mship)->inm_addr.s_addr
== ip->ip_dst.s_addr &&
MSHIP(inp, mship)->inm_ifp
== m->m_pkthdr.rcvif) {
foundmship = 1;
break;
bzero(&sin, sizeof(struct sockaddr_in));
sin.sin_len = sizeof(struct sockaddr_in);
sin.sin_family = AF_INET;
sin.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
udpstat.udps_filtermcast++;
blocked++;
}
}
if (foundmship == 0) {
if (blocked != 0) {
INP_UNLOCK(inp);
continue;
}
}
#undef NMSHIPS
#undef MSHIP
if (last != NULL) {
struct mbuf *n;
@ -410,7 +431,7 @@ udp_input(struct mbuf *m, int off)
* Locate pcb for datagram.
*/
inp = in_pcblookup_hash(&udbinfo, ip->ip_src, uh->uh_sport,
ip->ip_dst, uh->uh_dport, 1, m->m_pkthdr.rcvif);
ip->ip_dst, uh->uh_dport, 1, ifp);
if (inp == NULL) {
if (udp_log_in_vain) {
char buf[4*sizeof "123"];

View File

@ -68,6 +68,7 @@ struct udpstat {
u_long udps_fastout; /* output packets on fast path */
/* of no socket on port, arrived as multicast */
u_long udps_noportmcast;
u_long udps_filtermcast; /* blocked by multicast filter */
};
/*

View File

@ -467,6 +467,14 @@ struct route_in6 {
* the source address.
*/
/*
* The following option is private; do not use it from user applications.
* It is deliberately defined to the same value as IP_MSFILTER.
*/
#define IPV6_MSFILTER 74 /* struct __msfilterreq;
* set/get multicast source filter list.
*/
/* to define items, should talk with KAME guys first, for *BSD compatibility */
#define IPV6_RTHDR_LOOSE 0 /* this hop need not be a neighbor. XXX old spec */
@ -487,6 +495,18 @@ struct ipv6_mreq {
unsigned int ipv6mr_interface;
};
#ifdef notyet
/*
* Argument structure for IPV6_ADD_SOURCE_MEMBERSHIP,
* IPV6_DROP_SOURCE_MEMBERSHIP, IPV6_BLOCK_SOURCE, and IPV6_UNBLOCK_SOURCE.
*/
struct ipv6_mreq_source {
struct in6_addr ipv6mr_multiaddr;
struct in6_addr ipv6mr_sourceaddr;
uint32_t ipv6mr_interface;
};
#endif
/*
* IPV6_PKTINFO: Packet information(RFC2292 sec 5)
*/

View File

@ -76,6 +76,7 @@ static int generate_tmp_ifid __P((u_int8_t *, const u_int8_t *, u_int8_t *));
static int get_ifid __P((struct ifnet *, struct ifnet *, struct in6_addr *));
static int in6_ifattach_linklocal __P((struct ifnet *, struct ifnet *));
static int in6_ifattach_loopback __P((struct ifnet *));
static void in6_purgemaddrs __P((struct ifnet *));
#define EUI64_GBIT 0x01
#define EUI64_UBIT 0x02
@ -798,18 +799,10 @@ in6_ifdetach(ifp)
IFAFREE(&oia->ia_ifa);
}
/* leave from all multicast groups joined */
in6_pcbpurgeif0(&udbinfo, ifp);
in6_pcbpurgeif0(&ripcbinfo, ifp);
for (in6m = LIST_FIRST(&in6_multihead); in6m; in6m = in6m_next) {
in6m_next = LIST_NEXT(in6m, in6m_entry);
if (in6m->in6m_ifp != ifp)
continue;
in6_delmulti(in6m);
in6m = NULL;
}
/* leave from all multicast groups joined */
in6_purgemaddrs(ifp);
/*
* remove neighbor management table. we call it twice just to make
@ -898,3 +891,22 @@ in6_tmpaddrtimer(ignored_arg)
splx(s);
}
static void
in6_purgemaddrs(ifp)
struct ifnet *ifp;
{
struct in6_multi *in6m;
struct in6_multi *oin6m;
#ifdef DIAGNOSTIC
printf("%s: purging ifp %p\n", __func__, ifp);
#endif
IFF_LOCKGIANT(ifp);
LIST_FOREACH_SAFE(in6m, &in6_multihead, in6m_entry, oin6m) {
if (in6m->in6m_ifp == ifp)
in6_delmulti(in6m);
}
IFF_UNLOCKGIANT(ifp);
}

View File

@ -456,7 +456,8 @@ in6_pcbfree(struct inpcb *inp)
/* Check and free IPv4 related resources in case of mapped addr */
if (inp->inp_options)
(void)m_free(inp->inp_options);
ip_freemoptions(inp->inp_moptions);
if (inp->inp_moptions != NULL)
inp_freemoptions(inp->inp_moptions);
inp->inp_vflag = 0;
INP_UNLOCK(inp);
uma_zfree(ipi->ipi_zone, inp);

View File

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

View File

@ -234,6 +234,7 @@ struct sockproto {
};
#endif
#ifndef _STRUCT_SOCKADDR_STORAGE_DECLARED
/*
* RFC 2553: protocol-independent placeholder for socket addresses
*/
@ -251,6 +252,8 @@ struct sockaddr_storage {
__int64_t __ss_align; /* force desired struct alignment */
char __ss_pad2[_SS_PAD2SIZE];
};
#define _STRUCT_SOCKADDR_STORAGE_DECLARED
#endif
#if __BSD_VISIBLE
/*

View File

@ -679,7 +679,7 @@ test_ip_multicast_membership(int sock, const char *socktypename)
* this usually maps to the interface to which the default
* route is pointing.
*/
for (i = 0; i < nmcastgroups; i++) {
for (i = 1; i < nmcastgroups+1; i++) {
mreq.imr_multiaddr.s_addr = htonl((basegroup + i));
mreq.imr_interface.s_addr = INADDR_ANY;
inet_ntop(AF_INET, &mreq.imr_multiaddr, addrbuf, sizeof(addrbuf));
@ -692,7 +692,7 @@ test_ip_multicast_membership(int sock, const char *socktypename)
sock, socktypename, addrbuf, "INADDR_ANY");
}
}
for (i = 0; i < nmcastgroups; i++) {
for (i = 1; i < nmcastgroups+1; i++) {
mreq.imr_multiaddr.s_addr = htonl((basegroup + i));
mreq.imr_interface.s_addr = INADDR_ANY;
inet_ntop(AF_INET, &mreq.imr_multiaddr, addrbuf, sizeof(addrbuf));

View File

@ -513,7 +513,7 @@ udp_stats(u_long off __unused, const char *name, int af1 __unused)
p1a(udps_nosum, "\t%lu with no checksum\n");
p1a(udps_noport, "\t%lu dropped due to no socket\n");
p(udps_noportbcast,
"\t%lu broadcast/multicast datagram%s dropped due to no socket\n");
"\t%lu broadcast/multicast datagram%s undelivered\n");
p1a(udps_fullsock, "\t%lu dropped due to full socket buffers\n");
p1a(udpps_pcbhashmiss, "\t%lu not for hashed pcb\n");
delivered = udpstat.udps_ipackets -
@ -526,6 +526,9 @@ udp_stats(u_long off __unused, const char *name, int af1 __unused)
if (delivered || sflag <= 1)
printf("\t%lu delivered\n", delivered);
p(udps_opackets, "\t%lu datagram%s output\n");
/* the next statistic is cumulative in udps_noportbcast */
p(udps_filtermcast,
"\t%lu time%s multicast source filter matched\n");
#undef p
#undef p1a
}

View File

@ -55,6 +55,15 @@ __FBSDID("$FreeBSD$");
#include <err.h>
#include <unistd.h>
/* The following two socket options are private to the kernel and libc. */
#ifndef IP_SETMSFILTER
#define IP_SETMSFILTER 74 /* atomically set filter list */
#endif
#ifndef IP_GETMSFILTER
#define IP_GETMSFILTER 75 /* get filter list */
#endif
static void process_file(char *, int);
static void process_cmd(char*, int, FILE *fp);
static void usage(void);
@ -135,14 +144,14 @@ process_cmd(char *cmd, int s, FILE *fp __unused)
{
char str1[STR_SIZE];
char str2[STR_SIZE];
#ifdef WITH_IGMPV3
char str3[STR_SIZE];
#ifdef WITH_IGMPV3
char filtbuf[IP_MSFILTER_SIZE(MAX_ADDRS)];
#endif
struct ifreq ifr;
struct ip_mreq imr;
#ifdef WITH_IGMPV3
struct ip_mreq_source imrs;
#ifdef WITH_IGMPV3
struct ip_msfilter *imsfp;
#endif
char *line;
@ -256,6 +265,7 @@ process_cmd(char *cmd, int s, FILE *fp __unused)
*/
case 'i':
case 'e':
/* XXX: SIOCSIPMSFILTER will be made an internal API. */
if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) {
printf("-1\n");
break;
@ -284,10 +294,13 @@ process_cmd(char *cmd, int s, FILE *fp __unused)
else
printf("ok\n");
break;
#endif /* WITH_IGMPV3 */
/*
* Allow or block traffic from a source, using the
* delta based api.
* XXX: Currently we allow this to be used with the ASM-only
* implementation of RFC3678 in FreeBSD 7.
*/
case 't':
case 'b':
@ -302,6 +315,8 @@ process_cmd(char *cmd, int s, FILE *fp __unused)
break;
}
#ifdef WITH_IGMPV3
/* XXX: SIOCSIPMSFILTER will be made an internal API. */
/* First determine out current filter mode. */
imsfp = (struct ip_msfilter *)filtbuf;
imsfp->imsf_multiaddr.s_addr = imrs.imr_multiaddr.s_addr;
@ -325,13 +340,22 @@ process_cmd(char *cmd, int s, FILE *fp __unused)
opt = (*cmd == 't') ? IP_ADD_SOURCE_MEMBERSHIP :
IP_DROP_SOURCE_MEMBERSHIP;
}
#else /* !WITH_IGMPV3 */
/*
* Don't look before we leap; we may only block or unblock
* sources on a socket in exclude mode.
*/
opt = (*cmd == 't') ? IP_UNBLOCK_SOURCE : IP_BLOCK_SOURCE;
#endif /* WITH_IGMPV3 */
if (setsockopt(s, IPPROTO_IP, opt, &imrs, sizeof(imrs)) == -1)
warn("ioctl IP_ADD_SOURCE_MEMBERSHIP/IP_DROP_SOURCE_MEMBERSHIP/IP_UNBLOCK_SOURCE/IP_BLOCK_SOURCE");
else
printf("ok\n");
break;
#ifdef WITH_IGMPV3
case 'g':
/* XXX: SIOCSIPMSFILTER will be made an internal API. */
if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) {
printf("-1\n");
break;
@ -360,11 +384,11 @@ process_cmd(char *cmd, int s, FILE *fp __unused)
printf("%s\n", inet_ntoa(imsfp->imsf_slist[i]));
}
break;
#else /* !WITH_IGMPV3 */
#endif /* !WITH_IGMPV3 */
#ifndef WITH_IGMPV3
case 'i':
case 'e':
case 't':
case 'b':
case 'g':
printf("warning: IGMPv3 is not supported by this version "
"of FreeBSD; command ignored.\n");
@ -389,11 +413,15 @@ usage(void)
printf("d ifname e.e.e.e.e.e - delete ether multicast address\n");
printf("m ifname 1/0 - set/clear ether allmulti flag\n");
printf("p ifname 1/0 - set/clear ether promisc flag\n");
#ifdef WITH_IGMPv3
printf("i g.g.g.g i.i.i.i n - set n include mode src filter\n");
printf("e g.g.g.g i.i.i.i n - set n exclude mode src filter\n");
#endif
printf("t g.g.g.g i.i.i.i s.s.s.s - allow traffic from src\n");
printf("b g.g.g.g i.i.i.i s.s.s.s - block traffic from src\n");
#ifdef WITH_IGMPV3
printf("g g.g.g.g i.i.i.i n - get and show n src filters\n");
#endif
printf("f filename - read command(s) from file\n");
printf("s seconds - sleep for some time\n");
printf("q - quit\n");