From ffd77d9ba5a1376d64ccbb2909a7179c05de81bc Mon Sep 17 00:00:00 2001 From: bms Date: Tue, 12 Jun 2007 16:24:56 +0000 Subject: 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 . 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) --- sys/netinet/udp_usrreq.c | 121 +++++++++++++++++++++++++++-------------------- 1 file changed, 71 insertions(+), 50 deletions(-) (limited to 'sys/netinet/udp_usrreq.c') 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"]; -- cgit v1.1