From william.a@carrel.org  Tue Oct 21 19:30:28 2003
Return-Path: <william.a@carrel.org>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id 62AF316A4B3
	for <FreeBSD-gnats-submit@FreeBSD.org>; Tue, 21 Oct 2003 19:30:28 -0700 (PDT)
Received: from mx.carrel.org (adsl-64-91-104-251.gh.customer.centurytel.net [64.91.104.251])
	by mx1.FreeBSD.org (Postfix) with ESMTP id 5CA1443FAF
	for <FreeBSD-gnats-submit@FreeBSD.org>; Tue, 21 Oct 2003 19:30:27 -0700 (PDT)
	(envelope-from william.a@carrel.org)
Received: from carrel.org (unknown [172.16.4.3])
	by mx.carrel.org (Postfix) with ESMTP id 9AA4A14B
	for <FreeBSD-gnats-submit@FreeBSD.org>; Tue, 21 Oct 2003 19:30:34 -0700 (PDT)
Message-Id: <B153B420-0437-11D8-9AFA-003065479A66@carrel.org>
Date: Tue, 21 Oct 2003 19:30:25 -0700
From: William A.Carrel <william.a@carrel.org>
To: FreeBSD-gnats-submit@FreeBSD.org
Subject: Multicast packets sent to inappropriate sockets (patch)

>Number:         58359
>Category:       kern
>Synopsis:       Multicast packets sent to inappropriate sockets (patch)
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    bms
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Oct 21 19:40:11 PDT 2003
>Closed-Date:    Wed Nov 12 12:20:32 PST 2003
>Last-Modified:  Wed Nov 12 12:20:32 PST 2003
>Originator:     William A. Carrel
>Release:        
>Organization:
>Environment:
FreeBSD
 System: FreeBSD, OpenBSD, NetBSD, Darwin, DragonflyBSD
 
