Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 14 Mar 2019 22:52:16 +0000 (UTC)
From:      Gleb Smirnoff <glebius@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r345166 - head/sys/netpfil/ipfw
Message-ID:  <201903142252.x2EMqGss074944@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: glebius
Date: Thu Mar 14 22:52:16 2019
New Revision: 345166
URL: https://svnweb.freebsd.org/changeset/base/345166

Log:
  PFIL_MEMPTR for ipfw link level hook
  
  With new pfil(9) KPI it is possible to pass a void pointer with length
  instead of mbuf pointer to a packet filter. Until this commit no filters
  supported that, so pfil run through a shim function pfil_fake_mbuf().
  
  Now the ipfw(4) hook named "default-link", that is instantiated when
  net.link.ether.ipfw sysctl is on, supports processing pointer/length
  packets natively.
  
  - ip_fw_args now has union for either mbuf or void *, and if flags have
    non-zero length, then we use the void *.
  - through ipfw_chk() we handle mem/mbuf cases differently.
  - ether_header goes away from args. It is ipfw_chk() responsibility
    to do parsing of Ethernet header.
  - ipfw_log() now uses different bpf APIs to log packets.
  
  Although ipfw_chk() is now capable to process pointer/length packets,
  this commit adds support for the link level hook only, see
  ipfw_check_frame(). Potentially the IP processing hook ipfw_check_packet()
  can be improved too, but that requires more changes since the hook
  supports more complex actions: NAT, divert, etc.
  
  Reviewed by:	ae
  Differential Revision:	https://reviews.freebsd.org/D19357

Modified:
  head/sys/netpfil/ipfw/ip_fw2.c
  head/sys/netpfil/ipfw/ip_fw_bpf.c
  head/sys/netpfil/ipfw/ip_fw_log.c
  head/sys/netpfil/ipfw/ip_fw_pfil.c
  head/sys/netpfil/ipfw/ip_fw_private.h

