diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -824,7 +824,7 @@ ip6_sprintf(ip6bufd, &ip6->ip6_dst))); goto bad; } - if (V_ip6_sav && !(rcvif->if_flags & IFF_LOOPBACK) && + if (V_ip6_sav && !(m->m_flags & M_LOOP) && __predict_false(in6_localip_fib(&ip6->ip6_src, rcvif->if_fib))) { IP6STAT_INC(ip6s_badscope); /* XXX */ diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -688,8 +688,8 @@ if (ro->ro_nh != NULL && fwd_tag == NULL && ro->ro_dst.sin6_family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&ro->ro_dst.sin6_addr, &ip6->ip6_dst)) { + /* Nexthop is valid and contains valid ifp */ nh = ro->ro_nh; - ifp = nh->nh_ifp; } else { if (ro->ro_lle) LLE_FREE(ro->ro_lle); /* zeros ro_lle */ @@ -708,8 +708,11 @@ in6_ifstat_inc(ifp, ifs6_out_discard); goto bad; } - if (ifp != NULL) - mtu = ifp->if_mtu; + /* + * At this point at least @ifp is not NULL + * Can be the case when dst is multicast, link-local or + * interface is explicitly specificed by the caller. + */ } if (nh == NULL) { /* @@ -717,9 +720,11 @@ * dst may not have been updated. */ *dst = dst_sa; /* XXX */ + origifp = ifp; + mtu = ifp->if_mtu; } else { - if (nh->nh_flags & NHF_HOST) - mtu = nh->nh_mtu; + ifp = nh->nh_ifp; + origifp = nh->nh_aifp; ia = (struct in6_ifaddr *)(nh->nh_ifa); counter_u64_add(nh->nh_pksent, 1); } @@ -740,6 +745,7 @@ (ifp = im6o->im6o_multicast_ifp) != NULL) { /* We do not need a route lookup. */ *dst = dst_sa; /* XXX */ + origifp = ifp; goto nonh6lookup; } @@ -754,6 +760,7 @@ goto bad; } *dst = dst_sa; /* XXX */ + origifp = ifp; goto nonh6lookup; } } @@ -768,7 +775,7 @@ } ifp = nh->nh_ifp; - mtu = nh->nh_mtu; + origifp = nh->nh_aifp; ia = ifatoia6(nh->nh_ifa); if (nh->nh_flags & NHF_GATEWAY) dst->sin6_addr = nh->gw6_sa.sin6_addr; @@ -777,8 +784,21 @@ nonh6lookup: ; } + /* + * At this point ifp MUST be pointing to the valid transmit ifp. + * origifp MUST be valid and pointing to either the same ifp or, + * in case of loopback output, to the interface which ip6_src + * belongs to. + * Examples: + * fe80::1%em0 -> fe80::2%em0 -> ifp=em0, origifp=em0 + * fe80::1%em0 -> fe80::1%em0 -> ifp=lo0, origifp=em0 + * ::1 -> ::1 -> ifp=lo0, origifp=lo0 + * + * mtu can be 0 and will be refined later. + */ + KASSERT((ifp != NULL), ("output interface must not be NULL")); + KASSERT((origifp != NULL), ("output address interface must not be NULL")); - /* Then nh (for unicast) and ifp must be non-NULL valid values. */ if ((flags & IPV6_FORWARDING) == 0) { /* XXX: the FORWARDING flag can be set for mrouting. */ in6_ifstat_inc(ifp, ifs6_out_request); @@ -799,39 +819,22 @@ dst_sa.sin6_addr = ip6->ip6_dst; /* Check for valid scope ID. */ - if (in6_setscope(&src0, ifp, &zone) == 0 && + if (in6_setscope(&src0, origifp, &zone) == 0 && sa6_recoverscope(&src_sa) == 0 && zone == src_sa.sin6_scope_id && - in6_setscope(&dst0, ifp, &zone) == 0 && + in6_setscope(&dst0, origifp, &zone) == 0 && sa6_recoverscope(&dst_sa) == 0 && zone == dst_sa.sin6_scope_id) { /* * The outgoing interface is in the zone of the source * and destination addresses. * - * Because the loopback interface cannot receive - * packets with a different scope ID than its own, - * there is a trick to pretend the outgoing packet - * was received by the real network interface, by - * setting "origifp" different from "ifp". This is - * only allowed when "ifp" is a loopback network - * interface. Refer to code in nd6_output_ifp() for - * more details. */ - origifp = ifp; - - /* - * We should use ia_ifp to support the case of sending - * packets to an address of our own. - */ - if (ia != NULL && ia->ia_ifp) - ifp = ia->ia_ifp; - - } else if ((ifp->if_flags & IFF_LOOPBACK) == 0 || + } else if ((origifp->if_flags & IFF_LOOPBACK) == 0 || sa6_recoverscope(&src_sa) != 0 || sa6_recoverscope(&dst_sa) != 0 || dst_sa.sin6_scope_id == 0 || (src_sa.sin6_scope_id != 0 && src_sa.sin6_scope_id != dst_sa.sin6_scope_id) || - (origifp = ifnet_byindex(dst_sa.sin6_scope_id)) == NULL) { + ifnet_byindex(dst_sa.sin6_scope_id) == NULL) { /* * If the destination network interface is not a * loopback interface, or the destination network @@ -842,7 +845,7 @@ * pair is considered invalid. */ IP6STAT_INC(ip6s_badscope); - in6_ifstat_inc(ifp, ifs6_out_discard); + in6_ifstat_inc(origifp, ifs6_out_discard); if (error == 0) error = EHOSTUNREACH; /* XXX */ goto bad; diff --git a/tests/sys/netinet6/test_ip6_output.py b/tests/sys/netinet6/test_ip6_output.py --- a/tests/sys/netinet6/test_ip6_output.py +++ b/tests/sys/netinet6/test_ip6_output.py @@ -447,7 +447,7 @@ "source_validation", [ pytest.param(0, id="no_sav"), - pytest.param(1, id="sav", marks=pytest.mark.skip(reason="fails")), + pytest.param(1, id="sav"), ], ) @pytest.mark.parametrize("scope", ["gu", "ll", "lo"]) @@ -495,7 +495,7 @@ "source_validation", [ pytest.param(0, id="no_sav"), - pytest.param(1, id="sav", marks=pytest.mark.skip(reason="fails")), + pytest.param(1, id="sav"), ], ) @pytest.mark.parametrize("scope", ["gu", "ll", "lo"])