diff options
author | bms <bms@FreeBSD.org> | 2007-06-12 16:24:56 +0000 |
---|---|---|
committer | bms <bms@FreeBSD.org> | 2007-06-12 16:24:56 +0000 |
commit | ffd77d9ba5a1376d64ccbb2909a7179c05de81bc (patch) | |
tree | ef2e1b349db858481633196c1f4a1cc9cd67fe16 /sys/netinet/udp_usrreq.c | |
parent | d4a700c2dc6c55dae07b6ed7fe4d47508632b9f4 (diff) | |
download | FreeBSD-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.c | 121 |
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"]; |