diff options
Diffstat (limited to 'sys/netinet6/udp6_output.c')
-rw-r--r-- | sys/netinet6/udp6_output.c | 64 |
1 files changed, 49 insertions, 15 deletions
diff --git a/sys/netinet6/udp6_output.c b/sys/netinet6/udp6_output.c index 933e3b3..0cd4cde 100644 --- a/sys/netinet6/udp6_output.c +++ b/sys/netinet6/udp6_output.c @@ -96,6 +96,7 @@ #include <netinet6/udp6_var.h> #include <netinet/icmp6.h> #include <netinet6/ip6protosw.h> +#include <netinet6/scope6_var.h> #ifdef IPSEC #include <netinet6/ipsec.h> @@ -128,6 +129,9 @@ udp6_output(in6p, m, addr6, control, td) struct ip6_hdr *ip6; struct udphdr *udp6; struct in6_addr *laddr, *faddr; + struct sockaddr_in6 *sin6 = NULL; + struct ifnet *oifp = NULL; + int scope_ambiguous = 0; u_short fport; int error = 0; struct ip6_pktopts *optp, opt; @@ -139,6 +143,29 @@ udp6_output(in6p, m, addr6, control, td) priv = 0; if (td && !suser(td)) priv = 1; + + if (addr6) { + /* addr6 has been validated in udp6_send(). */ + sin6 = (struct sockaddr_in6 *)addr6; + + /* protect *sin6 from overwrites */ + tmp = *sin6; + sin6 = &tmp; + + /* + * Application should provide a proper zone ID or the use of + * default zone IDs should be enabled. Unfortunately, some + * applications do not behave as it should, so we need a + * workaround. Even if an appropriate ID is not determined, + * we'll see if we can determine the outgoing interface. If we + * can, determine the zone ID based on the interface below. + */ + if (sin6->sin6_scope_id == 0 && !ip6_use_defzone) + scope_ambiguous = 1; + if ((error = sa6_embedscope(sin6, ip6_use_defzone)) != 0) + return (error); + } + if (control) { if ((error = ip6_setpktopts(control, &opt, in6p->in6p_outputopts, priv, IPPROTO_UDP)) != 0) @@ -147,7 +174,9 @@ udp6_output(in6p, m, addr6, control, td) } else optp = in6p->in6p_outputopts; - if (addr6) { + if (sin6) { + faddr = &sin6->sin6_addr; + /* * IPv4 version of udp_output calls in_pcbconnect in this case, * which needs splnet and affects performance. @@ -156,7 +185,6 @@ udp6_output(in6p, m, addr6, control, td) * and in6_pcbsetport in order to fill in the local address * and the local port. */ - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr6; if (sin6->sin6_port == 0) { error = EADDRNOTAVAIL; goto release; @@ -168,11 +196,6 @@ udp6_output(in6p, m, addr6, control, td) goto release; } - /* protect *sin6 from overwrites */ - tmp = *sin6; - sin6 = &tmp; - - faddr = &sin6->sin6_addr; fport = sin6->sin6_port; /* allow 0 port */ if (IN6_IS_ADDR_V4MAPPED(faddr)) { @@ -189,19 +212,30 @@ udp6_output(in6p, m, addr6, control, td) */ error = EINVAL; goto release; - } else - af = AF_INET; - } + } + if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) && + !IN6_IS_ADDR_V4MAPPED(&in6p->in6p_laddr)) { + /* + * when remote addr is an IPv4-mapped address, + * local addr should not be an IPv6 address, + * since you cannot determine how to map IPv6 + * source address to IPv4. + */ + error = EINVAL; + goto release; + } - /* KAME hack: embed scopeid */ - if (in6_embedscope(&sin6->sin6_addr, sin6, in6p, NULL) != 0) { - error = EINVAL; - goto release; + af = AF_INET; } if (!IN6_IS_ADDR_V4MAPPED(faddr)) { laddr = in6_selectsrc(sin6, optp, in6p->in6p_moptions, - NULL, &in6p->in6p_laddr, &error); + NULL, &in6p->in6p_laddr, &oifp, &error); + if (oifp && scope_ambiguous && + (error = in6_setscope(&sin6->sin6_addr, + oifp, NULL))) { + goto release; + } } else laddr = &in6p->in6p_laddr; /* XXX */ if (laddr == NULL) { |