Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 12 Jan 2008 12:06:59 +0100
From:      Fredrik Lindberg <fli@shapeshifter.se>
To:        freebsd-net@freebsd.org
Subject:   Unexpected multicast IPv4 socket behavior
Message-ID:  <47889F53.4010605@shapeshifter.se>

next in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.
--------------010609050507030507010803
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

Hi

I find the following socket behavior a bit unexpected. Multicast from
an IPv4 socket (with IP_MULTICAST_IF set) with its source address bound
to INADDR_ANY only works if there is a default route defined, otherwise
send() returns ENETUNREACH.

Default route set, src INADDR_ANY : Works
Default route set, src bind() to interface address : Works
No default route, src INADDR_ANY : Returns ENETUNREACH
No default route, src bind() to interface address : Works

In all cases IP_MULTICAST_IF was set to the outgoing interface and
IP_ADD_MEMBERSHIP was properly called. IGMP membership reports
were seen on the link in all cases.

I believe the cause of this (unless this is the expected behavior?)
is in in_pcbconnect_setup() (netinet/in_pcb.c) [1].
The check for a multicast destination address is run after the attempt
to get the source address by finding a directly connected interface,
this check also returns ENETUNREACH if it fails (which it does for the
destination 224.0.0.0/24 if no default route is set).

Moving the multicast check before the directly connected check solves
this (or any other combinations that makes sure that the
IN_MULTICAST() check is executed).

I've attached a test case and a patch to illustrate it, comments?


Fredrik Lindberg



[1] http://fxr.watson.org/fxr/source/netinet/in_pcb.c#L610



--------------010609050507030507010803
Content-Type: text/x-patch;
 name="in_pcb.c-multicast.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="in_pcb.c-multicast.patch"

Index: netinet/in_pcb.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/in_pcb.c,v
retrieving revision 1.198
diff -d -u -r1.198 in_pcb.c
--- netinet/in_pcb.c	22 Dec 2007 10:06:11 -0000	1.198
+++ netinet/in_pcb.c	12 Jan 2008 10:48:08 -0000
@@ -618,26 +618,6 @@
 		if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0)
 			ia = ip_rtaddr(faddr);
 		/*
-		 * If we found a route, use the address corresponding to
-		 * the outgoing interface.
-		 * 
-		 * Otherwise assume faddr is reachable on a directly connected
-		 * network and try to find a corresponding interface to take
-		 * the source address from.
-		 */
-		if (ia == 0) {
-			bzero(&sa, sizeof(sa));
-			sa.sin_addr = faddr;
-			sa.sin_len = sizeof(sa);
-			sa.sin_family = AF_INET;
-
-			ia = ifatoia(ifa_ifwithdstaddr(sintosa(&sa)));
-			if (ia == 0)
-				ia = ifatoia(ifa_ifwithnet(sintosa(&sa)));
-			if (ia == 0)
-				return (ENETUNREACH);
-		}
-		/*
 		 * If the destination address is multicast and an outgoing
 		 * interface has been set as a multicast option, use the
 		 * address of that interface as our source address.
@@ -657,6 +637,26 @@
 					return (EADDRNOTAVAIL);
 			}
 		}
+		/*
+		 * If we found a route, use the address corresponding to
+		 * the outgoing interface.
+		 * 
+		 * Otherwise assume faddr is reachable on a directly connected
+		 * network and try to find a corresponding interface to take
+		 * the source address from.
+		 */
+		if (ia == 0) {
+			bzero(&sa, sizeof(sa));
+			sa.sin_addr = faddr;
+			sa.sin_len = sizeof(sa);
+			sa.sin_family = AF_INET;
+
+			ia = ifatoia(ifa_ifwithdstaddr(sintosa(&sa)));
+			if (ia == 0)
+				ia = ifatoia(ifa_ifwithnet(sintosa(&sa)));
+			if (ia == 0)
+				return (ENETUNREACH);
+		}
 		laddr = ia->ia_addr.sin_addr;
 	}
 

--------------010609050507030507010803--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?47889F53.4010605>