Index: sbin/route/keywords =================================================================== --- sbin/route/keywords +++ sbin/route/keywords @@ -14,6 +14,7 @@ fib flush gateway +gateway6 genmask get host Index: sbin/route/route.c =================================================================== --- sbin/route/route.c +++ sbin/route/route.c @@ -786,6 +786,7 @@ #define F_FORCEHOST 0x04 #define F_PROXY 0x08 #define F_INTERFACE 0x10 +#define F_GATEWAY6 0x20 static void newroute(int argc, char **argv) @@ -912,6 +913,13 @@ getaddr(RTAX_GATEWAY, *++argv, 0, nrflags); gateway = *argv; break; + case K_GATEWAY6: + if (!--argc) + usage(NULL); + nrflags |= F_GATEWAY6; + getaddr(RTAX_GATEWAY, *++argv, 0, nrflags); + gateway = *argv; + break; case K_DST: if (!--argc) usage(NULL); @@ -1212,8 +1220,16 @@ #endif rtm_addrs |= (1 << idx); sa = (struct sockaddr *)&so[idx]; - sa->sa_family = af; - sa->sa_len = aflen; +#ifdef INET6 + if ((nrflags & F_GATEWAY6) && idx == RTAX_GATEWAY) { + sa->sa_family = AF_INET6; + sa->sa_len = sizeof(struct sockaddr_in6); + } else +#endif + { + sa->sa_family = af; + sa->sa_len = aflen; + } switch (idx) { case RTAX_GATEWAY: Index: sys/net/if_ethersubr.c =================================================================== --- sys/net/if_ethersubr.c +++ sys/net/if_ethersubr.c @@ -246,6 +246,18 @@ memcpy(&eh->ether_type, &etype, sizeof(etype)); memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); } + /* + * Keep M_ETHER_RFC5549 flag if nd6_resolve() has failed + * to do lookup. It may hold a chain of mbufs, and then if + * queue becomes overfilled, it could generate ICMPv6 error + * for dropped packets. But ICMPv6 code expects IPv6 packet + * in a mbuf. With the flag we can avoid ICMPv6 handling. + */ + if (error == 0 && (m->m_flags & M_ETHER_RFC5549) != 0) { + m->m_flags &= ~M_ETHER_RFC5549; + etype = htons(ETHERTYPE_IP); + memcpy(&eh->ether_type, &etype, sizeof(etype)); + } break; #endif default: Index: sys/net/route.h =================================================================== --- sys/net/route.h +++ sys/net/route.h @@ -210,6 +210,7 @@ #define NHF_DEFAULT 0x0080 /* Default route */ #define NHF_BROADCAST 0x0100 /* RTF_BROADCAST */ #define NHF_GATEWAY 0x0200 /* RTF_GATEWAY */ +#define NHF_RFC5549 0x0400 /* next hop conforms to RFC5549 */ /* Nexthop request flags */ #define NHR_IFAIF 0x01 /* Return ifa_ifp interface */ Index: sys/net/route.c =================================================================== --- sys/net/route.c +++ sys/net/route.c @@ -1585,8 +1585,11 @@ case RTM_ADD: if ((flags & RTF_GATEWAY) && !gateway) return (EINVAL); - if (dst && gateway && (dst->sa_family != gateway->sa_family) && - (gateway->sa_family != AF_UNSPEC) && (gateway->sa_family != AF_LINK)) + if (dst && gateway && + (dst->sa_family != gateway->sa_family) && + (gateway->sa_family != AF_UNSPEC) && + (gateway->sa_family != AF_LINK) && + (gateway->sa_family != AF_INET6)) return (EINVAL); if (info->rti_ifa == NULL) { Index: sys/netinet/in_fib.h =================================================================== --- sys/netinet/in_fib.h +++ sys/netinet/in_fib.h @@ -37,7 +37,15 @@ struct ifnet *nh_ifp; /* Logical egress interface */ uint16_t nh_mtu; /* nexthop mtu */ uint16_t nh_flags; /* nhop flags */ - struct in_addr nh_addr; /* GW/DST IPv4 address */ + uint8_t spare[4]; + union { /* GW/DST IPv4 address */ + struct in_addr nh_addr; +#ifdef INET6 + struct in6_addr nh_addr6; +#else + uint8_t nh_addr6[16]; +#endif + }; }; /* Extended nexthop info used for control protocols */ @@ -46,9 +54,15 @@ uint16_t nh_mtu; /* nexthop mtu */ uint16_t nh_flags; /* nhop flags */ uint8_t spare[4]; - struct in_addr nh_addr; /* GW/DST IPv4 address */ struct in_addr nh_src; /* default source IPv4 address */ - uint64_t spare2[2]; + union { /* GW/DST IPv4 address */ + struct in_addr nh_addr; +#ifdef INET6 + struct in6_addr nh_addr6; +#else + uint8_t nh_addr6[16]; +#endif + }; }; int fib4_lookup_nh_basic(uint32_t fibnum, struct in_addr dst, uint32_t flags, Index: sys/netinet/in_fib.c =================================================================== --- sys/netinet/in_fib.c +++ sys/netinet/in_fib.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); #include "opt_inet.h" +#include "opt_inet6.h" #include "opt_route.h" #include "opt_mpath.h" @@ -58,6 +59,9 @@ #include #include #include +#ifdef INET6 +#include +#endif #ifdef INET static void fib4_rte_to_nh_basic(struct rtentry *rte, struct in_addr dst, @@ -71,22 +75,32 @@ fib4_rte_to_nh_basic(struct rtentry *rte, struct in_addr dst, uint32_t flags, struct nhop4_basic *pnh4) { - struct sockaddr_in *gw; + struct sockaddr *gw; if ((flags & NHR_IFAIF) != 0) pnh4->nh_ifp = rte->rt_ifa->ifa_ifp; else pnh4->nh_ifp = rte->rt_ifp; pnh4->nh_mtu = min(rte->rt_mtu, rte->rt_ifp->if_mtu); + pnh4->nh_flags = fib_rte_to_nh_flags(rte->rt_flags); if (rte->rt_flags & RTF_GATEWAY) { - gw = (struct sockaddr_in *)rte->rt_gateway; - pnh4->nh_addr = gw->sin_addr; + gw = rte->rt_gateway; + MPASS(gw->sa_family == AF_INET || + gw->sa_family == AF_INET6); +#ifdef INET6 + if (gw->sa_family == AF_INET6) { + pnh4->nh_flags |= NHF_RFC5549; + pnh4->nh_addr6 = satosin6(gw)->sin6_addr; + in6_clearscope(&pnh4->nh_addr6); /* XXXAE */ + } else +#endif + { + pnh4->nh_addr = satosin(gw)->sin_addr; + } } else pnh4->nh_addr = dst; - /* Set flags */ - pnh4->nh_flags = fib_rte_to_nh_flags(rte->rt_flags); - gw = (struct sockaddr_in *)rt_key(rte); - if (gw->sin_addr.s_addr == 0) + gw = rt_key(rte); + if (satosin(gw)->sin_addr.s_addr == INADDR_ANY) pnh4->nh_flags |= NHF_DEFAULT; /* TODO: Handle RTF_BROADCAST here */ } @@ -95,7 +109,7 @@ fib4_rte_to_nh_extended(struct rtentry *rte, struct in_addr dst, uint32_t flags, struct nhop4_extended *pnh4) { - struct sockaddr_in *gw; + struct sockaddr *gw; struct in_ifaddr *ia; if ((flags & NHR_IFAIF) != 0) @@ -103,15 +117,25 @@ else pnh4->nh_ifp = rte->rt_ifp; pnh4->nh_mtu = min(rte->rt_mtu, rte->rt_ifp->if_mtu); + pnh4->nh_flags = fib_rte_to_nh_flags(rte->rt_flags); if (rte->rt_flags & RTF_GATEWAY) { - gw = (struct sockaddr_in *)rte->rt_gateway; - pnh4->nh_addr = gw->sin_addr; + gw = rte->rt_gateway; + MPASS(gw->sa_family == AF_INET || + gw->sa_family == AF_INET6); +#ifdef INET6 + if (gw->sa_family == AF_INET6) { + pnh4->nh_flags |= NHF_RFC5549; + pnh4->nh_addr6 = satosin6(gw)->sin6_addr; + in6_clearscope(&pnh4->nh_addr6); /* XXXAE */ + } else +#endif + { + pnh4->nh_addr = satosin(gw)->sin_addr; + } } else pnh4->nh_addr = dst; - /* Set flags */ - pnh4->nh_flags = fib_rte_to_nh_flags(rte->rt_flags); - gw = (struct sockaddr_in *)rt_key(rte); - if (gw->sin_addr.s_addr == 0) + gw = rt_key(rte); + if (satosin(gw)->sin_addr.s_addr == INADDR_ANY) pnh4->nh_flags |= NHF_DEFAULT; /* XXX: Set RTF_BROADCAST if GW address is broadcast */ Index: sys/netinet/ip_fastfwd.c =================================================================== --- sys/netinet/ip_fastfwd.c +++ sys/netinet/ip_fastfwd.c @@ -79,6 +79,7 @@ __FBSDID("$FreeBSD$"); #include "opt_ipstealth.h" +#include "opt_inet6.h" #include #include @@ -152,7 +153,13 @@ struct ip *ip; struct mbuf *m0 = NULL; struct nhop4_basic nh; - struct sockaddr_in dst; + union { + struct sockaddr sa; + struct sockaddr_in sin; +#ifdef INET6 + struct sockaddr_in6 sin6; +#endif + } dst; struct in_addr dest, odest, rtdest; uint16_t ip_len, ip_off; int error = 0; @@ -376,9 +383,21 @@ ip_off = ntohs(ip->ip_off); bzero(&dst, sizeof(dst)); - dst.sin_family = AF_INET; - dst.sin_len = sizeof(dst); - dst.sin_addr = nh.nh_addr; +#ifdef INET6 + if (nh.nh_flags & NHF_RFC5549) { + dst.sin6.sin6_family = AF_INET6; + dst.sin6.sin6_len = sizeof(dst.sin6); + dst.sin6.sin6_addr = nh.nh_addr6; + if (IN6_IS_SCOPE_LINKLOCAL(&nh.nh_addr6)) + dst.sin6.sin6_addr.s6_addr16[1] = + htons(nh.nh_ifp->if_index & 0xffff); + } else +#endif + { + dst.sin.sin_family = AF_INET; + dst.sin.sin_len = sizeof(dst.sin); + dst.sin.sin_addr = nh.nh_addr; + } /* * Check if packet fits MTU or if hardware will fragment for us @@ -388,15 +407,23 @@ * Avoid confusing lower layers. */ m_clrprotoflags(m); +#ifdef INET6 /* + * Notify ethernet layer that we need change ethertype + * for RFC5549 case. + */ + if (nh.nh_flags & NHF_RFC5549) + m->m_flags |= M_ETHER_RFC5549; +#endif + /* * Send off the packet via outgoing interface */ IP_PROBE(send, NULL, NULL, ip, nh.nh_ifp, ip, NULL); - error = (*nh.nh_ifp->if_output)(nh.nh_ifp, m, - (struct sockaddr *)&dst, NULL); + error = (*nh.nh_ifp->if_output)(nh.nh_ifp, m, &dst.sa, NULL); } else { /* - * Handle EMSGSIZE with icmp reply needfrag for TCP MTU discovery + * Handle EMSGSIZE with icmp reply needfrag for TCP MTU + * discovery. */ if (ip_off & IP_DF) { IPSTAT_INC(ips_cantfrag); @@ -423,13 +450,20 @@ * Avoid confusing lower layers. */ m_clrprotoflags(m); - +#ifdef INET6 + /* + * Notify ethernet layer that we need + * change ethertype for RFC5549 case. + */ + if (nh.nh_flags & NHF_RFC5549) + m->m_flags |= M_ETHER_RFC5549; +#endif IP_PROBE(send, NULL, NULL, mtod(m, struct ip *), nh.nh_ifp, mtod(m, struct ip *), NULL); /* XXX: we can use cached route here */ error = (*nh.nh_ifp->if_output)(nh.nh_ifp, m, - (struct sockaddr *)&dst, NULL); + &dst.sa, NULL); if (error) break; } while ((m = m0) != NULL); Index: sys/netinet/ip_input.c =================================================================== --- sys/netinet/ip_input.c +++ sys/netinet/ip_input.c @@ -39,6 +39,7 @@ #include "opt_ipsec.h" #include "opt_route.h" #include "opt_rss.h" +#include "opt_inet6.h" #include #include @@ -1053,12 +1054,16 @@ #define RTA(rt) ((struct in_ifaddr *)(rt->rt_ifa)) u_long src = ntohl(ip->ip_src.s_addr); - if (RTA(rt) && - (src & RTA(rt)->ia_subnetmask) == RTA(rt)->ia_subnet) { - if (rt->rt_flags & RTF_GATEWAY) - dest.s_addr = satosin(rt->rt_gateway)->sin_addr.s_addr; - else - dest.s_addr = ip->ip_dst.s_addr; + if (RTA(rt) && (src & RTA(rt)->ia_subnetmask) == + RTA(rt)->ia_subnet +#ifdef INET6 + && ((rt->rt_flags & RTF_GATEWAY) == 0 || + rt->rt_gateway->sa_family == AF_INET) +#endif /* INET6 */ + ) { + dest = (rt->rt_flags & RTF_GATEWAY) ? + satosin(rt->rt_gateway)->sin_addr : + ip->ip_dst; /* Router requirements says to only send host redirects */ type = ICMP_REDIRECT; code = ICMP_REDIRECT_HOST; Index: sys/netinet/ip_output.c =================================================================== --- sys/netinet/ip_output.c +++ sys/netinet/ip_output.c @@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$"); #include "opt_inet.h" +#include "opt_inet6.h" #include "opt_ratelimit.h" #include "opt_ipsec.h" #include "opt_mbuf_stress_test.h" @@ -222,7 +223,7 @@ int mtu; int error = 0; struct sockaddr_in *dst; - const struct sockaddr_in *gw; + const struct sockaddr *gw; struct in_ifaddr *ia; int isbroadcast; uint16_t ip_len, ip_off; @@ -276,7 +277,8 @@ * gw is readonly but can point either to dst OR rt_gateway, * therefore we need restore gw if we're redoing lookup. */ - gw = dst = (struct sockaddr_in *)&ro->ro_dst; + gw = &ro->ro_dst; + dst = (struct sockaddr_in *)&ro->ro_dst; fibnum = (inp != NULL) ? inp->inp_inc.inc_fibnum : M_GETFIB(m); rte = ro->ro_rt; if (rte == NULL) { @@ -391,11 +393,13 @@ counter_u64_add(rte->rt_pksent, 1); rt_update_ro_flags(ro); if (rte->rt_flags & RTF_GATEWAY) - gw = (struct sockaddr_in *)rte->rt_gateway; + gw = rte->rt_gateway; if (rte->rt_flags & RTF_HOST) isbroadcast = (rte->rt_flags & RTF_BROADCAST); - else if (ifp->if_flags & IFF_BROADCAST) - isbroadcast = in_ifaddr_broadcast(gw->sin_addr, ia); + else if ((ifp->if_flags & IFF_BROADCAST) && + gw->sa_family == AF_INET) + isbroadcast = in_ifaddr_broadcast( + ((const struct sockaddr_in *)gw)->sin_addr, ia); else isbroadcast = 0; } @@ -419,7 +423,7 @@ * still points to the address in "ro". (It may have been * changed to point to a gateway address, above.) */ - gw = dst; + gw = (const struct sockaddr *)dst; /* * See if the caller provided any multicast options */ @@ -581,7 +585,7 @@ RO_RTFREE(ro); ro->ro_prepend = NULL; rte = NULL; - gw = dst; + gw = (const struct sockaddr *)dst; ip = mtod(m, struct ip *); goto again; @@ -646,6 +650,14 @@ * to avoid confusing lower layers. */ m_clrprotoflags(m); +#ifdef INET6 + /* + * Notify ethernet layer that we need change ethertype + * for RFC5549 case. + */ + if (gw->sa_family == AF_INET6) + m->m_flags |= M_ETHER_RFC5549; +#endif IP_PROBE(send, NULL, NULL, ip, ifp, ip, NULL); #ifdef RATELIMIT if (inp != NULL) { @@ -657,8 +669,7 @@ m->m_pkthdr.snd_tag = NULL; } #endif - error = (*ifp->if_output)(ifp, m, - (const struct sockaddr *)gw, ro); + error = (*ifp->if_output)(ifp, m, gw, ro); #ifdef RATELIMIT /* check for route change */ if (error == EAGAIN) @@ -696,6 +707,14 @@ * to avoid confusing upper layers. */ m_clrprotoflags(m); +#ifdef INET6 + /* + * Notify ethernet layer that we need change + * ethertype for RFC5549 case. + */ + if (gw->sa_family == AF_INET6) + m->m_flags |= M_ETHER_RFC5549; +#endif IP_PROBE(send, NULL, NULL, mtod(m, struct ip *), ifp, mtod(m, struct ip *), NULL); @@ -709,8 +728,7 @@ m->m_pkthdr.snd_tag = NULL; } #endif - error = (*ifp->if_output)(ifp, m, - (const struct sockaddr *)gw, ro); + error = (*ifp->if_output)(ifp, m, gw, ro); #ifdef RATELIMIT /* check for route change */ if (error == EAGAIN) Index: sys/netinet6/icmp6.c =================================================================== --- sys/netinet6/icmp6.c +++ sys/netinet6/icmp6.c @@ -229,9 +229,11 @@ { struct ip6_hdr *ip6; - if (ifp == NULL) + if (ifp == NULL || (m->m_flags & M_ETHER_RFC5549)) { + ICMP6STAT_INC(icp6s_canterror); + m_freem(m); return; - + } #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), ); #else @@ -270,7 +272,7 @@ icmp6_errcount(type, code); #ifdef M_DECRYPTED /*not openbsd*/ - if (m->m_flags & M_DECRYPTED) { + if (m->m_flags & (M_DECRYPTED | M_ETHER_RFC5549)) { ICMP6STAT_INC(icp6s_canterror); goto freeit; } Index: sys/netinet6/in6.h =================================================================== --- sys/netinet6/in6.h +++ sys/netinet6/in6.h @@ -661,6 +661,7 @@ #define M_AUTHIPDGM M_PROTO7 #define M_RTALERT_MLD M_PROTO8 #define M_FRAGMENTED M_PROTO9 /* contained fragment header */ +#define M_ETHER_RFC5549 M_PROTO12 /* ethertype fix needed */ #ifdef _KERNEL struct cmsghdr;