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