From owner-freebsd-current@FreeBSD.ORG Sat Dec 8 19:15:48 2007 Return-Path: Delivered-To: current@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 2E73816A46B for ; Sat, 8 Dec 2007 19:15:48 +0000 (UTC) (envelope-from joemann@beefree.free.de) Received: from beefree.free.de (offshorenew.free.de [194.77.83.23]) by mx1.freebsd.org (Postfix) with ESMTP id C7EDF13C45B for ; Sat, 8 Dec 2007 19:15:45 +0000 (UTC) (envelope-from joemann@beefree.free.de) Received: (qmail 14033 invoked from network); 8 Dec 2007 19:42:53 +0100 Date: Sat, 8 Dec 2007 19:42:48 +0100 From: Johannes 5 Joemann To: "Bruce M. Simpson" Message-Id: <20071208194248.5cfcf0fd.joemann@beefree.free.de> In-Reply-To: <468A84FA.2090703@FreeBSD.org> References: <468A5B9D.6030401@FreeBSD.org> <468A84FA.2090703@FreeBSD.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="Multipart=_Sat__8_Dec_2007_19_42_48_+0100_/Fu51QzRhA5OoiZa" X-Mailman-Approved-At: Sun, 09 Dec 2007 05:19:38 +0000 Cc: boris@tagnet.ru, current@freebsd.org Subject: Re: Multicast problems [PATCH] X-BeenThere: freebsd-current@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Discussions about the use of FreeBSD-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 08 Dec 2007 19:15:48 -0000 This is a multi-part message in MIME format. --Multipart=_Sat__8_Dec_2007_19_42_48_+0100_/Fu51QzRhA5OoiZa Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Hi! On Tue, 03 Jul 2007 18:18:50 +0100 "Bruce M. Simpson" wrote: > Bruce M. Simpson wrote: > > I see now that Linux also supports ip_mreqn in its > > IP_ADD_MEMBERSHIP path. I could potentially change the ASM API > > ioctl paths (IP_ADD_MEMBERSHIP, IP_DEL_MEMBERSHIP) to detect and > > support the ip_mreqn structure -- however -- I am loathe to do > > this as it introduces another bunch of nested conditionals, as > > the same code now has to support IP_ADD_SOURCE_MEMBERSHIP in > > FreeBSD, which has the same structure size. It is also a > > retrograde change. > > I have attached a diff which emulates the Linux ip_mreqn kludge in > the IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP paths; this includes > the changes from the previous patch to workaround the non-existence > of a default route on boot. Thanx! > I do not plan to commit it at the moment and have not tested it. I tested your patch [1] on 7.0-BETA4 in order to make quagga's ospfd functional again (over here it is multicasting on 5 interfaces (3 of them point-to-point) - without the fix it only works on one interface). Your patch solves the problem that if quagga discovers ip_mreqn it also uses it for IP_ADD_MEMBERSHIP. Although you consider your patch to in_mcast.c to be a retrograde kludge, it made my day:-) For the convenience of other 7.0 users being lost in the Bermuda Triangle between RFC1724, Linuxisms, and RFC3678 I've attached a trivial modification of your patch, which makes it apply to the RELENG_7 version 1.3.2.1 of src/sys/netinet/in_mcast.c. > The right thing for applications to do is to use the RFC 3678 API Meanwhile users of ospfd might survive even without your kernel patch as outlined in [2]. I've attached that patch suggestion as a drop in for ports/net/quagga/files. It worked for me with an unpatched (and with a patched) 7.0-BETA4 kernel. > if they need to join an interface by index, the legacy ASM API can > only be relied upon if interfaces are assigned IPv4 addresses, and > it breaks for point-to-point because of legacy BSD behaviour. As long as FreeBSD supplies ip_mreqn in src/sys/netinet/in.h it seems reasonable to support associated operations like IP_ADD_MEMBERSHIP. It's certainly better to eradicate kludges and go with RFC3678, but meanwhile I'd support your patch being committed to RELENG_7 in order to make the ip_mreqn kludge smooth for production. (This is just a naive comment from a humble user who's not in the multicast business.-) Thanx for your work! Johannes ps I've Cc:ed the quagga maintainer (I hope that's OK) as I consider it undesirable to have 7.0 shipped with a broken quagga-ospfd. Whether this should be fixed in the kernel or the port (or both:) is another question, but something should happen ... [1] [2] --Multipart=_Sat__8_Dec_2007_19_42_48_+0100_/Fu51QzRhA5OoiZa Content-Type: text/x-diff; name="sys-netinet-in_mcast.c.patch" Content-Disposition: attachment; filename="sys-netinet-in_mcast.c.patch" Content-Transfer-Encoding: 7bit --- sys/netinet/in_mcast.c.orig 2007-11-30 18:01:05.000000000 +0100 +++ sys/netinet/in_mcast.c 2007-12-07 21:34:28.000000000 +0100 @@ -980,11 +980,40 @@ struct ip_mreq_source mreqs; if (sopt->sopt_name == IP_ADD_MEMBERSHIP) { - error = sooptcopyin(sopt, &mreqs, - sizeof(struct ip_mreq), - sizeof(struct ip_mreq)); /* - * Do argument switcharoo from ip_mreq into + * Handle the case where a struct ip_mreqn + * was passed to the IP_ADD_MEMBERSHIP ioctl to + * specify an interface index. This is an + * undocumented modification of the IPv4 ASM API + * obtained from Linux. + */ + if (sopt->sopt_valsize == sizeof(struct ip_mreqn)) { + struct ip_mreqn *mreqn = + (struct ip_mreqn *)&mreqs; + + error = sooptcopyin(sopt, mreqn, + sizeof(struct ip_mreqn), + sizeof(struct ip_mreqn)); + if (error) + return (error); + + if (mreqn->imr_ifindex < 0 || + if_index < mreqn->imr_ifindex) + return (EINVAL); + if (mreqn->imr_ifindex == 0) { + ifp = NULL; + } else { + ifp = ifnet_byindex(mreqn->imr_ifindex); + if (ifp == NULL) + return (EADDRNOTAVAIL); + } + } else { + error = sooptcopyin(sopt, &mreqs, + sizeof(struct ip_mreq), + sizeof(struct ip_mreq)); + } + /* + * Do argument switcharoo from ip_mreq[n] into * ip_mreq_source to avoid using two instances. */ mreqs.imr_interface = mreqs.imr_sourceaddr; @@ -1008,10 +1037,12 @@ } /* - * Obtain ifp. If no interface address was provided, - * use the interface of the route in the unicast FIB for - * the given multicast destination; usually, this is the - * default route. + * If ifp was not already overridden, obtain it. + * + * If no interface address was provided, use the interface + * of the route in the unicast FIB for the given multicast + * destination; usually, this is the default route. + * * If this lookup fails, attempt to use the first non-loopback * interface with multicast capability in the system as a * last resort. The legacy IPv4 ASM API requires that we do @@ -1020,9 +1051,9 @@ * If all of these conditions fail, return EADDRNOTAVAIL, and * reject the IPv4 multicast join. */ - if (mreqs.imr_interface.s_addr != INADDR_ANY) { - ifp = ip_multicast_if(&mreqs.imr_interface); - } else { + if (ifp == NULL && mreqs.imr_interface.s_addr != INADDR_ANY) { + INADDR_TO_IFP(mreqs.imr_interface, ifp); + } else if (ifp == NULL) { struct route ro; ro.ro_rt = NULL; @@ -1235,12 +1266,42 @@ case IP_DROP_MEMBERSHIP: case IP_DROP_SOURCE_MEMBERSHIP: if (sopt->sopt_name == IP_DROP_MEMBERSHIP) { - error = sooptcopyin(sopt, &mreqs, - sizeof(struct ip_mreq), - sizeof(struct ip_mreq)); + /* + * Handle the case where a struct ip_mreqn + * was passed to the IP_DROP_MEMBERSHIP ioctl to + * specify an interface index. This is an + * undocumented modification of the IPv4 ASM API + * obtained from Linux. + */ + if (sopt->sopt_valsize == sizeof(struct ip_mreqn)) { + struct ip_mreqn *mreqn = + (struct ip_mreqn *)&mreqs; + + error = sooptcopyin(sopt, mreqn, + sizeof(struct ip_mreqn), + sizeof(struct ip_mreqn)); + if (error) + return (error); + + if (mreqn->imr_ifindex < 0 || + if_index < mreqn->imr_ifindex) + return (EINVAL); + if (mreqn->imr_ifindex == 0) { + ifp = NULL; + } else { + ifp = ifnet_byindex(mreqn->imr_ifindex); + if (ifp == NULL) + return (EADDRNOTAVAIL); + } + } else { + error = sooptcopyin(sopt, &mreqs, + sizeof(struct ip_mreq), + sizeof(struct ip_mreq)); + } + /* * Swap interface and sourceaddr arguments, - * as ip_mreq and ip_mreq_source are laid + * as ip_mreq[n] and ip_mreq_source are laid * out differently. */ mreqs.imr_interface = mreqs.imr_sourceaddr; @@ -1263,7 +1324,7 @@ ssa->sin.sin_addr = mreqs.imr_sourceaddr; } - if (gsa->sin.sin_addr.s_addr != INADDR_ANY) + if (ifp == NULL && gsa->sin.sin_addr.s_addr != INADDR_ANY) INADDR_TO_IFP(mreqs.imr_interface, ifp); #ifdef DIAGNOSTIC --Multipart=_Sat__8_Dec_2007_19_42_48_+0100_/Fu51QzRhA5OoiZa Content-Type: text/x-diff; name="patch-lib_sockopt.c" Content-Disposition: attachment; filename="patch-lib_sockopt.c" Content-Transfer-Encoding: 7bit --- lib/sockopt.c.orig 2007-08-22 18:22:54.000000000 +0200 +++ lib/sockopt.c 2007-12-07 20:46:35.000000000 +0100 @@ -211,70 +211,12 @@ used instead of if_addr */) { -#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX - /* This is better because it uses ifindex directly */ - struct ip_mreqn mreqn; - int ret; - - switch (optname) - { - case IP_MULTICAST_IF: - case IP_ADD_MEMBERSHIP: - case IP_DROP_MEMBERSHIP: - memset (&mreqn, 0, sizeof(mreqn)); - - if (mcast_addr) - mreqn.imr_multiaddr.s_addr = mcast_addr; - - if (ifindex) - mreqn.imr_ifindex = ifindex; - else - mreqn.imr_address = if_addr; - - ret = setsockopt(sock, IPPROTO_IP, optname, - (void *)&mreqn, sizeof(mreqn)); - if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE)) - { - /* see above: handle possible problem when interface comes back up */ - char buf[2][INET_ADDRSTRLEN]; - zlog_info("setsockopt_multicast_ipv4 attempting to drop and " - "re-add (fd %d, ifaddr %s, mcast %s, ifindex %u)", - sock, - inet_ntop(AF_INET, &if_addr, buf[0], sizeof(buf[0])), - inet_ntop(AF_INET, &mreqn.imr_multiaddr, - buf[1], sizeof(buf[1])), ifindex); - setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, - (void *)&mreqn, sizeof(mreqn)); - ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, - (void *)&mreqn, sizeof(mreqn)); - } - return ret; - break; - - default: - /* Can out and give an understandable error */ - errno = EINVAL; - return -1; - break; - } - - /* Example defines for another OS, boilerplate off other code in this - function, AND handle optname as per other sections for consistency !! */ - /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */ - /* Add your favourite OS here! */ - -#else /* #if OS_TYPE */ /* standard BSD API */ struct in_addr m; struct ip_mreq mreq; int ret; -#ifdef HAVE_BSD_STRUCT_IP_MREQ_HACK - if (ifindex) - m.s_addr = htonl(ifindex); - else -#endif m = if_addr; switch (optname) @@ -314,7 +256,6 @@ return -1; break; } -#endif /* #if OS_TYPE */ } --Multipart=_Sat__8_Dec_2007_19_42_48_+0100_/Fu51QzRhA5OoiZa--