diff options
author | Mike Belopuhov <mikeb@cvs.openbsd.org> | 2011-08-07 18:49:51 +0000 |
---|---|---|
committer | Mike Belopuhov <mikeb@cvs.openbsd.org> | 2011-08-07 18:49:51 +0000 |
commit | 74b2de56b668edd9a8c0c30c5b20bac341a8fe29 (patch) | |
tree | 94a5eee21d8e57d9e7ace52c7dde0602cd33ac89 /sys/netinet6 | |
parent | 9e4f82c84e899ab6779339bb8d4404fa8e4ff457 (diff) |
Several fixes for the IPV6_PKTINFO handling with sendmsg(2)
Verify that the address in the in6_pktinfo structure included
in the control message is unicast and configured on the local
host. Additional checks prevent from using non-routable
addresses and inactive interfaces.
Embed the scope identifier into the link local addresses as
required by the stack. Do not force users to provide valid
interface index in the ipi6_ifindex but look it up in place
if needed.
ok bluhm, waived by deraadt for the release.
Diffstat (limited to 'sys/netinet6')
-rw-r--r-- | sys/netinet6/in6_src.c | 81 |
1 files changed, 78 insertions, 3 deletions
diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c index e0cdfbf31eb..d4ba5a85272 100644 --- a/sys/netinet6/in6_src.c +++ b/sys/netinet6/in6_src.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in6_src.c,v 1.25 2010/05/07 13:33:17 claudio Exp $ */ +/* $OpenBSD: in6_src.c,v 1.26 2011/08/07 18:49:50 mikeb Exp $ */ /* $KAME: in6_src.c,v 1.36 2001/02/06 04:08:17 itojun Exp $ */ /* @@ -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 *dstsock, struct ip6_pktopts *opts, /* * 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) @@ -483,6 +514,50 @@ selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, } 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 in6_selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, struct ip6_moptions *mopts, struct route_in6 *ro, struct ifnet **retifp, struct rtentry **retrt) |