Subject: kern/23221: Multicast packets sent to inappropriate sockets (patch)
To: None <gnats-bugs@gnats.NetBSD.org>
From: None <william.a@carrel.org>
List: netbsd-bugs
Date: 10/22/2003 02:35:17
>Number:         23221
>Category:       kern
>Synopsis:       Multicast packets sent to inappropriate sockets (patch)
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Oct 22 02:36:00 UTC 2003
>Closed-Date:
>Last-Modified:
>Originator:     William A. Carrel
>Release:        Any
>Organization:
>Environment:
n/a
>Description:
	udp_input() in src/sys/netinet/udp_usrreq.c does not properly check 
	inbound multicast packets to make sure they conform with memberships 
	on a given socket before sending packets along to that socket.  

	This can result in UDP multicast packets being erroneously delivered 
	to sockets that should not be seeing them. Programs wishing to 
	guarantee that a socket is only seeing packets on one interface must 
	read in information about the interface configuration to try and 
	determine whether the packet was legitimately delivered to it.

	This bug has been present since at least 4.4BSD-Lite and is present
	in all known BSD derivatives.  Problem reports are simultaneously 
	being filed to FreeBSD, NetBSD, Darwin, DragonflyBSD and OpenBSD.
	I'll follow up to this PR with cross-references.

	A brief discussion of this issue and more details about it can be 
	found in the freebsd-net mailing list under the subject 
	"setsockopt IP_ADD_MEMBERSHIP not honored" around the date of this
	problem report.

>How-To-Repeat:
	On a machine with two multicast interfaces, open two sockets and
	add them to the same multicast address but on different interfaces
	using setsockopt([sock], IP_ADD_MEMBERSHIP, sizeof(the_mreq), 
	&the_mreq);  Inbound multicast packets to the requested multicast
	address on either interface will appear in both sockets, not just
	the requested one.

	See mailing list discussion for more information.

>Fix:
	Programs must either use sysctl or ioctl to gather information about
	the interface they want to be exclusively receiving multicast packets
	from or the kernel must be patched.  Patches follow for 
	NetBSD-CURRENT.  (The new code may not be fully 
	style(9) compliant, my apologies in advance.)

	The docontinue goto present in the stable patch is consistent with 
	treatment for FreeBSD-CURRENT and reduces diffs to that code base.

--- udp_usrreq.c.orig	Tue Oct 21 19:18:49 2003
+++ udp_usrreq.c	Tue Oct 21 19:18:25 2003
@@ -629,8 +629,10 @@
 		 */
 		CIRCLEQ_FOREACH(inph, &udbtable.inpt_queue, inph_queue) {
 			inp = (struct inpcb *)inph;
-			if (inp->inp_af != AF_INET)
+			if (inp->inp_af != AF_INET) {
+		docontinue:
 				continue;
+			}
 
 			if (inp->inp_lport != *dport)
 				continue;
@@ -643,6 +645,22 @@
 				    inp->inp_fport != *sport)
 					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
+			 */
+			if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
+				int mshipno;
+
+				for (mshipno = 0; mshipno <= inp->inp_moptions->imo_num_memberships; ++mshipno) {
+					if (mshipno == inp->inp_moptions->imo_num_memberships)
+						goto docontinue;
+					if (ip->ip_dst.s_addr == inp->inp_moptions->imo_membership[mshipno]->inm_addr.s_addr && m->m_pkthdr.rcvif == inp->inp_moptions->imo_membership[mshipno]->inm_ifp)
+						break;
+				}
+			}
+
 
 			udp4_sendup(m, off, (struct sockaddr *)src,
 				inp->inp_socket);

>Release-Note:
>Audit-Trail:
>Unformatted: