From owner-freebsd-net@FreeBSD.ORG Sat Jan 12 11:23:38 2008 Return-Path: Delivered-To: freebsd-net@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 10F4816A419 for ; Sat, 12 Jan 2008 11:23:38 +0000 (UTC) (envelope-from fli@shapeshifter.se) Received: from mx1.h3q.net (mx1.h3q.net [212.37.5.30]) by mx1.freebsd.org (Postfix) with ESMTP id 9213F13C447 for ; Sat, 12 Jan 2008 11:23:37 +0000 (UTC) (envelope-from fli@shapeshifter.se) Received: from [192.168.1.100] (78-69-107-234-no83.tbcn.telia.com [78.69.107.234]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) (Authenticated sender: fli@shapeshifter.se) by mx1.h3q.net (Postfix) with ESMTP id 066F078C20 for ; Sat, 12 Jan 2008 12:07:03 +0100 (CET) Message-ID: <47889F53.4010605@shapeshifter.se> Date: Sat, 12 Jan 2008 12:06:59 +0100 From: Fredrik Lindberg User-Agent: Thunderbird 2.0.0.9 (X11/20071129) MIME-Version: 1.0 To: freebsd-net@freebsd.org Content-Type: multipart/mixed; boundary="------------010609050507030507010803" X-Content-Filtered-By: Mailman/MimeDel 2.1.5 Subject: Unexpected multicast IPv4 socket behavior X-BeenThere: freebsd-net@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Networking and TCP/IP with FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 12 Jan 2008 11:23:38 -0000 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--