--- //SpectraBSD/stable/sys/netinet6/ip6_input.c 2018-01-08 21:40:23.000000000 -0700 +++ //SpectraBSD/stable/sys/netinet6/ip6_input.c 2018-01-23 00:04:05.000000000 -0700 @@ -802,7 +802,8 @@ goto bad; } if (in6_setscope(&ip6->ip6_src, rcvif, NULL) || - in6_setscope(&ip6->ip6_dst, rcvif, NULL)) { + (!(srcrt && IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) && + in6_setscope(&ip6->ip6_dst, rcvif, NULL))) { IP6STAT_INC(ip6s_badscope); goto bad; } --- //SpectraBSD/stable/sys/netinet6/ip6_output.c 2018-01-08 21:40:23.000000000 -0700 +++ //SpectraBSD/stable/sys/netinet6/ip6_output.c 2018-01-23 00:04:05.000000000 -0700 @@ -306,7 +306,7 @@ struct route_in6 ip6route; struct rtentry *rt = NULL; struct sockaddr_in6 *dst, src_sa, dst_sa; - struct in6_addr odst; + struct in6_addr osrc, odst; int error = 0; struct in6_ifaddr *ia = NULL; u_long mtu; @@ -322,6 +322,7 @@ uint32_t fibnum; struct m_tag *fwd_tag = NULL; uint32_t id; + bool badsrcscope = false, baddstscope = false; if (inp != NULL) { INP_LOCK_ASSERT(inp); @@ -593,25 +594,24 @@ src0 = ip6->ip6_src; if (in6_setscope(&src0, origifp, &zone)) - goto badscope; + badsrcscope = true; bzero(&src_sa, sizeof(src_sa)); src_sa.sin6_family = AF_INET6; src_sa.sin6_len = sizeof(src_sa); src_sa.sin6_addr = ip6->ip6_src; if (sa6_recoverscope(&src_sa) || zone != src_sa.sin6_scope_id) - goto badscope; + badsrcscope = true; dst0 = ip6->ip6_dst; if (in6_setscope(&dst0, origifp, &zone)) - goto badscope; + baddstscope = true; /* re-initialize to be sure */ bzero(&dst_sa, sizeof(dst_sa)); dst_sa.sin6_family = AF_INET6; dst_sa.sin6_len = sizeof(dst_sa); dst_sa.sin6_addr = ip6->ip6_dst; - if (sa6_recoverscope(&dst_sa) || zone != dst_sa.sin6_scope_id) { - goto badscope; - } + if (sa6_recoverscope(&dst_sa) || zone != dst_sa.sin6_scope_id) + baddstscope = true; /* We should use ia_ifp to support the case of * sending packets to an address of our own. @@ -619,17 +619,6 @@ if (ia != NULL && ia->ia_ifp) ifp = ia->ia_ifp; - /* scope check is done. */ - goto routefound; - - badscope: - IP6STAT_INC(ip6s_badscope); - in6_ifstat_inc(origifp, ifs6_out_discard); - if (error == 0) - error = EHOSTUNREACH; /* XXX */ - goto bad; - - routefound: if (rt && !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { if (opt && opt->ip6po_nextroute.ro_rt) { /* @@ -665,6 +654,9 @@ * the address; protocols will filter it later, * thus deferring a hash lookup and lock acquisition * at the expense of an m_copym(). + * + * Note: ip6_mloopback clears the embedded scope IDs on + * both src and dst */ ip6_mloopback(ifp, m); } else { @@ -788,6 +780,7 @@ if (!PFIL_HOOKED(&V_inet6_pfil_hook)) goto passout; + osrc = ip6->ip6_src; odst = ip6->ip6_dst; /* Run through list of hooks for output packets. */ error = pfil_run_hooks(&V_inet6_pfil_hook, &m, ifp, PFIL_OUT, inp); @@ -796,6 +789,50 @@ /* adjust pointer */ ip6 = mtod(m, struct ip6_hdr *); + /* Repeat the scope check if pfil changed addresses */ + if (!IN6_ARE_ADDR_EQUAL(&osrc, &ip6->ip6_src)) { + badsrcscope = false; + src0 = ip6->ip6_src; + if (in6_setscope(&src0, origifp, &zone)) + badsrcscope = true; + bzero(&src_sa, sizeof(src_sa)); + src_sa.sin6_family = AF_INET6; + src_sa.sin6_len = sizeof(src_sa); + src_sa.sin6_addr = ip6->ip6_src; + if (sa6_recoverscope(&src_sa) || zone != src_sa.sin6_scope_id) + badsrcscope = true; + } + if (!IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst)) { + baddstscope = false; + dst0 = ip6->ip6_dst; + if (in6_setscope(&dst0, origifp, &zone)) + baddstscope = true; + /* re-initialize to be sure */ + bzero(&dst_sa, sizeof(dst_sa)); + dst_sa.sin6_family = AF_INET6; + dst_sa.sin6_len = sizeof(dst_sa); + dst_sa.sin6_addr = ip6->ip6_dst; + if (sa6_recoverscope(&dst_sa) || zone != dst_sa.sin6_scope_id) + baddstscope = true; + } + if (!badsrcscope && !baddstscope) + goto goodscope; + + IP6STAT_INC(ip6s_badscope); + in6_ifstat_inc(origifp, ifs6_out_discard); + if (error == 0) + error = EHOSTUNREACH; /* XXX */ + goto bad; + + goodscope: + + /* + * clear embedded scope identifiers if necessary. + * in6_clearscope will touch the addresses only when necessary. + */ + in6_clearscope(&ip6->ip6_src); + in6_clearscope(&ip6->ip6_dst); + needfiblookup = 0; /* See if destination IP address was changed by packet filter. */ if (!IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst)) {