Page MenuHomeFreeBSD

D35117.diff
No OneTemporary

D35117.diff

Index: sys/netinet6/ip6_output.c
===================================================================
--- sys/netinet6/ip6_output.c
+++ sys/netinet6/ip6_output.c
@@ -416,7 +416,7 @@
struct mbuf *mprev;
struct route_in6 *ro_pmtu;
struct nhop_object *nh;
- struct sockaddr_in6 *dst, sin6, src_sa, dst_sa;
+ struct sockaddr_in6 *dst, sin6, dst_sa;
struct in6_addr odst;
u_char *nexthdrp;
int tlen, len;
@@ -427,8 +427,6 @@
int alwaysfrag, dontfrag;
u_int32_t optlen, plen = 0, unfragpartlen;
struct ip6_exthdrs exthdrs;
- struct in6_addr src0, dst0;
- u_int32_t zone;
bool hdrsplit;
int sw_csum, tso;
int needfiblookup;
@@ -689,7 +687,6 @@
ro->ro_dst.sin6_family == AF_INET6 &&
IN6_ARE_ADDR_EQUAL(&ro->ro_dst.sin6_addr, &ip6->ip6_dst)) {
nh = ro->ro_nh;
- ifp = nh->nh_ifp;
} else {
if (ro->ro_lle)
LLE_FREE(ro->ro_lle); /* zeros ro_lle */
@@ -708,8 +705,6 @@
in6_ifstat_inc(ifp, ifs6_out_discard);
goto bad;
}
- if (ifp != NULL)
- mtu = ifp->if_mtu;
}
if (nh == NULL) {
/*
@@ -717,9 +712,12 @@
* 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;
+ mtu = nh->nh_mtu;
ia = (struct in6_ifaddr *)(nh->nh_ifa);
counter_u64_add(nh->nh_pksent, 1);
}
@@ -740,6 +738,7 @@
(ifp = im6o->im6o_multicast_ifp) != NULL) {
/* We do not need a route lookup. */
*dst = dst_sa; /* XXX */
+ origifp = ifp;
goto nonh6lookup;
}
@@ -754,6 +753,7 @@
goto bad;
}
*dst = dst_sa; /* XXX */
+ origifp = ifp;
goto nonh6lookup;
}
}
@@ -768,6 +768,7 @@
}
ifp = nh->nh_ifp;
+ origifp = nh->nh_aifp;
mtu = nh->nh_mtu;
ia = ifatoia6(nh->nh_ifa);
if (nh->nh_flags & NHF_GATEWAY)
@@ -784,65 +785,18 @@
in6_ifstat_inc(ifp, ifs6_out_request);
}
- /* Setup data structures for scope ID checks. */
- src0 = ip6->ip6_src;
- 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;
-
- dst0 = ip6->ip6_dst;
- /* 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;
-
- /* 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 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.
- */
+ /*
+ * Check scope for source/destination addresses.
+ * Per RFC 4007, clause 5, it is required to maintain "convex" property -
+ * do not allow packets routed outside their zone.
+ * Separately check the zone for source and destination, using _address interface_ -
+ * "normal" transmit interface if dst is non-local destination and ifp which dst
+ * belongs to otherwise. This allows to pass link-local packets via loopback interface.
+ */
+ if (!in6_checkscope_embedded(&ip6->ip6_src, origifp) ||
+ !in6_checkscope_embedded(&ip6->ip6_dst, origifp)) {
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;
Index: sys/netinet6/scope6.c
===================================================================
--- sys/netinet6/scope6.c
+++ sys/netinet6/scope6.c
@@ -542,6 +542,57 @@
*scopeid = zoneid;
}
+/*
+ * Checks that @addr_zone of @addr scope spans across @ifp.
+ * Returns true on success.
+ *
+ * @addr - Specified (non ::) valid IPv6 address, w/o embedded scope, used only to determine scope
+ * @addr_zone - zone id (host format) of an @addr zone
+ * @check_multi - if true, verify @addr_zone against the relevant @ifp zone, otherwise pass
+ * @ifp - interface to check scope against (if @addr is a local address, then @ifp is the addr iface)
+ */
+bool
+in6_checkscope(const struct in6_addr *addr, uint32_t addr_zone, bool check_multi, const struct ifnet *ifp)
+{
+ NET_EPOCH_ASSERT();
+
+ if (IN6_IS_ADDR_LINKLOCAL(addr) || IN6_IS_ADDR_LOOPBACK(addr)) {
+ return (addr_zone == ifp->if_index);
+ }
+ if (__predict_false(IN6_IS_ADDR_MULTICAST(addr) && check_multi)) {
+ switch (IPV6_ADDR_MC_SCOPE(addr)) {
+ case 0x0F:
+ return (true); // IPV6_ADDR_SCOPE_GLOBAL
+ case IPV6_ADDR_SCOPE_INTFACELOCAL:
+ case IPV6_ADDR_SCOPE_LINKLOCAL:
+ return (addr_zone == ifp->if_index);
+ default:
+ if (ifp->if_afdata[AF_INET6] != NULL) {
+ return (addr_zone == SID(ifp)->s6id_list[IPV6_ADDR_MC_SCOPE(addr)]);
+ }
+ return false;
+ }
+ }
+ // Explicitly pass check for global unicast & site-local addresses
+ return (true);
+}
+
+/*
+ * Checks that the zoneid potentially embedded in @addr spans across @ifp.
+ * Returns true on success.
+ */
+bool
+in6_checkscope_embedded(const struct in6_addr *addr, const struct ifnet *ifp)
+{
+ uint32_t zone;
+
+ if (!IN6_IS_ADDR_LOOPBACK(addr))
+ zone = ntohs(addr->s6_addr16[1]);
+ else
+ zone = (ifp->if_flags & IFF_LOOPBACK) ? ifp->if_index : 0;
+ return (in6_checkscope(addr, zone, false, ifp));
+}
+
/*
* This function is for checking sockaddr_in6 structure passed
* from the application level (usually).
Index: sys/netinet6/scope6_var.h
===================================================================
--- sys/netinet6/scope6_var.h
+++ sys/netinet6/scope6_var.h
@@ -66,6 +66,9 @@
struct ifnet* in6_getlinkifnet(uint32_t);
uint32_t in6_get_unicast_scopeid(const struct in6_addr *, const struct ifnet *);
void in6_set_unicast_scopeid(struct in6_addr *, uint32_t);
+bool in6_checkscope(const struct in6_addr *addr, uint32_t addr_zone,
+ bool check_multi, const struct ifnet *ifp);
+bool in6_checkscope_embedded(const struct in6_addr *addr, const struct ifnet *ifp);
#endif /* _KERNEL */
#endif /* _NETINET6_SCOPE6_VAR_H_ */

File Metadata

Mime Type
text/plain
Expires
Thu, Jan 15, 11:02 AM (1 h, 27 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27638475
Default Alt Text
D35117.diff (7 KB)

Event Timeline