Index: in_mcast.c =================================================================== RCS file: /home/ncvs/src/sys/netinet/in_mcast.c,v retrieving revision 1.1 diff -u -p -r1.1 in_mcast.c --- in_mcast.c 12 Jun 2007 16:24:53 -0000 1.1 +++ in_mcast.c 3 Jul 2007 17:10:01 -0000 @@ -967,11 +967,40 @@ inp_join_group(struct inpcb *inp, struct 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; @@ -995,29 +1024,48 @@ inp_join_group(struct inpcb *inp, struct } /* - * Obtain ifp. If no interface address was provided, - * use the interface of the route to the given multicast - * address (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 + * this in order to allow groups to be joined when the routing + * table has not yet been populated during boot. + * + * If all of these conditions fail, return EADDRNOTAVAIL, and + * reject the IPv4 multicast join. */ - if (mreqs.imr_interface.s_addr != INADDR_ANY) { + if (ifp == NULL && mreqs.imr_interface.s_addr != INADDR_ANY) { INADDR_TO_IFP(mreqs.imr_interface, ifp); - } else { + } else if (ifp == NULL) { struct route ro; ro.ro_rt = NULL; *(struct sockaddr_in *)&ro.ro_dst = gsa->sin; rtalloc_ign(&ro, RTF_CLONING); - if (ro.ro_rt == NULL) { -#ifdef DIAGNOSTIC - printf("%s: no route to %s\n", __func__, - inet_ntoa(gsa->sin.sin_addr)); -#endif - return (EADDRNOTAVAIL); + if (ro.ro_rt != NULL) { + ifp = ro.ro_rt->rt_ifp; + KASSERT(ifp != NULL, ("%s: null ifp", + __func__)); + RTFREE(ro.ro_rt); + } else { + struct in_ifaddr *ia; + struct ifnet *mfp = NULL; + TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) { + mfp = ia->ia_ifp; + if (!(mfp->if_flags & IFF_LOOPBACK) && + (mfp->if_flags & IFF_MULTICAST)) { + ifp = mfp; + break; + } + } } - ifp = ro.ro_rt->rt_ifp; - KASSERT(ifp != NULL, ("%s: null ifp", __func__)); - RTFREE(ro.ro_rt); } + #ifdef DIAGNOSTIC if (bootverbose) { printf("%s: imr_interface = %s, ifp = %p\n", @@ -1207,12 +1255,42 @@ inp_leave_group(struct inpcb *inp, struc 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; @@ -1235,7 +1313,7 @@ inp_leave_group(struct inpcb *inp, struc 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