summaryrefslogtreecommitdiffstats
path: root/sys/netinet6/udp6_output.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet6/udp6_output.c')
-rw-r--r--sys/netinet6/udp6_output.c64
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) {
OpenPOWER on IntegriCloud