the system is inconsistent wrt to tunnels and ipv6 link local
addresses.
there are three things to consider here:
1. configuring addresses on a tunnel
2. using addresses to encapsulate traffic
3. reading the addresses a tunnel uses
the ioctls used for 1 and 3 use sockaddr_in6 to pass v6 addresses
in and out of the kernel for tunnel configuration. sin6 structures
have a sin6_scope_id member that is supposed to represent the scope.
i get the impression sin6_scope_id wasnt always there thoughway.
kame had (has) a hack where it embeds the scope id into bits that
are guaranteed to be zero.
both ifconfig and the kernel have code to pack sin6_scope_id into
a v6 address, and to unpack it later.
currently 1 uses getaddrinfo to parse an address on the command
line and submit it to the kernel. getaddrinfo reads scope (eg, %ix0)
and puts it into sin6_scope_id.
for some reason 3 currently assumes that needs to unpack a kame
packed address and. that means it reads from the "zero" bits in the
address into sin6_scope_id.
1 and 3 are therefore inconsistent. 1 submits a nice neat sin6_scope_id
to the kernel, but 3 assumes its a kame packed address.
to resolve this im suggesting we pick 1 as the right way to do
things. sin6_scope_id is portable, and less confusing. i havent met
anyone who likes the kame hack.
for 2, ipv6 packets end up in ip6_output, and ip6_output relies on
scope embedded into link local addresses. at the moment there are
3 tunnel interfaces that let you use v6 addresses for the enpoints:
gif, etherip, and vxlan. they all use the sockaddr_sin6 from userland
to fill in a v6 header, but none of them use the scope id.
the diff below should fix all this. the most important fix is vxlan.
ok?
Index: sbin/ifconfig/ifconfig.c
===================================================================
RCS file: /cvs/src/sbin/ifconfig/ifconfig.c,v
retrieving revision 1.333
diff -u -p -r1.333 ifconfig.c
--- sbin/ifconfig/ifconfig.c 10 Nov 2016 14:36:03 -0000 1.333
+++ sbin/ifconfig/ifconfig.c 12 Dec 2016 09:44:48 -0000
@@ -2873,8 +2873,6 @@ phys_status(int force)
(void) strlcpy(req.iflr_name, name, sizeof(req.iflr_name));
if (ioctl(s, SIOCGLIFPHYADDR, (caddr_t)&req) < 0)
return;
- if (req.addr.ss_family == AF_INET6)
- in6_fillscopeid((struct sockaddr_in6 *)&req.addr);
if (getnameinfo((struct sockaddr *)&req.addr, req.addr.ss_len,
psrcaddr, sizeof(psrcaddr), 0, 0, niflag) != 0)
strlcpy(psrcaddr, "<error>", sizeof(psrcaddr));
@@ -2883,10 +2881,8 @@ phys_status(int force)
if (req.dstaddr.ss_family == AF_INET)
dstport = ((struct sockaddr_in *)&req.dstaddr)->sin_port;
- else if (req.dstaddr.ss_family == AF_INET6) {
- in6_fillscopeid((struct sockaddr_in6 *)&req.dstaddr);
+ else if (req.dstaddr.ss_family == AF_INET6)
dstport = ((struct sockaddr_in6 *)&req.dstaddr)->sin6_port;
- }
if (getnameinfo((struct sockaddr *)&req.dstaddr, req.dstaddr.ss_len,
pdstaddr, sizeof(pdstaddr), 0, 0, niflag) != 0)
strlcpy(pdstaddr, "<error>", sizeof(pdstaddr));
Index: sys/net/if_etherip.c
===================================================================
RCS file: /cvs/src/sys/net/if_etherip.c,v
retrieving revision 1.7
diff -u -p -r1.7 if_etherip.c
--- sys/net/if_etherip.c 13 Apr 2016 11:41:15 -0000 1.7
+++ sys/net/if_etherip.c 12 Dec 2016 09:44:48 -0000
@@ -514,18 +514,19 @@ ip6_etherip_output(struct ifnet *ifp, st
struct sockaddr_in6 *src, *dst;
struct etherip_header *eip;
struct ip6_hdr *ip6;
+ int error;
src = (struct sockaddr_in6 *)&sc->sc_src;
dst = (struct sockaddr_in6 *)&sc->sc_dst;
if (src == NULL || dst == NULL ||
src->sin6_family != AF_INET6 || dst->sin6_family != AF_INET6) {
- m_freem(m);
- return EAFNOSUPPORT;
+ error = EAFNOSUPPORT;
+ goto drop;
}
if (IN6_IS_ADDR_UNSPECIFIED(&dst->sin6_addr)) {
- m_freem(m);
- return ENETUNREACH;
+ error = ENETUNREACH;
+ goto drop;
}
m->m_flags &= ~(M_BCAST|M_MCAST);
@@ -552,8 +553,12 @@ ip6_etherip_output(struct ifnet *ifp, st
ip6->ip6_nxt = IPPROTO_ETHERIP;
ip6->ip6_hlim = ip6_defhlim;
ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
- ip6->ip6_src = src->sin6_addr;
- ip6->ip6_dst = dst->sin6_addr;
+ error = in6_embedscope(&ip6->ip6_src, src, NULL);
+ if (error != 0)
+ goto drop;
+ error = in6_embedscope(&ip6->ip6_dst, dst, NULL);
+ if (error != 0)
+ goto drop;
m->m_pkthdr.ph_rtableid = sc->sc_rdomain;
@@ -565,6 +570,10 @@ ip6_etherip_output(struct ifnet *ifp, st
(sizeof(struct ip6_hdr) + sizeof(struct etherip_header)));
return ip6_output(m, 0, NULL, IPV6_MINMTU, 0, NULL);
+
+drop:
+ m_freem(m);
+ return (error);
}
int
Index: sys/net/if_vxlan.c
===================================================================
RCS file: /cvs/src/sys/net/if_vxlan.c,v
retrieving revision 1.50
diff -u -p -r1.50 if_vxlan.c
--- sys/net/if_vxlan.c 14 Oct 2016 10:25:02 -0000 1.50
+++ sys/net/if_vxlan.c 12 Dec 2016 09:44:48 -0000
@@ -724,7 +724,6 @@ vxlan_encap6(struct ifnet *ifp, struct m
struct vxlan_softc *sc = (struct vxlan_softc *)ifp->if_softc;
struct ip6_hdr *ip6;
struct in6_addr *in6a;
- int error;
M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
if (m == NULL)
@@ -736,8 +735,10 @@ vxlan_encap6(struct ifnet *ifp, struct m
ip6->ip6_vfc |= IPV6_VERSION;
ip6->ip6_nxt = IPPROTO_UDP;
ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
- ip6->ip6_src = satosin6(src)->sin6_addr;
- ip6->ip6_dst = satosin6(dst)->sin6_addr;
+ if (in6_embedscope(&ip6->ip6_src, satosin6(src), NULL) != 0)
+ goto drop;
+ if (in6_embedscope(&ip6->ip6_dst, satosin6(dst), NULL) != 0)
+ goto drop;
if (sc->sc_ttl > 0)
ip6->ip6_hlim = sc->sc_ttl;
@@ -745,12 +746,10 @@ vxlan_encap6(struct ifnet *ifp, struct m
ip6->ip6_hlim = ip6_defhlim;
if (IN6_IS_ADDR_UNSPECIFIED(&satosin6(src)->sin6_addr)) {
- error = in6_selectsrc(&in6a, satosin6(dst), NULL, NULL,
- sc->sc_rdomain);
- if (error != 0) {
- m_freem(m);
- return (NULL);
- }
+ if (in6_selectsrc(&in6a, satosin6(dst), NULL, NULL,
+ sc->sc_rdomain) != 0)
+ goto drop;
+
ip6->ip6_src = *in6a;
}
@@ -763,6 +762,10 @@ vxlan_encap6(struct ifnet *ifp, struct m
m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
return (m);
+
+drop:
+ m_freem(m);
+ return (NULL);
}
int