summaryrefslogtreecommitdiffstats
path: root/sys/netinet/udp_usrreq.c
diff options
context:
space:
mode:
authorbms <bms@FreeBSD.org>2007-06-12 16:24:56 +0000
committerbms <bms@FreeBSD.org>2007-06-12 16:24:56 +0000
commitffd77d9ba5a1376d64ccbb2909a7179c05de81bc (patch)
treeef2e1b349db858481633196c1f4a1cc9cd67fe16 /sys/netinet/udp_usrreq.c
parentd4a700c2dc6c55dae07b6ed7fe4d47508632b9f4 (diff)
downloadFreeBSD-src-ffd77d9ba5a1376d64ccbb2909a7179c05de81bc.zip
FreeBSD-src-ffd77d9ba5a1376d64ccbb2909a7179c05de81bc.tar.gz
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)
Diffstat (limited to 'sys/netinet/udp_usrreq.c')
-rw-r--r--sys/netinet/udp_usrreq.c121
1 files changed, 71 insertions, 50 deletions
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
index f6031d6..1679699 100644
--- a/sys/netinet/udp_usrreq.c
+++ b/sys/netinet/udp_usrreq.c
@@ -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;
- }
-
/*
- * Check multicast packets to make sure they are only
- * sent to sockets with multicast memberships for the
- * packet's destination address and arrival interface
+ * 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.
*/
-#define MSHIP(_inp, n) ((_inp)->inp_moptions->imo_membership[(n)])
-#define NMSHIPS(_inp) ((_inp)->inp_moptions->imo_num_memberships)
+ if (inp->inp_fport != 0 &&
+ inp->inp_fport != uh->uh_sport)
+ continue;
+
INP_LOCK(inp);
- if (strict_mcast_mship && inp->inp_moptions != NULL) {
- int mship, foundmship = 0;
-
- 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;
+
+ /*
+ * Handle socket delivery policy for any-source
+ * and source-specific multicast. [RFC3678]
+ */
+ 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;
+
+ 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"];
OpenPOWER on IntegriCloud