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


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;
 	}
 
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-net
To unsubscribe, send any mail to "[EMAIL PROTECTED]"

Reply via email to