aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorHans Petter Selasky <hselasky@FreeBSD.org>2019-01-09 14:28:08 +0000
committerHans Petter Selasky <hselasky@FreeBSD.org>2019-01-09 14:28:08 +0000
commitef0111fdf364e4e87b522025b13aad69067c3fe6 (patch)
tree69dc40c11a7e7d03d13a9f7826d9638d24d4a223 /sys
parent7071b803da63f80811c7e4250642788c9d79d08c (diff)
downloadsrc-ef0111fdf364e4e87b522025b13aad69067c3fe6.tar.gz
src-ef0111fdf364e4e87b522025b13aad69067c3fe6.zip
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
Notes
Notes: svn path=/head/; revision=342884
Diffstat (limited to 'sys')
-rw-r--r--sys/netinet6/ip6_output.c82
1 files changed, 51 insertions, 31 deletions
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
index f6539834d39d..741521abb8a1 100644
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -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;
-
- /* scope check is done. */
- goto routefound;
+ /* 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;
+
+ } 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) {
/*