⬢ DragonFlyBSD Kernel Audit
DF-3377 / fix.diff
← back to finding ↓ download raw
--- sys/netinet6/ip6_mroute.c.orig
+++ sys/netinet6/ip6_mroute.c
@@ -588,6 +588,39 @@
 			    IFNAMSIZ);
 			multicast_register_if.if_flags |= IFF_LOOPBACK;
 			multicast_register_if.if_index = mifcp->mif6c_mifi;
+			/*
+			 * The register "interface" is never if_attach()ed,
+			 * but pim6_input() re-injects decapsulated PIM
+			 * REGISTER packets through if_simloop(), which bumps
+			 * per-CPU ifnet statistics through
+			 * ifp->if_data_pcpu.  Allocate the per-CPU stats
+			 * block (as if_attach() would) so that path does
+			 * not write through a NULL pointer.
+			 */
+			if (multicast_register_if.if_data_pcpu == NULL) {
+				multicast_register_if.if_data_pcpu =
+				    kmalloc(ncpus *
+				            sizeof(struct ifdata_pcpu),
+					    M_DEVBUF, M_WAITOK | M_ZERO |
+					    M_CACHEALIGN);
+			}
+			/*
+			 * if_addrheads[] is iterated by in6_ifawithifp()
+			 * from ip6_input() for packets "received" on this
+			 * fake interface (if.c:550-553 does this in
+			 * if_attach()).  Empty lists are fine.
+			 */
+			if (multicast_register_if.if_addrheads == NULL) {
+				int _i;
+
+				multicast_register_if.if_addrheads =
+				    kmalloc(ncpus *
+				            sizeof(struct ifaddrhead),
+					    M_IFADDR, M_WAITOK | M_ZERO);
+				for (_i = 0; _i < ncpus; ++_i)
+					TAILQ_INIT(
+					    &multicast_register_if.if_addrheads[_i]);
+			}
 			reg_mif_num = mifcp->mif6c_mifi;
 		}
 
@@ -1417,9 +1450,18 @@
 	 * For each mif, forward a copy of the packet if there are group
 	 * members downstream on the interface.
 	 */
-	if (in6_addr2zoneid(ifp, &ip6->ip6_dst, &dscopein) ||
-	    in6_addr2zoneid(ifp, &ip6->ip6_src, &sscopein))
+	/*
+	 * XXX For packets arriving through the PIM register tunnel
+	 * interface we believe the routing daemon (mirroring the
+	 * output-side check below); the register "interface" has no
+	 * scope6 data attached (never in6_ifattach()ed).
+	 */
+	if (mif6table[rt->mf6c_parent].m6_flags & MIFF_REGISTER) {
+		dscopein = sscopein = 0;
+	} else if (in6_addr2zoneid(ifp, &ip6->ip6_dst, &dscopein) ||
+	    in6_addr2zoneid(ifp, &ip6->ip6_src, &sscopein)) {
 		return (EINVAL);
+	}
 	for (mifp = mif6table, mifi = 0; mifi < nummifs; mifp++, mifi++) {
 		if (IF_ISSET(mifi, &rt->mf6c_ifset)) {
 			u_int32_t dscopeout, sscopeout;