Date: Wed, 9 Jan 2019 14:28:08 +0000 (UTC) From: Hans Petter Selasky <hselasky@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r342884 - head/sys/netinet6 Message-ID: <201901091428.x09ES8BD070614@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: hselasky Date: Wed Jan 9 14:28:08 2019 New Revision: 342884 URL: https://svnweb.freebsd.org/changeset/base/342884 Log: Fix loopback traffic when using non-lo0 link local IPv6 addresses. The loopback interface can only receive packets with a single scope ID, namely the scope ID of the loopback interface itself. To mitigate this packets which use the scope ID are appearing as received by the real network interface, see "origifp" in the patch. The current code would drop packets which are designated for loopback which use a link-local scope ID in the destination address or source address, because they won't match the lo0's scope ID. To fix this restore the network interface pointer from the scope ID in the destination address for the problematic cases. See comments added in patch for a more detailed description. This issue was introduced with route caching (ae@). Reviewed by: bz (network) Differential Revision: https://reviews.freebsd.org/D18769 MFC after: 1 week Sponsored by: Mellanox Technologies Modified: head/sys/netinet6/ip6_output.c Modified: head/sys/netinet6/ip6_output.c ============================================================================== --- head/sys/netinet6/ip6_output.c Wed Jan 9 12:50:24 2019 (r342883) +++ head/sys/netinet6/ip6_output.c Wed Jan 9 14:28:08 2019 (r342884) @@ -571,52 +571,72 @@ again: counter_u64_add(rt->rt_pksent, 1); } - - /* - * The outgoing interface must be in the zone of source and - * destination addresses. - */ - origifp = ifp; - + /* Setup data structures for scope ID checks. */ src0 = ip6->ip6_src; - if (in6_setscope(&src0, origifp, &zone)) - goto badscope; 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; dst0 = ip6->ip6_dst; - if (in6_setscope(&dst0, origifp, &zone)) - goto badscope; /* 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; - } - /* 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; + /* Check for valid scope ID. */ + if (in6_setscope(&src0, ifp, &zone) == 0 && + sa6_recoverscope(&src_sa) == 0 && zone == src_sa.sin6_scope_id && + in6_setscope(&dst0, ifp, &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 is 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; - /* scope check is done. */ - goto routefound; + } else if ((ifp->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) { + /* + * If the destination network interface is not a + * loopback interface, or the destination network + * address has no scope ID, or the source address has + * a scope ID set which is different from the + * destination address one, or there is no network + * interface representing this scope ID, the address + * pair is considered invalid. + */ + IP6STAT_INC(ip6s_badscope); + in6_ifstat_inc(ifp, ifs6_out_discard); + if (error == 0) + error = EHOSTUNREACH; /* XXX */ + goto bad; + } - badscope: - IP6STAT_INC(ip6s_badscope); - in6_ifstat_inc(origifp, ifs6_out_discard); - if (error == 0) - error = EHOSTUNREACH; /* XXX */ - goto bad; + /* All scope ID checks are successful. */ - routefound: if (rt && !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { if (opt && opt->ip6po_nextroute.ro_rt) { /*
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201901091428.x09ES8BD070614>