i've looked at it again and although it's a bit more involved,
it's better (it calles selectroute) than what i was going to
do by calling rtalloc in place in the other solution.

so just everyone understands, it works by figuring out the
destination interface (by looking at ipi6_ifindex or doing
an route lookup) and looking up the provided address in the
interface address tree.  if needed it embeds the scope from
the outgoing interface.  while doing that it does several
additional checks (route is available, address is unicast,
etc).

this fixes jeremy's test case and solves a bunch of problems
with assigning addresses that don't belong us.

opinions?

On Thu, Aug 04, 2011 at 23:29 +0200, Mike Belopuhov wrote:
> i saw what kame did.  be my guest and try to port that.
> 
> On Thu, Aug 4, 2011 at 11:23 PM, Alexander Bluhm
> <alexander.bl...@gmx.net> wrote:
> > On Thu, Aug 04, 2011 at 05:06:24PM +0200, Mike Belopuhov wrote:
> >> I'm not sure it's a desired behavior and afaik it's not possible
> >> to achieve this with IPv4 sockets without the need to be a root.
> >> Do we want to change that?
> >
> > Yes.  KAME fixed that, too.
> >
> >> The following change restricts it to the locally configured
> >> addresses.  Is it a way to go?
> >
> > I would prefer to take as much as possible from the KAME solution.
> > No need to introduce more differently implemented code.
> >
> > bluhm
> >

Index: in6_src.c
===================================================================
RCS file: /home/cvs/src/sys/netinet6/in6_src.c,v
retrieving revision 1.25
diff -u -p -u -p -r1.25 in6_src.c
--- in6_src.c   7 May 2010 13:33:17 -0000       1.25
+++ in6_src.c   5 Aug 2011 18:00:00 -0000
@@ -86,6 +86,8 @@
 #include <netinet6/ip6_var.h>
 #include <netinet6/nd6.h>
 
+int in6_selectif(struct sockaddr_in6 *, struct ip6_pktopts *,
+    struct ip6_moptions *, struct route_in6 *, struct ifnet **);
 int selectroute(struct sockaddr_in6 *, struct ip6_pktopts *,
     struct ip6_moptions *, struct route_in6 *, struct ifnet **,
     struct rtentry **, int);
@@ -110,11 +112,41 @@ in6_selectsrc(struct sockaddr_in6 *dstso
 
        /*
         * If the source address is explicitly specified by the caller,
-        * use it.
+        * check if the requested source address is indeed a unicast address
+        * assigned to the node, and can be used as the packet's source
+        * address.  If everything is okay, use the address as source.
         */
        if (opts && (pi = opts->ip6po_pktinfo) &&
-           !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr))
+           !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr)) {
+               struct ifnet *ifp = NULL;
+               struct sockaddr_in6 sa6;
+
+               /* get the outgoing interface */
+               if ((*errorp = in6_selectif(dstsock, opts, mopts, ro,
+                   &ifp)) != 0)
+                       return (NULL);
+
+               bzero(&sa6, sizeof(sa6));
+               sa6.sin6_family = AF_INET6;
+               sa6.sin6_len = sizeof(sa6);
+               sa6.sin6_addr = pi->ipi6_addr;
+
+               if (ifp && !(ifp->if_flags & IFF_LOOPBACK))
+                       sa6.sin6_addr.s6_addr16[1] =
+                           htons(in6_addr2scopeid(ifp, &sa6.sin6_addr));
+
+               ia6 = (struct in6_ifaddr *)
+                   ifa_ifwithaddr((struct sockaddr *)&sa6, 0);
+               if (ia6 == NULL ||
+                   (ia6->ia6_flags & (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) {
+                       *errorp = EADDRNOTAVAIL;
+                       return (NULL);
+               }
+
+               pi->ipi6_addr = sa6.sin6_addr; /* XXX: this overrides pi */
+
                return (&pi->ipi6_addr);
+       }
 
        /*
         * If the source address is not specified but the socket(if any)
@@ -480,6 +512,51 @@ selectroute(struct sockaddr_in6 *dstsock
                *retrt = rt;    /* rt may be NULL */
 
        return (error);
+}
+
+int
+in6_selectif(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
+    struct ip6_moptions *mopts, struct route_in6 *ro, struct ifnet **retifp)
+{
+       int error, clone;
+       struct rtentry *rt = NULL;
+
+       clone = IN6_IS_ADDR_MULTICAST(&dstsock->sin6_addr) ? 0 : 1;
+       if ((error = selectroute(dstsock, opts, mopts, ro, retifp,
+           &rt, clone)) != 0)
+               return (error);
+
+       /*
+        * do not use a rejected or black hole route.
+        * XXX: this check should be done in the L2 output routine.
+        * However, if we skipped this check here, we'd see the following
+        * scenario:
+        * - install a rejected route for a scoped address prefix
+        *   (like fe80::/10)
+        * - send a packet to a destination that matches the scoped prefix,
+        *   with ambiguity about the scope zone.
+        * - pick the outgoing interface from the route, and disambiguate the
+        *   scope zone with the interface.
+        * - ip6_output() would try to get another route with the "new"
+        *   destination, which may be valid.
+        * - we'd see no error on output.
+        * Although this may not be very harmful, it should still be confusing.
+        * We thus reject the case here.
+        */
+       if (rt && (rt->rt_flags & (RTF_REJECT | RTF_BLACKHOLE)))
+               return (rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
+
+       /*
+        * Adjust the "outgoing" interface.  If we're going to loop the packet
+        * back to ourselves, the ifp would be the loopback interface.
+        * However, we'd rather know the interface associated to the
+        * destination address (which should probably be one of our own
+        * addresses.)
+        */
+       if (rt && rt->rt_ifa && rt->rt_ifa->ifa_ifp)
+               *retifp = rt->rt_ifa->ifa_ifp;
+
+       return (0);
 }
 
 int

Reply via email to