On Sat, Aug 06, 2011 at 20:05 +0200, Alexander Bluhm wrote:
> On Fri, Aug 05, 2011 at 08:19:21PM +0200, Mike Belopuhov wrote:
> > opinions?
> 
> Although there are other opinions, I think we should not reinvent
> the wheel and use KAME where apropiate.  So we should go this way.
> 
> > +           if (ifp && !(ifp->if_flags & IFF_LOOPBACK))
> > +                   sa6.sin6_addr.s6_addr16[1] =
> > +                       htons(in6_addr2scopeid(ifp, &sa6.sin6_addr));
> 
> I think this sets the scope id also for global addresses.

no, it sets it only for IPV6_ADDR_SCOPE_INTFACELOCAL and
IPV6_ADDR_SCOPE_LINKLOCAL.

> Should this be more like the code in ip6_input()?
> 
>               if (ifp && IN6_IS_SCOPE_EMBED(&sa6.sin6_addr))
>                       sa6.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
> 

i think it's essentially the same, except that i don't need
to special case the loopback perhaps...

> > +           pi->ipi6_addr = sa6.sin6_addr; /* XXX: this overrides pi */
> > +
> >             return (&pi->ipi6_addr);
> 
> Do you know, why KAME returns &ia6->ia_addr.sin6_addr?
> It is more conservative to keep the code we have, so that is fine.
> 

it doesn't matter what to return, they're equal.

> > +   clone = IN6_IS_ADDR_MULTICAST(&dstsock->sin6_addr) ? 0 : 1;
> > +   if ((error = selectroute(dstsock, opts, mopts, ro, retifp,
> > +       &rt, clone)) != 0)
> 
> Our selectroute() function is missing the clone paramter, not the
> norouteok.  So you should pass 1 as last argument.
> 

true.

> bluhm

updated diff

Index: in6_src.c
===================================================================
RCS file: /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   7 Aug 2011 15:51:30 -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,40 @@ 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 && IN6_IS_SCOPE_EMBED(&sa6.sin6_addr))
+                       sa6.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+
+               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 +511,50 @@ 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)
+{
+       struct rtentry *rt = NULL;
+       int error;
+
+       if ((error = selectroute(dstsock, opts, mopts, ro, retifp,
+           &rt, 1)) != 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