Modified: head/sys/netpfil/ipfw/ip_fw2.c
==============================================================================
--- head/sys/netpfil/ipfw/ip_fw2.c	Thu Mar 14 22:32:50 2019	(r345165)
+++ head/sys/netpfil/ipfw/ip_fw2.c	Thu Mar 14 22:52:16 2019	(r345166)
@@ -1258,7 +1258,6 @@ jump_linear(struct ip_fw_chain *chain, struct ip_fw *f
  *
  *	args->m	(in/out) The packet; we set to NULL when/if we nuke it.
  *		Starts with the IP header.
- *	args->eh (in)	Mac header if present, NULL for layer3 packet.
  *	args->L3offset	Number of bytes bypassed if we came from L2.
  *			e.g. often sizeof(eh)  ** NOTYET **
  *	args->ifp	Incoming or outgoing interface.
@@ -1297,23 +1296,19 @@ ipfw_chk(struct ip_fw_args *args)
 	 * the implementation of the various instructions to make sure
 	 * that they still work.
 	 *
-	 * args->eh	The MAC header. It is non-null for a layer2
-	 *	packet, it is NULL for a layer-3 packet.
-	 * **notyet**
-	 * args->L3offset Offset in the packet to the L3 (IP or equiv.) header.
-	 *
 	 * m | args->m	Pointer to the mbuf, as received from the caller.
 	 *	It may change if ipfw_chk() does an m_pullup, or if it
 	 *	consumes the packet because it calls send_reject().
 	 *	XXX This has to change, so that ipfw_chk() never modifies
 	 *	or consumes the buffer.
-	 * ip	is the beginning of the ip(4 or 6) header.
-	 *	Calculated by adding the L3offset to the start of data.
-	 *	(Until we start using L3offset, the packet is
-	 *	supposed to start with the ip header).
+	 *	OR
+	 * args->mem	Pointer to contigous memory chunk.
+	 * ip	Is the beginning of the ip(4 or 6) header.
+	 * eh	Ethernet header in case if input is Layer2.
 	 */
-	struct mbuf *m = args->m;
-	struct ip *ip = mtod(m, struct ip *);
+	struct mbuf *m;
+	struct ip *ip;
+	struct ether_header *eh;
 
 	/*
 	 * For rules which contain uid/gid or jail constraints, cache
@@ -1370,7 +1365,6 @@ ipfw_chk(struct ip_fw_args *args)
 	struct in_addr src_ip, dst_ip;		/* NOTE: network format	*/
 	int iplen = 0;
 	int pktlen;
-	uint16_t etype;			/* Host order stored ether type */
 
 	struct ipfw_dyn_info dyn_info;
 	struct ip_fw *q = NULL;
@@ -1394,14 +1388,45 @@ ipfw_chk(struct ip_fw_args *args)
 
 	int done = 0;		/* flag to exit the outer loop */
 	IPFW_RLOCK_TRACKER;
+	bool mem;
 
-	if (m->m_flags & M_SKIP_FIREWALL || (! V_ipfw_vnet_ready))
-		return (IP_FW_PASS);	/* accept */
+	if ((mem = (args->flags & IPFW_ARGS_LENMASK))) {
+		if (args->flags & IPFW_ARGS_ETHER) {
+			eh = (struct ether_header *)args->mem;
+			if (eh->ether_type == htons(ETHERTYPE_VLAN))
+				ip = (struct ip *)
+				    ((struct ether_vlan_header *)eh + 1);
+			else
+				ip = (struct ip *)(eh + 1);
+		} else {
+			eh = NULL;
+			ip = (struct ip *)args->mem;
+		}
+		pktlen = IPFW_ARGS_LENGTH(args->flags);
+		args->f_id.fib = args->ifp->if_fib;	/* best guess */
+	} else {
+		m = args->m;
+		if (m->m_flags & M_SKIP_FIREWALL || (! V_ipfw_vnet_ready))
+			return (IP_FW_PASS);	/* accept */
+		if (args->flags & IPFW_ARGS_ETHER) {
+	                /* We need some amount of data to be contiguous. */
+			if (m->m_len < min(m->m_pkthdr.len, max_protohdr) &&
+			    (args->m = m = m_pullup(m, min(m->m_pkthdr.len,
+			    max_protohdr))) == NULL)
+				goto pullup_failed;
+			eh = mtod(m, struct ether_header *);
+			ip = (struct ip *)(eh + 1);
+		} else {
+			eh = NULL;
+			ip = mtod(m, struct ip *);
+		}
+		pktlen = m->m_pkthdr.len;
+		args->f_id.fib = M_GETFIB(m); /* mbuf not altered */
+	}
 
 	dst_ip.s_addr = 0;		/* make sure it is initialized */
 	src_ip.s_addr = 0;		/* make sure it is initialized */
 	src_port = dst_port = 0;
-	pktlen = m->m_pkthdr.len;
 
 	DYN_INFO_INIT(&dyn_info);
 /*
@@ -1411,28 +1436,41 @@ ipfw_chk(struct ip_fw_args *args)
  * this way).
  */
 #define PULLUP_TO(_len, p, T)	PULLUP_LEN(_len, p, sizeof(T))
+#define	EHLEN	(eh != NULL ? ((char *)ip - (char *)eh) : 0)
 #define PULLUP_LEN(_len, p, T)					\
 do {								\
-	int x = (_len) + T;					\
-	if ((m)->m_len < x) {					\
-		args->m = m = m_pullup(m, x);			\
-		if (m == NULL)					\
-			goto pullup_failed;			\
+	int x = (_len) + T + EHLEN;				\
+	if (mem) {						\
+		MPASS(pktlen >= x);				\
+		p = (char *)args->mem + (_len) + EHLEN;		\
+	} else {						\
+		if (__predict_false((m)->m_len < x)) {		\
+			args->m = m = m_pullup(m, x);		\
+			if (m == NULL)				\
+				goto pullup_failed;		\
+		}						\
+		p = mtod(m, char *) + (_len) + EHLEN;		\
 	}							\
-	p = (mtod(m, char *) + (_len));				\
 } while (0)
+/*
+ * In case pointers got stale after pullups, update them.
+ */
+#define	UPDATE_POINTERS()					\
+do {								\
+	if (!mem) {						\
+		if (eh != NULL) {				\
+			eh = mtod(m, struct ether_header *);	\
+			ip = (struct ip *)(eh + 1);		\
+		} else						\
+			ip = mtod(m, struct ip *);		\
+		args->m = m;					\
+	}							\
+} while (0)
 
-	/*
-	 * if we have an ether header,
-	 */
-	if (args->flags & IPFW_ARGS_ETHER)
-		etype = ntohs(args->eh->ether_type);
-	else
-		etype = 0;
-
 	/* Identify IP packets and fill up variables. */
 	if (pktlen >= sizeof(struct ip6_hdr) &&
-	    (etype == 0 || etype == ETHERTYPE_IPV6) && ip->ip_v == 6) {
+	    (eh == NULL || eh->ether_type == htons(ETHERTYPE_IPV6)) &&
+	    ip->ip_v == 6) {
 		struct ip6_hdr *ip6 = (struct ip6_hdr *)ip;
 
 		is_ipv6 = 1;
@@ -1609,7 +1647,7 @@ do {								\
 				break;
 			} /*switch */
 		}
-		ip = mtod(m, struct ip *);
+		UPDATE_POINTERS();
 		ip6 = (struct ip6_hdr *)ip;
 		args->f_id.addr_type = 6;
 		args->f_id.src_ip6 = ip6->ip6_src;
@@ -1617,7 +1655,8 @@ do {								\
 		args->f_id.flow_id6 = ntohl(ip6->ip6_flow);
 		iplen = ntohs(ip6->ip6_plen) + sizeof(*ip6);
 	} else if (pktlen >= sizeof(struct ip) &&
-	    (etype == 0 || etype == ETHERTYPE_IP) && ip->ip_v == 4) {
+	    (eh == NULL || eh->ether_type == htons(ETHERTYPE_IP)) &&
+	    ip->ip_v == 4) {
 		is_ipv4 = 1;
 		args->flags |= IPFW_ARGS_IP4;
 		hlen = ip->ip_hl << 2;
@@ -1675,7 +1714,7 @@ do {								\
 			}
 		}
 
-		ip = mtod(m, struct ip *);
+		UPDATE_POINTERS();
 		args->f_id.addr_type = 4;
 		args->f_id.src_ip = ntohl(src_ip.s_addr);
 		args->f_id.dst_ip = ntohl(dst_ip.s_addr);
@@ -1692,7 +1731,6 @@ do {								\
 	args->f_id.proto = proto;
 	args->f_id.src_port = src_port = ntohs(src_port);
 	args->f_id.dst_port = dst_port = ntohs(dst_port);
-	args->f_id.fib = M_GETFIB(m);
 
 	IPFW_PF_RLOCK(chain);
 	if (! V_ipfw_vnet_ready) { /* shutting down, leave NOW. */
@@ -1720,7 +1758,7 @@ do {								\
 		oif = NULL;
 	} else {
 		MPASS(args->flags & IPFW_ARGS_OUT);
-		iif = m->m_pkthdr.rcvif;
+		iif = mem ? NULL : m->m_pkthdr.rcvif;
 		oif = args->ifp;
 	}
 
@@ -1840,7 +1878,7 @@ do {								\
 						((ipfw_insn_mac *)cmd)->addr;
 					u_int32_t *mask = (u_int32_t *)
 						((ipfw_insn_mac *)cmd)->mask;
-					u_int32_t *hdr = (u_int32_t *)args->eh;
+					u_int32_t *hdr = (u_int32_t *)eh;
 
 					match =
 					    ( want[0] == (hdr[0] & mask[0]) &&
@@ -1857,8 +1895,11 @@ do {								\
 
 					for (i = cmdlen - 1; !match && i>0;
 					    i--, p += 2)
-						match = (etype >= p[0] &&
-						    etype <= p[1]);
+						match =
+						    (ntohs(eh->ether_type) >=
+						    p[0] &&
+						    ntohs(eh->ether_type) <=
+						    p[1]);
 				}
 				break;
 
@@ -2332,7 +2373,7 @@ do {								\
 			}
 
 			case O_LOG:
-				ipfw_log(chain, f, hlen, args, m,
+				ipfw_log(chain, f, hlen, args,
 				    offset | ip6f_mf, tablearg, ip);
 				match = 1;
 				break;

Modified: head/sys/netpfil/ipfw/ip_fw_bpf.c
==============================================================================
--- head/sys/netpfil/ipfw/ip_fw_bpf.c	Thu Mar 14 22:32:50 2019	(r345165)
+++ head/sys/netpfil/ipfw/ip_fw_bpf.c	Thu Mar 14 22:52:16 2019	(r345166)
@@ -161,6 +161,28 @@ ipfwlog_clone_create(struct if_clone *ifc, int unit, c
 }
 
 void
+ipfw_bpf_tap(u_char *pkt, u_int pktlen)
+{
+	LOGIF_RLOCK_TRACKER;
+
+	LOGIF_RLOCK();
+	if (V_log_if != NULL)
+		BPF_TAP(V_log_if, pkt, pktlen);
+	LOGIF_RUNLOCK();
+}
+
+void
+ipfw_bpf_mtap(struct mbuf *m)
+{
+	LOGIF_RLOCK_TRACKER;
+
+	LOGIF_RLOCK();
+	if (V_log_if != NULL)
+		BPF_MTAP(V_log_if, m);
+	LOGIF_RUNLOCK();
+}
+
+void
 ipfw_bpf_mtap2(void *data, u_int dlen, struct mbuf *m)
 {
 	struct ifnet *logif;

Modified: head/sys/netpfil/ipfw/ip_fw_log.c
==============================================================================
--- head/sys/netpfil/ipfw/ip_fw_log.c	Thu Mar 14 22:32:50 2019	(r345165)
+++ head/sys/netpfil/ipfw/ip_fw_log.c	Thu Mar 14 22:52:16 2019	(r345166)
@@ -99,30 +99,32 @@ __FBSDID("$FreeBSD$");
  */
 void
 ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen,
-    struct ip_fw_args *args, struct mbuf *m,
-    u_short offset, uint32_t tablearg, struct ip *ip)
+    struct ip_fw_args *args, u_short offset, uint32_t tablearg, struct ip *ip)
 {
 	char *action;
 	int limit_reached = 0;
 	char action2[92], proto[128], fragment[32];
 
 	if (V_fw_verbose == 0) {
-		if (args->flags & IPFW_ARGS_ETHER) /* layer2, use orig hdr */
-			ipfw_bpf_mtap2(args->eh, ETHER_HDR_LEN, m);
+		if (args->flags & IPFW_ARGS_LENMASK)
+			ipfw_bpf_tap(args->mem, IPFW_ARGS_LENGTH(args->flags));
+		else if (args->flags & IPFW_ARGS_ETHER)
+			/* layer2, use orig hdr */
+			ipfw_bpf_mtap(args->m);
 		else {
 			/* Add fake header. Later we will store
 			 * more info in the header.
 			 */
 			if (ip->ip_v == 4)
 				ipfw_bpf_mtap2("DDDDDDSSSSSS\x08\x00",
-				    ETHER_HDR_LEN, m);
+				    ETHER_HDR_LEN, args->m);
 			else if (ip->ip_v == 6)
 				ipfw_bpf_mtap2("DDDDDDSSSSSS\x86\xdd",
-				    ETHER_HDR_LEN, m);
+				    ETHER_HDR_LEN, args->m);
 			else
 				/* Obviously bogus EtherType. */
 				ipfw_bpf_mtap2("DDDDDDSSSSSS\xff\xff",
-				    ETHER_HDR_LEN, m);
+				    ETHER_HDR_LEN, args->m);
 		}
 		return;
 	}

Modified: head/sys/netpfil/ipfw/ip_fw_pfil.c
==============================================================================
--- head/sys/netpfil/ipfw/ip_fw_pfil.c	Thu Mar 14 22:32:50 2019	(r345165)
+++ head/sys/netpfil/ipfw/ip_fw_pfil.c	Thu Mar 14 22:52:16 2019	(r345166)
@@ -328,69 +328,50 @@ again:
  * ipfw processing for ethernet packets (in and out).
  */
 static pfil_return_t
-ipfw_check_frame(struct mbuf **m0, struct ifnet *ifp, int flags,
+ipfw_check_frame(pfil_packet_t p, struct ifnet *ifp, int flags,
     void *ruleset __unused, struct inpcb *inp)
 {
 	struct ip_fw_args args;
-	struct ether_header save_eh;
-	struct ether_header *eh;
-	struct m_tag *mtag;
-	struct mbuf *m;
 	pfil_return_t ret;
-	int i;
+	bool mem, realloc;
+	int ipfw;
 
-	args.flags = IPFW_ARGS_ETHER;
-	args.flags |= (flags & PFIL_IN) ? IPFW_ARGS_IN : IPFW_ARGS_OUT;
-again:
-	/* fetch start point from rule, if any.  remove the tag if present. */
-	mtag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL);
-	if (mtag != NULL) {
-		args.rule = *((struct ipfw_rule_ref *)(mtag+1));
-		m_tag_delete(*m0, mtag);
-		if (args.rule.info & IPFW_ONEPASS)
-			return (0);
-		args.flags |= IPFW_ARGS_REF;
+	if (flags & PFIL_MEMPTR) {
+		mem = true;
+		realloc = false;
+		args.flags = PFIL_LENGTH(flags) | IPFW_ARGS_ETHER;
+		args.mem = p.mem;
+	} else {
+		mem = realloc = false;
+		args.flags = IPFW_ARGS_ETHER;
 	}
-
-	/* I need some amt of data to be contiguous */
-	m = *m0;
-	i = min(m->m_pkthdr.len, max_protohdr);
-	if (m->m_len < i) {
-		m = m_pullup(m, i);
-		if (m == NULL) {
-			*m0 = m;
-			return (0);
-		}
-	}
-	eh = mtod(m, struct ether_header *);
-	save_eh = *eh;			/* save copy for restore below */
-	m_adj(m, ETHER_HDR_LEN);	/* strip ethernet header */
-
-	args.m = m;		/* the packet we are looking at		*/
+	args.flags |= (flags & PFIL_IN) ? IPFW_ARGS_IN : IPFW_ARGS_OUT;
 	args.ifp = ifp;
-	args.eh = &save_eh;	/* MAC header for bridged/MAC packets	*/
-	args.inp = inp;	/* used by ipfw uid/gid/jail rules	*/
-	i = ipfw_chk(&args);
-	m = args.m;
-	if (m != NULL) {
+	args.inp = inp;
+
+again:
+	if (!mem) {
 		/*
-		 * Restore Ethernet header, as needed, in case the
-		 * mbuf chain was replaced by ipfw.
+		 * Fetch start point from rule, if any.
+		 * Remove the tag if present.
 		 */
-		M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT);
-		if (m == NULL) {
-			*m0 = NULL;
-			return (0);
+		struct m_tag *mtag;
+
+		mtag = m_tag_locate(*p.m, MTAG_IPFW_RULE, 0, NULL);
+		if (mtag != NULL) {
+			args.rule = *((struct ipfw_rule_ref *)(mtag+1));
+			m_tag_delete(*p.m, mtag);
+			if (args.rule.info & IPFW_ONEPASS)
+				return (PFIL_PASS);
+			args.flags |= IPFW_ARGS_REF;
 		}
-		if (eh != mtod(m, struct ether_header *))
-			bcopy(&save_eh, mtod(m, struct ether_header *),
-				ETHER_HDR_LEN);
+		args.m = *p.m;
 	}
-	*m0 = m;
 
+	ipfw = ipfw_chk(&args);
+
 	ret = PFIL_PASS;
-	/* Check result of ipfw_chk() */
-	switch (i) {
+	switch (ipfw) {
 	case IP_FW_PASS:
 		break;
 
@@ -403,9 +384,16 @@ again:
 			ret = PFIL_DROPPED;
 			break;
 		}
-		*m0 = NULL;
+		if (mem) {
+			if (pfil_realloc(&p, flags, ifp) != 0) {
+				ret = PFIL_DROPPED;
+				break;
+			}
+			mem = false;
+			realloc = true;
+		}
 		MPASS(args.flags & IPFW_ARGS_REF);
-		ip_dn_io_ptr(&m, &args);
+		ip_dn_io_ptr(p.m, &args);
 		return (PFIL_CONSUMED);
 
 	case IP_FW_NGTEE:
@@ -414,9 +402,17 @@ again:
 			ret = PFIL_DROPPED;
 			break;
 		}
+		if (mem) {
+			if (pfil_realloc(&p, flags, ifp) != 0) {
+				ret = PFIL_DROPPED;
+				break;
+			}
+			mem = false;
+			realloc = true;
+		}
 		MPASS(args.flags & IPFW_ARGS_REF);
-		(void )ng_ipfw_input_p(m0, &args, i == IP_FW_NGTEE);
-		if (i == IP_FW_NGTEE) /* ignore errors for NGTEE */
+		(void )ng_ipfw_input_p(p.m, &args, ipfw == IP_FW_NGTEE);
+		if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */
 			goto again;	/* continue with packet */
 		ret = PFIL_CONSUMED;
 		break;
@@ -425,12 +421,15 @@ again:
 		KASSERT(0, ("%s: unknown retval", __func__));
 	}
 
-	if (ret != PFIL_PASS) {
-		if (*m0)
-			FREE_PKT(*m0);
-		*m0 = NULL;
+	if (!mem && ret != PFIL_PASS) {
+		if (*p.m)
+			FREE_PKT(*p.m);
+		*p.m = NULL;
 	}
 
+	if (realloc && ret == PFIL_PASS)
+		ret = PFIL_REALLOCED;
+
 	return (ret);
 }
 
@@ -545,7 +544,7 @@ ipfw_hook(int onoff, int pf)
 	pfil_hook_t *h;
 
 	pha.pa_version = PFIL_VERSION;
-	pha.pa_flags = PFIL_IN | PFIL_OUT;
+	pha.pa_flags = PFIL_IN | PFIL_OUT | PFIL_MEMPTR;
 	pha.pa_modname = "ipfw";
 	pha.pa_ruleset = NULL;
 

Modified: head/sys/netpfil/ipfw/ip_fw_private.h
==============================================================================
--- head/sys/netpfil/ipfw/ip_fw_private.h	Thu Mar 14 22:32:50 2019	(r345165)
+++ head/sys/netpfil/ipfw/ip_fw_private.h	Thu Mar 14 22:52:16 2019	(r345166)
@@ -111,14 +111,11 @@ struct ip_fw_args {
 	struct inpcb		*inp;
 	union {
 		/*
-		 * We don't support forwarding on layer2, thus we can
-		 * keep eh pointer in this union.
 		 * next_hop[6] pointers can be used to point to next hop
 		 * stored in rule's opcode to avoid copying into hopstore.
 		 * Also, it is expected that all 0x1-0x10 flags are mutually
 		 * exclusive.
 		 */
-		struct ether_header	*eh;	/* for bridged packets	*/
 		struct sockaddr_in	*next_hop;
 		struct sockaddr_in6	*next_hop6;
 		/* ipfw next hop storage */
@@ -129,8 +126,10 @@ struct ip_fw_args {
 			uint16_t	sin6_port;
 		} hopstore6;
 	};
-
-	struct mbuf		*m;	/* the mbuf chain		*/
+	union {
+		struct mbuf	*m;	/* the mbuf chain		*/
+		void		*mem;	/* or memory pointer		*/
+	};
 	struct ipfw_flow_id	f_id;	/* grabbed from IP header	*/
 };
 
@@ -164,10 +163,11 @@ struct ip_fw_chain;
 
 void ipfw_bpf_init(int);
 void ipfw_bpf_uninit(int);
+void ipfw_bpf_tap(u_char *, u_int);
+void ipfw_bpf_mtap(struct mbuf *);
 void ipfw_bpf_mtap2(void *, u_int, struct mbuf *);
 void ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen,
-    struct ip_fw_args *args, struct mbuf *m,
-    u_short offset, uint32_t tablearg, struct ip *ip);
+    struct ip_fw_args *args, u_short offset, uint32_t tablearg, struct ip *ip);
 VNET_DECLARE(u_int64_t, norule_counter);
 #define	V_norule_counter	VNET(norule_counter)
 VNET_DECLARE(int, verbose_limit);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201903142252.x2EMqGss074944>