DragonFlyBSD Kernel Audit
DF-0735 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/netgraph7/ng_ipfw.c b/sys/netgraph7/ng_ipfw.c
--- a/sys/netgraph7/ng_ipfw.c
+++ b/sys/netgraph7/ng_ipfw.c
@@ -37,6 +37,8 @@
 #include <sys/syslog.h>
 
 #include <net/if.h>
+#include <net/netisr.h>
+#include <net/netmsg2.h>
 
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
@@ -62,6 +64,9 @@
 static hook_p		ng_ipfw_findhook1(node_p, u_int16_t );
 static int		ng_ipfw_input(struct mbuf **, int, struct ip_fw_args *,
 			    int);
+/* DF-0735 fix: ip_output() requires netisr thread context
+ * (ASSERT_NETISR_NCPUS at ip_output.c:185); this netmsg handler runs there. */
+static void		ng_ipfw_ip_output_dispatch(netmsg_t nmsg);
 
 /* We have only one node */
 static node_p	fw_node;
@@ -216,6 +221,21 @@
 }
 
 
+/*
+ * DF-0735 fix: netmsg handler that runs ip_output() from a netisr thread.
+ * ip_output() opens with ASSERT_NETISR_NCPUS(mycpuid) (ip_output.c:185);
+ * this callback is dispatched from ng_ipfw_rcvdata via lwkt_sendmsg to a
+ * netisr port so the assertion holds. Pattern matches dummynet's
+ * ip_dn_ip_output at sys/net/dummynet/ip_dummynet_glue.c:270.
+ */
+static void
+ng_ipfw_ip_output_dispatch(netmsg_t nmsg)
+{
+	struct mbuf *m = nmsg->packet.nm_packet;
+
+	ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL);
+}
+
 static int
 ng_ipfw_rcvdata(hook_p hook, item_p item)
 {
@@ -233,20 +253,36 @@
 
 	switch (ngit->dir) {
 	case NG_IPFW_OUT:
+	    /*
+	     * DF-0735: do NOT call ip_output() directly -- this rcvdata runs
+	     * in the netgraph worker thread (created at ng_base.c:2787-2789),
+	     * which is NOT a netisr thread, so ip_output.c:185's
+	     * ASSERT_NETISR_NCPUS(mycpuid) would panic on default-GENERIC
+	     * (INVARIANTS ON). Dispatch the mbuf to a netisr port and call
+	     * ip_output from there, mirroring dummynet's ip_dn_queue pattern
+	     * (sys/net/dummynet/ip_dummynet_glue.c:82-99).
+	     */
 	    {
-		struct ip *ip;
+		struct netmsg_packet *nmp;
 
-		if (m->m_len < sizeof(struct ip) &&
-		    (m = m_pullup(m, sizeof(struct ip))) == NULL)
-			return (EINVAL);
-
-		ip = mtod(m, struct ip *);
-
-		return ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL);
+		nmp = &m->m_hdr.mh_netmsg;
+		netmsg_init(&nmp->base, NULL, &netisr_apanic_rport,
+			    0, ng_ipfw_ip_output_dispatch);
+		nmp->nm_packet = m;
+		lwkt_sendmsg(netisr_cpuport(mycpuid), &nmp->base.lmsg);
+		return (0);
 	    }
 	case NG_IPFW_IN:
-		ip_input(m);
-		return (0);
+	    /*
+	     * DF-0735: do NOT call ip_input() directly. Hand the packet to
+	     * NETISR_IP via netisr_queue(), as the sibling ng_ip_input.c:125
+	     * does. ip_input() at ip_input.c:460 opens with
+	     * ASSERT_NETISR_NCPUS(mycpuid) and must be called from a netisr
+	     * thread.
+	     */
+	    m->m_flags &= ~M_HASH;
+	    netisr_queue(NETISR_IP, m);
+	    return (0);
 	default:
 		panic("ng_ipfw_rcvdata: bad dir %u", ngit->dir);
 	}