>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
 	FreeBSD-CURRENT and FreeBSD-STABLE.  (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.
 
 === FreeBSD CURRENT patch ===
 --- udp_usrreq.c.orig	Wed Sep  3 10:20:27 2003
 +++ udp_usrreq.c	Tue Oct 21 19:13:55 2003
 @@ -307,6 +307,21 @@
   				    inp->inp_fport != uh->uh_sport)
   					goto docontinue;
   			}
 +			/*
 +			 * 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;
 +				}
 + 			}
 
   			if (last != NULL) {
   				struct mbuf *n;
 === EOP ===
 
 === FreeBSD RELENG_4 patch ===
 --- udp_usrreq.c.orig	Tue Oct 21 17:22:09 2003
 +++ udp_usrreq.c	Tue Oct 21 18:21:15 2003
 @@ -279,8 +279,11 @@
   			if ((inp->inp_vflag & INP_IPV4) == 0)
   				continue;
   #endif
 -			if (inp->inp_lport != uh->uh_dport)
 +			if (inp->inp_lport != uh->uh_dport) {
 +		docontinue:
   				continue;
 +			}
 +
   			if (inp->inp_laddr.s_addr != INADDR_ANY) {
   				if (inp->inp_laddr.s_addr !=
   				    ip->ip_dst.s_addr)
 @@ -291,6 +294,21 @@
   				    ip->ip_src.s_addr ||
   				    inp->inp_fport != uh->uh_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;
 +				}
   			}
 
   			if (last != NULL) {
 === EOP ===
 
>How-To-Repeat:
>Fix:
>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: gnats-admin->freebsd-bugs 
Responsible-Changed-By: linimon 
Responsible-Changed-When: Tue Oct 21 23:49:31 PDT 2003 
Responsible-Changed-Why:  
Assign to appropriate category and responsible. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=58359 

From: William A.Carrel <william.a@carrel.org>
To: freebsd-gnats-submit@FreeBSD.org,
	William A.Carrel <william.a@carrel.org>
Cc: freebsd-net@freebsd.org
Subject: Re: kern/58359: Strict Multicast Membership (patch w/ sysctl knob)
Date: Sat, 25 Oct 2003 00:00:47 -0700

 I've been told thirdhand that there has been some amount of 
 handwringing regarding this PR.  My impression is that the worries 
 mainly surround the fact that certain programs may depend on the fairly 
 sparsely documented (http://www.kohala.com/start/mcast.api.txt) 
 behavior currently shown.  Change in behavior of decades old code can 
 certainly be a concern, yet I would also like to see the option of the 
 new behavior (which I'm told matches Solaris), therefore...
 
 Here is a new patch which provides a sysctl to turn this "extra" 
 filtering on and off.  net.inet.udp.strict_mcast_mship.  It defaults to 
 off so as to reduce any risk of POLA violation to zero.  This also can 
 help push any bikeshed discussions about the default into the future.  
 ;-)  If it would help, I can also work on providing a patch for the 
 ip(4) manpage to reflect this option and/or note the current stack 
 behavior more explicitly.
 
 Please feel free to let me know what you think, or feel free to let 
 word of what you think drift through a few layers of indirection before 
 it gets to me.  I'll do what I can to mitigate concerns either way. :-) 
   Thanks for reading.
 
 --- freebsd_current_udp_usrreq.c.orig	Fri Oct 24 12:35:36 2003
 +++ freebsd_current_udp_usrreq.c	Fri Oct 24 23:19:11 2003
 @@ -108,6 +108,10 @@
   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 */
   #define	udb6	udb  /* for KAME src sync over BSD*'s */
   struct	inpcbinfo udbinfo;
 @@ -306,6 +310,28 @@
   				    ip->ip_src.s_addr ||
   				    inp->inp_fport != uh->uh_sport)
   					goto docontinue;
 +			}
 +			/*
 +			 * 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 (strict_mcast_mship &&
 +			    IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) &&
 +			    inp->inp_moptions != NULL) {
 +				int mshipno;
 +
 +				for (mshipno = 0;
 +				     mshipno <= inp->inp_moptions->imo_num_memberships;
 +				     ++mshipno) {
 +					if (ip->ip_dst.s_addr == 
 inp->inp_moptions->imo_membership[mshipno]->inm_addr.s_addr && 
 m->m_pkthdr.rcvif == inp->inp_mo
 +ptions->imo_membership[mshipno]->inm_ifp)
 +						break;
 +				}
 +				if (mshipno ==
 +				    inp->inp_moptions->imo_num_memberships)
 +					goto docontinue;
 +					
   			}
 
   			if (last != NULL) {
 
 --- freebsd_stable_udp_usrreq.c.orig	Fri Oct 24 12:35:46 2003
 +++ freebsd_stable_udp_usrreq.c	Fri Oct 24 23:22:01 2003
 @@ -102,6 +102,10 @@
   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 */
   #define	udb6	udb  /* for KAME src sync over BSD*'s */
   struct	inpcbinfo udbinfo;
 @@ -290,6 +294,27 @@
   				if (inp->inp_faddr.s_addr !=
   				    ip->ip_src.s_addr ||
   				    inp->inp_fport != uh->uh_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 (strict_mcast_mship &&
 +			    IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) &&
 +			    inp->inp_moptions != NULL) {
 +				int mshipno;
 +				
 +				for (mshipno = 0;
 +				     mshipno <=
 +				        inp->inp_moptions->imo_num_memberships;
 +				     ++mshipno) {
 +					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;
 +				}
 +				if (mshipno ==
 +				    inp->inp_moptions->imo_num_memberships)
   					continue;
   			}
 
Responsible-Changed-From-To: freebsd-bugs->fenner 
Responsible-Changed-By: gordon 
Responsible-Changed-When: Tue Oct 28 17:51:44 PST 2003 
Responsible-Changed-Why:  
Bill seems to be the local guru when it comes to this area. Bill, can you 
let William know if you have any reservations about the patch so that he 
can follow up with you more directly? I'd like to make this a bit more 
transparent from the user's point of view. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=58359 
Responsible-Changed-From-To: fenner->bms 
Responsible-Changed-By: bms 
Responsible-Changed-When: Wed 12 Nov 2003 08:08:15 PST 
Responsible-Changed-Why:  
I'll look into this as unofficial understudy, as the Man Himself is 
Busy At Dayjob. Also, I just got my own wireless multicast cloud working, 
so I have more reason to fix multicast things. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=58359 
State-Changed-From-To: open->closed 
State-Changed-By: bms 
State-Changed-When: Wed 12 Nov 2003 12:19:58 PST 
State-Changed-Why:  
Another one bites the dust (committed, with some cleanups). Thanks! 

http://www.freebsd.org/cgi/query-pr.cgi?pr=58359 
>Unformatted:
