PERFORCE change 142637 for review

Gleb Kurtsou gk at FreeBSD.org
Sat May 31 16:39:10 UTC 2008


http://perforce.freebsd.org/chv.cgi?CH=142637

Change 142637 by gk at gk_h1 on 2008/05/31 16:38:33

	       Add mtag containing ethernet header into packet.
	       Add layer2 support to pfil (ethernet only).
	       Update ipfw to support layer2 pfil.
	       Eliminate special ipfw handling from for ethernet and bridge, use pfil.
	       Rework ipfw layer2 firewall to separate pure layer2 and mixed layer2-layer3 filtering.
	       ipfw layer2 filter: do not touch layer3 headers.
	       ipfw mixed layer2-layer3: capable of filtering by mac and ip addresses
	
	       TODO:
	       All packets are handled by pfil.
	       dummynet support is untested (especially in bridge).
	       dummynet in bridge undergone some architectural changes. Now layer2 packet is injected to dummynet (and ipfw).
	       Header tag is added only in input direction.

Affected files ...

.. //depot/projects/soc2008/gk_l2filter/sys-net/ethernet.h#2 edit
.. //depot/projects/soc2008/gk_l2filter/sys-net/if_bridge.c#2 edit
.. //depot/projects/soc2008/gk_l2filter/sys-net/if_ethersubr.c#2 edit
.. //depot/projects/soc2008/gk_l2filter/sys-net/pfil.h#2 edit
.. //depot/projects/soc2008/gk_l2filter/sys-netinet/ip_fw.h#2 edit
.. //depot/projects/soc2008/gk_l2filter/sys-netinet/ip_fw2.c#2 edit
.. //depot/projects/soc2008/gk_l2filter/sys-netinet/ip_fw_pfil.c#2 edit

Differences ...

==== //depot/projects/soc2008/gk_l2filter/sys-net/ethernet.h#2 (text+ko) ====

@@ -367,6 +367,10 @@
 } while (0)
 
 #ifdef _KERNEL
+#include <net/pfil.h>
+
+#define	MTAG_ETHER		1080579719
+#define	MTAG_ETHER_HEADER	0
 
 struct ifnet;
 struct mbuf;
@@ -388,6 +392,8 @@
 	    void *, u_int);
 struct mbuf  *ether_vlanencap(struct mbuf *, uint16_t);
 
+extern struct pfil_head ether_pfil_hook;	/* Packet filter hooks */
+
 #else /* _KERNEL */
 
 #include <sys/cdefs.h>

==== //depot/projects/soc2008/gk_l2filter/sys-net/if_bridge.c#2 (text+ko) ====

@@ -124,6 +124,7 @@
 #include <machine/in_cksum.h>
 #include <netinet/if_ether.h> /* for struct arpcom */
 #include <net/bridgestp.h>
+#include <net/ethernet.h> /* for ether_pfil_hook */
 #include <net/if_bridgevar.h>
 #include <net/if_llc.h>
 #include <net/if_vlan_var.h>
@@ -338,15 +339,18 @@
 static int pfil_onlyip = 1; /* only pass IP[46] packets when pfil is enabled */
 static int pfil_bridge = 1; /* run pfil hooks on the bridge interface */
 static int pfil_member = 1; /* run pfil hooks on the member interface */
-static int pfil_ipfw = 0;   /* layer2 filter with ipfw */
-static int pfil_ipfw_arp = 0;   /* layer2 filter with ipfw */
+/* GK_XXX should be if flag */
+static int pfil_layer2 = 0;   /* layer2 filter with PFIL */
+static int pfil_layer2_arp = 0;   /* layer2 filter with PFIL */
 static int pfil_local_phys = 0; /* run pfil hooks on the physical interface for
                                    locally destined packets */
 static int log_stp   = 0;   /* log STP state changes */
 SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_onlyip, CTLFLAG_RW,
     &pfil_onlyip, 0, "Only pass IP packets when pfil is enabled");
-SYSCTL_INT(_net_link_bridge, OID_AUTO, ipfw_arp, CTLFLAG_RW,
-    &pfil_ipfw_arp, 0, "Filter ARP packets through IPFW layer2");
+SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_layer2, CTLFLAG_RW,
+    &pfil_layer2, 0, "Filter packets through PFIL layer2");
+SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_layer2_arp, CTLFLAG_RW,
+    &pfil_layer2_arp, 0, "Filter ARP packets through PFIL layer2");
 SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_bridge, CTLFLAG_RW,
     &pfil_bridge, 0, "Packet filter on the bridge interface");
 SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_member, CTLFLAG_RW,
@@ -504,39 +508,6 @@
 MODULE_DEPEND(if_bridge, bridgestp, 1, 1, 1);
 
 /*
- * handler for net.link.bridge.pfil_ipfw
- */
-static int
-sysctl_pfil_ipfw(SYSCTL_HANDLER_ARGS)
-{
-	int enable = pfil_ipfw;
-	int error;
-
-	error = sysctl_handle_int(oidp, &enable, 0, req);
-	enable = (enable) ? 1 : 0;
-
-	if (enable != pfil_ipfw) {
-		pfil_ipfw = enable;
-
-		/*
-		 * Disable pfil so that ipfw doesnt run twice, if the user
-		 * really wants both then they can re-enable pfil_bridge and/or
-		 * pfil_member. Also allow non-ip packets as ipfw can filter by
-		 * layer2 type.
-		 */
-		if (pfil_ipfw) {
-			pfil_onlyip = 0;
-			pfil_bridge = 0;
-			pfil_member = 0;
-		}
-	}
-
-	return (error);
-}
-SYSCTL_PROC(_net_link_bridge, OID_AUTO, ipfw, CTLTYPE_INT|CTLFLAG_RW,
-	    &pfil_ipfw, 0, &sysctl_pfil_ipfw, "I", "Layer2 filter with IPFW");
-
-/*
  * bridge_clone_create:
  *
  *	Create a new bridge instance.
@@ -1739,11 +1710,7 @@
 		return;
 	}
 
-	if (PFIL_HOOKED(&inet_pfil_hook)
-#ifdef INET6
-	    || PFIL_HOOKED(&inet6_pfil_hook)
-#endif
-	    ) {
+	if (PFIL_HOOKED(&ether_pfil_hook) && pfil_layer2 != 0) {
 		if (bridge_pfil(&m, sc->sc_ifp, ifp, PFIL_OUT) != 0)
 			return;
 		if (m == NULL)
@@ -2872,7 +2839,7 @@
 {
 	int snap, error, i, hlen;
 	struct ether_header *eh1, eh2;
-	struct ip_fw_args args;
+	struct m_tag *mtag_ether_header;
 	struct ip *ip;
 	struct llc llc1;
 	u_int16_t ether_type;
@@ -2885,7 +2852,7 @@
 	KASSERT(M_WRITABLE(*mp), ("%s: modifying a shared mbuf", __func__));
 #endif
 
-	if (pfil_bridge == 0 && pfil_member == 0 && pfil_ipfw == 0)
+	if (pfil_bridge == 0 && pfil_member == 0 && pfil_layer2 == 0)
 		return (0); /* filtering is disabled */
 
 	i = min((*mp)->m_pkthdr.len, max_protohdr);
@@ -2927,7 +2894,7 @@
 	switch (ether_type) {
 		case ETHERTYPE_ARP:
 		case ETHERTYPE_REVARP:
-			if (pfil_ipfw_arp == 0)
+			if (pfil_layer2_arp == 0)
 				return (0); /* Automatically pass */
 			break;
 
@@ -2946,6 +2913,12 @@
 				goto bad;
 	}
 
+	/* GK_XXX */
+	if (PFIL_HOOKED(&ether_pfil_hook) && pfil_layer2 != 0 && dir == PFIL_OUT && ifp != NULL) {
+		if (pfil_run_hooks(&ether_pfil_hook, mp, ifp, PFIL_OUT, NULL) != 0)
+			return EACCES;
+	}
+
 	/* Strip off the Ethernet header and keep a copy. */
 	m_copydata(*mp, 0, ETHER_HDR_LEN, (caddr_t) &eh2);
 	m_adj(*mp, ETHER_HDR_LEN);
@@ -2976,47 +2949,18 @@
 			goto bad;
 	}
 
-	if (IPFW_LOADED && pfil_ipfw != 0 && dir == PFIL_OUT && ifp != NULL) {
-		error = -1;
-		args.rule = ip_dn_claim_rule(*mp);
-		if (args.rule != NULL && fw_one_pass)
-			goto ipfwpass; /* packet already partially processed */
+	error = 0;
 
-		args.m = *mp;
-		args.oif = ifp;
-		args.next_hop = NULL;
-		args.eh = &eh2;
-		args.inp = NULL;	/* used by ipfw uid/gid/jail rules */
-		i = ip_fw_chk_ptr(&args);
-		*mp = args.m;
-
-		if (*mp == NULL)
-			return (error);
-
-		if (DUMMYNET_LOADED && (i == IP_FW_DUMMYNET)) {
-
-			/* put the Ethernet header back on */
-			M_PREPEND(*mp, ETHER_HDR_LEN, M_DONTWAIT);
-			if (*mp == NULL)
-				return (error);
-			bcopy(&eh2, mtod(*mp, caddr_t), ETHER_HDR_LEN);
-
-			/*
-			 * Pass the pkt to dummynet, which consumes it. The
-			 * packet will return to us via bridge_dummynet().
-			 */
-			args.oif = ifp;
-			ip_dn_io_ptr(mp, DN_TO_IFB_FWD, &args);
-			return (error);
-		}
-
-		if (i != IP_FW_PASS) /* drop */
-			goto bad;
+	/* GK_XXX */
+	/*
+	 * XXX: conditionally allocate mtag
+	 */
+	mtag_ether_header = m_tag_alloc(MTAG_ETHER, MTAG_ETHER_HEADER, ETHER_HDR_LEN, M_NOWAIT);
+	if (mtag_ether_header != NULL) {
+		memcpy(mtag_ether_header + 1, &eh2, ETHER_HDR_LEN);
+		m_tag_prepend(*mp, mtag_ether_header);
 	}
 
-ipfwpass:
-	error = 0;
-
 	/*
 	 * Run the packet through pfil
 	 */

==== //depot/projects/soc2008/gk_l2filter/sys-net/if_ethersubr.c#2 (text+ko) ====

@@ -61,14 +61,13 @@
 #include <net/ethernet.h>
 #include <net/if_bridgevar.h>
 #include <net/if_vlan_var.h>
+#include <net/pfil.h>
 #include <net/pf_mtag.h>
 
 #if defined(INET) || defined(INET6)
 #include <netinet/in.h>
 #include <netinet/in_var.h>
 #include <netinet/if_ether.h>
-#include <netinet/ip_fw.h>
-#include <netinet/ip_dummynet.h>
 #endif
 #ifdef INET6
 #include <netinet6/nd6.h>
@@ -132,12 +131,7 @@
 
 #define senderr(e) do { error = (e); goto bad;} while (0)
 
-#if defined(INET) || defined(INET6)
-int
-ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst,
-	struct ip_fw **rule, int shared);
-static int ether_ipfw;
-#endif
+struct pfil_head ether_pfil_hook;	/* Packet filter hooks */
 
 /*
  * Ethernet output routine.
@@ -385,20 +379,13 @@
 int
 ether_output_frame(struct ifnet *ifp, struct mbuf *m)
 {
-	int error;
-#if defined(INET) || defined(INET6)
-	struct ip_fw *rule = ip_dn_claim_rule(m);
+	int error = 0;
 
-	if (IPFW_LOADED && ether_ipfw != 0) {
-		if (ether_ipfw_chk(&m, ifp, &rule, 0) == 0) {
-			if (m) {
-				m_freem(m);
-				return EACCES;	/* pkt dropped */
-			} else
-				return 0;	/* consumed e.g. in a pipe */
-		}
-	}
-#endif
+	/* GK_XXX */
+	if (PFIL_HOOKED(&ether_pfil_hook))
+		error = pfil_run_hooks(&ether_pfil_hook, &m, ifp, PFIL_OUT, NULL);
+	if (m == NULL)
+		return 0;	/* consumed e.g. in a pipe */
 
 	/*
 	 * Queue message on interface, update output statistics if
@@ -408,103 +395,7 @@
 	return (error);
 }
 
-#if defined(INET) || defined(INET6)
 /*
- * ipfw processing for ethernet packets (in and out).
- * The second parameter is NULL from ether_demux, and ifp from
- * ether_output_frame.
- */
-int
-ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst,
-	struct ip_fw **rule, int shared)
-{
-	struct ether_header *eh;
-	struct ether_header save_eh;
-	struct mbuf *m;
-	int i;
-	struct ip_fw_args args;
-
-	if (*rule != NULL && fw_one_pass)
-		return 1; /* dummynet packet, already partially processed */
-
-	/*
-	 * I need some amt of data to be contiguous, and in case others need
-	 * the packet (shared==1) also better be in the first mbuf.
-	 */
-	m = *m0;
-	i = min( m->m_pkthdr.len, max_protohdr);
-	if ( shared || 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.oif = dst;		/* destination, if any			*/
-	args.rule = *rule;	/* matching rule to restart		*/
-	args.next_hop = NULL;	/* we do not support forward yet	*/
-	args.eh = &save_eh;	/* MAC header for bridged/MAC packets	*/
-	args.inp = NULL;	/* used by ipfw uid/gid/jail rules	*/
-	i = ip_fw_chk_ptr(&args);
-	m = args.m;
-	if (m != NULL) {
-		/*
-		 * Restore Ethernet header, as needed, in case the
-		 * mbuf chain was replaced by ipfw.
-		 */
-		M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT);
-		if (m == NULL) {
-			*m0 = m;
-			return 0;
-		}
-		if (eh != mtod(m, struct ether_header *))
-			bcopy(&save_eh, mtod(m, struct ether_header *),
-				ETHER_HDR_LEN);
-	}
-	*m0 = m;
-	*rule = args.rule;
-
-	if (i == IP_FW_DENY) /* drop */
-		return 0;
-
-	KASSERT(m != NULL, ("ether_ipfw_chk: m is NULL"));
-
-	if (i == IP_FW_PASS) /* a PASS rule.  */
-		return 1;
-
-	if (DUMMYNET_LOADED && (i == IP_FW_DUMMYNET)) {
-		/*
-		 * Pass the pkt to dummynet, which consumes it.
-		 * If shared, make a copy and keep the original.
-		 */
-		if (shared) {
-			m = m_copypacket(m, M_DONTWAIT);
-			if (m == NULL)
-				return 0;
-		} else {
-			/*
-			 * Pass the original to dummynet and
-			 * nothing back to the caller
-			 */
-			*m0 = NULL ;
-		}
-		ip_dn_io_ptr(&m, dst ? DN_TO_ETH_OUT: DN_TO_ETH_DEMUX, &args);
-		return 0;
-	}
-	/*
-	 * XXX at some point add support for divert/forward actions.
-	 * If none of the above matches, we have to drop the pkt.
-	 */
-	return 0;
-}
-#endif
-
-/*
  * Process a received Ethernet packet; the packet is in the
  * mbuf chain m with the ethernet header at the front.
  */
@@ -701,6 +592,7 @@
 ether_demux(struct ifnet *ifp, struct mbuf *m)
 {
 	struct ether_header *eh;
+	struct m_tag *mtag_ether_header;
 	int isr;
 	u_short ether_type;
 #if defined(NETATALK)
@@ -709,21 +601,15 @@
 
 	KASSERT(ifp != NULL, ("%s: NULL interface pointer", __func__));
 
-#if defined(INET) || defined(INET6)
 	/*
-	 * Allow dummynet and/or ipfw to claim the frame.
+	 * Allow pfil to claim the frame.
 	 * Do not do this for PROMISC frames in case we are re-entered.
 	 */
-	if (IPFW_LOADED && ether_ipfw != 0 && !(m->m_flags & M_PROMISC)) {
-		struct ip_fw *rule = ip_dn_claim_rule(m);
-
-		if (ether_ipfw_chk(&m, NULL, &rule, 0) == 0) {
-			if (m)
-				m_freem(m);	/* dropped; free mbuf chain */
-			return;			/* consumed */
-		}
+	/* GK_XXX */
+	if (PFIL_HOOKED(&ether_pfil_hook) && !(m->m_flags & M_PROMISC)) {
+		if (pfil_run_hooks(&ether_pfil_hook, &m, ifp, PFIL_IN, NULL) != 0)
+			return;
 	}
-#endif
 	eh = mtod(m, struct ether_header *);
 	ether_type = ntohs(eh->ether_type);
 
@@ -756,6 +642,15 @@
 	}
 
 	/*
+	 * XXX: conditionally allocate mtag
+	 */
+	mtag_ether_header = m_tag_alloc(MTAG_ETHER, MTAG_ETHER_HEADER, ETHER_HDR_LEN, M_NOWAIT);
+	if (mtag_ether_header != NULL) {
+		memcpy(mtag_ether_header + 1, eh, ETHER_HDR_LEN);
+		m_tag_prepend(m, mtag_ether_header);
+	}
+
+	/*
 	 * Reset layer specific mbuf flags to avoid confusing upper layers.
 	 * Strip off Ethernet header.
 	 */
@@ -930,10 +825,6 @@
 
 SYSCTL_DECL(_net_link);
 SYSCTL_NODE(_net_link, IFT_ETHER, ether, CTLFLAG_RW, 0, "Ethernet");
-#if defined(INET) || defined(INET6)
-SYSCTL_INT(_net_link_ether, OID_AUTO, ipfw, CTLFLAG_RW,
-	    &ether_ipfw,0,"Pass ether pkts through firewall");
-#endif
 
 #if 0
 /*
@@ -1189,10 +1080,16 @@
 static int
 ether_modevent(module_t mod, int type, void *data)
 {
+	int err;
 
 	switch (type) {
 	case MOD_LOAD:
 		if_register_com_alloc(IFT_ETHER, ether_alloc, ether_free);
+		ether_pfil_hook.ph_type = PFIL_TYPE_IFT;
+		ether_pfil_hook.ph_af = IFT_ETHER;
+		if ((err = pfil_head_register(&ether_pfil_hook)) != 0)
+			printf("%s: WARNING: unable to register pfil hook, "
+				"error %d\n", __func__, err);
 		break;
 	case MOD_UNLOAD:
 		if_deregister_com_alloc(IFT_ETHER);

==== //depot/projects/soc2008/gk_l2filter/sys-net/pfil.h#2 (text+ko) ====

@@ -63,6 +63,7 @@
 
 #define	PFIL_TYPE_AF		1	/* key is AF_* type */
 #define	PFIL_TYPE_IFNET		2	/* key is ifnet pointer */
+#define	PFIL_TYPE_IFT		3	/* key is IFT_* type */
 
 struct pfil_head {
 	pfil_list_t	ph_in;

==== //depot/projects/soc2008/gk_l2filter/sys-netinet/ip_fw.h#2 (text+ko) ====

@@ -581,6 +581,8 @@
        struct route_in6 ro_pmtu_or;
 };
 
+#define IP_FW_ARGS_LAYER2		0x01
+
 /*
  * Arguments for calling ipfw_chk() and dummynet_io(). We put them
  * all into a structure because this way it is easier and more
@@ -591,7 +593,8 @@
 	struct ifnet	*oif;		/* output interface		*/
 	struct sockaddr_in *next_hop;	/* forward address		*/
 	struct ip_fw	*rule;		/* matching rule		*/
-	struct ether_header *eh;	/* for bridged packets		*/
+	struct ether_header *eh;	/* for saved ethernet header	*/
+	int flags;
 
 	struct ipfw_flow_id f_id;	/* grabbed from IP header	*/
 	u_int32_t	cookie;		/* a cookie depending on rule action */
@@ -611,6 +614,8 @@
 
 int ipfw_check_in(void *, struct mbuf **, struct ifnet *, int, struct inpcb *inp);
 int ipfw_check_out(void *, struct mbuf **, struct ifnet *, int, struct inpcb *inp);
+int ipfw_ether_check_in(void *, struct mbuf **, struct ifnet *, int, struct inpcb *inp);
+int ipfw_ether_check_out(void *, struct mbuf **, struct ifnet *, int, struct inpcb *inp);
 
 int ipfw_chk(struct ip_fw_args *);
 

==== //depot/projects/soc2008/gk_l2filter/sys-netinet/ip_fw2.c#2 (text+ko) ====

@@ -753,7 +753,6 @@
     struct mbuf *m, struct ifnet *oif, u_short offset, uint32_t tablearg,
     struct ip *ip)
 {
-	struct ether_header *eh = args->eh;
 	char *action;
 	int limit_reached = 0;
 	char action2[40], proto[128], fragment[32];
@@ -982,13 +981,8 @@
 #endif
 		{
 			int ip_off, ip_len;
-			if (eh != NULL) { /* layer 2 packets are as on the wire */
-				ip_off = ntohs(ip->ip_off);
-				ip_len = ntohs(ip->ip_len);
-			} else {
-				ip_off = ip->ip_off;
-				ip_len = ip->ip_len;
-			}
+			ip_off = ip->ip_off;
+			ip_len = ip->ip_len;
 			if (ip_off & (IP_MF | IP_OFFMASK))
 				snprintf(SNPARGS(fragment, 0),
 				    " (frag %d:%d@%d%s)",
@@ -1699,11 +1693,6 @@
 		m_adj(m, args->L3offset);
 #endif
 	if (code != ICMP_REJECT_RST) { /* Send an ICMP unreach */
-		/* We need the IP header in host order for icmp_error(). */
-		if (args->eh != NULL) {
-			ip->ip_len = ntohs(ip->ip_len);
-			ip->ip_off = ntohs(ip->ip_off);
-		}
 		icmp_error(args->m, ICMP_UNREACH, code, 0L, 0);
 	} else if (args->f_id.proto == IPPROTO_TCP) {
 		struct tcphdr *const tcp =
@@ -2110,7 +2099,7 @@
 	 *	supposed to start with the ip header).
 	 */
 	struct mbuf *m = args->m;
-	struct ip *ip = mtod(m, struct ip *);
+	struct ip *ip = NULL;
 
 	/*
 	 * For rules which contain uid/gid or jail constraints, cache
@@ -2206,6 +2195,9 @@
 	if (m->m_flags & M_SKIP_FIREWALL)
 		return (IP_FW_PASS);	/* accept */
 
+	if ((args->flags & IP_FW_ARGS_LAYER2) == 0)
+		ip = mtod(m, struct ip *);
+
 	pktlen = m->m_pkthdr.len;
 	args->f_id.fib = M_GETFIB(m); /* note mbuf not altered) */
 	proto = args->f_id.proto = 0;	/* mark f_id invalid */
@@ -2236,7 +2228,7 @@
 
 	/* Identify IP packets and fill up variables. */
 	if (pktlen >= sizeof(struct ip6_hdr) &&
-	    (args->eh == NULL || etype == ETHERTYPE_IPV6) && ip->ip_v == 6) {
+	    (args->flags & IP_FW_ARGS_LAYER2) == 0 && ip->ip_v == 6) {
 		struct ip6_hdr *ip6 = (struct ip6_hdr *)ip;
 		is_ipv6 = 1;
 		args->f_id.addr_type = 6;
@@ -2401,7 +2393,7 @@
 		args->f_id.dst_ip = 0;
 		args->f_id.flow_id6 = ntohl(ip6->ip6_flow);
 	} else if (pktlen >= sizeof(struct ip) &&
-	    (args->eh == NULL || etype == ETHERTYPE_IP) && ip->ip_v == 4) {
+	    (args->flags & IP_FW_ARGS_LAYER2) == 0 && ip->ip_v == 4) {
 	    	is_ipv4 = 1;
 		hlen = ip->ip_hl << 2;
 		args->f_id.addr_type = 4;
@@ -2412,13 +2404,8 @@
 		proto = ip->ip_p;
 		src_ip = ip->ip_src;
 		dst_ip = ip->ip_dst;
-		if (args->eh != NULL) { /* layer 2 packets are as on the wire */
-			offset = ntohs(ip->ip_off) & IP_OFFMASK;
-			ip_len = ntohs(ip->ip_len);
-		} else {
-			offset = ip->ip_off & IP_OFFMASK;
-			ip_len = ip->ip_len;
-		}
+		offset = ip->ip_off & IP_OFFMASK;
+		ip_len = ip->ip_len;
 		pktlen = ip_len < pktlen ? ip_len : pktlen;
 
 		if (offset == 0) {
@@ -2484,7 +2471,7 @@
 		int skipto = mtag ? divert_cookie(mtag) : 0;
 
 		f = chain->rules;
-		if (args->eh == NULL && skipto != 0) {
+		if ((args->flags & IP_FW_ARGS_LAYER2) == 0 && skipto != 0) {
 			if (skipto >= IPFW_DEFAULT_RULE) {
 				IPFW_RUNLOCK(chain);
 				return (IP_FW_DENY); /* invalid */
@@ -2612,7 +2599,7 @@
 				break;
 
 			case O_MAC_TYPE:
-				if (args->eh != NULL) {
+				if (args->eh != NULL) {	/* have MAC header */
 					u_int16_t *p =
 					    ((ipfw_insn_u16 *)cmd)->ports;
 					int i;
@@ -3196,7 +3183,7 @@
 			case O_TEE: {
 				struct divert_tag *dt;
 
-				if (args->eh) /* not on layer 2 */
+				if (args->flags & IP_FW_ARGS_LAYER2) /* not valid on layer2 pkts */
 					break;
 				mtag = m_tag_get(PACKET_TAG_DIVERT,
 						sizeof(struct divert_tag),
@@ -3268,7 +3255,7 @@
 			case O_FORWARD_IP: {
 				struct sockaddr_in *sa;
 				sa = &(((ipfw_insn_sa *)cmd)->sa);
-				if (args->eh)	/* not valid on layer2 pkts */
+				if (args->flags & IP_FW_ARGS_LAYER2) /* not valid on layer2 pkts */
 					break;
 				if (!q || dyn_dir == MATCH_FORWARD) {
 					if (sa->sin_addr.s_addr == INADDR_ANY) {

==== //depot/projects/soc2008/gk_l2filter/sys-netinet/ip_fw_pfil.c#2 (text+ko) ====

@@ -49,6 +49,7 @@
 #include <sys/ucred.h>
 
 #include <net/if.h>
+#include <net/if_types.h>
 #include <net/route.h>
 #include <net/pfil.h>
 
@@ -60,6 +61,7 @@
 #include <netinet/ip_fw.h>
 #include <netinet/ip_divert.h>
 #include <netinet/ip_dummynet.h>
+#include <netinet/if_ether.h>
 
 #include <netgraph/ng_ipfw.h>
 
@@ -92,7 +94,9 @@
 {
 	struct ip_fw_args args;
 	struct ng_ipfw_tag *ng_tag;
+	struct m_tag *tag_ether_hdr;
 	struct m_tag *dn_tag;
+	struct ether_header eh;
 	int ipfw = 0;
 	int divert;
 	int tee;
@@ -113,6 +117,14 @@
 		m_tag_delete(*m0, (struct m_tag *)ng_tag);
 	}
 
+	tag_ether_hdr = m_tag_locate(*m0, MTAG_ETHER, MTAG_ETHER_HEADER,
+	    NULL);
+	if (tag_ether_hdr != NULL) {
+		eh = *(struct ether_header *)(tag_ether_hdr + 1);
+		args.eh = &eh;
+		m_tag_delete(*m0, tag_ether_hdr);
+	}
+
 again:
 	dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
 	if (dn_tag != NULL){
@@ -421,33 +433,157 @@
 	return 1;
 }
 
+int
+ipfw_ether_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
+    struct inpcb *inp)
+{
+	struct ip_fw_args args;
+	int error;
+
+	KASSERT(dir == PFIL_IN, ("ipfw_ether_check_in wrong direction!"));
+
+	bzero(&args, sizeof(args));
+
+	args.rule = ip_dn_claim_rule(*m0);
+	if (args.rule != NULL && fw_one_pass)
+		return 0; /* packet already partially processed */
+
+	args.m = *m0;
+	/*
+	args.oif = ifp;
+	*/
+	/* GK_XXX */
+	/*
+	 * perform layer2 filtering only
+	 */
+	args.flags = IP_FW_ARGS_LAYER2;
+	args.eh = mtod(*m0, struct ether_header *);
+	args.inp = inp;
+	error = ip_fw_chk_ptr(&args);
+	*m0 = args.m;
+	printf("IN  %6D -> %6D: %s\n", 
+			args.eh->ether_shost, ":", 
+			args.eh->ether_dhost, ":",
+			(error == IP_FW_PASS ? "passed" : "droped"));
+
+	if (error == IP_FW_PASS)
+		return 0;
+
+	if (DUMMYNET_LOADED && (error == IP_FW_DUMMYNET)) {
+		ip_dn_io_ptr(m0, DN_TO_ETH_DEMUX, &args);
+		return 0;
+	}
+
+	/*
+	 * XXX at some point add support for divert/forward actions.
+	 * If none of the above matches, we have to drop the pkt.
+	 */
+
+	if (*m0)
+		m_freem(*m0);
+	*m0 = NULL;
+	return (EACCES);
+}
+
+int
+ipfw_ether_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
+    struct inpcb *inp)
+{
+	struct ip_fw_args args;
+	int error;
+
+	KASSERT(dir == PFIL_OUT, ("ipfw_ether_check_out wrong direction!"));
+
+	bzero(&args, sizeof(args));
+
+	args.rule = ip_dn_claim_rule(*m0);
+	if (args.rule != NULL && fw_one_pass)
+		return 0; /* packet already partially processed */
+
+	args.m = *m0;
+	args.oif = ifp;
+	/* GK_XXX */
+	/*
+	 * perform layer2 filtering only
+	 */
+	args.flags = IP_FW_ARGS_LAYER2;
+	args.eh = mtod(*m0, struct ether_header *);
+	args.inp = inp;
+	error = ip_fw_chk_ptr(&args);
+	*m0 = args.m;
+	printf("OUT %6D -> %6D: %s\n", 
+			args.eh->ether_shost, ":", 
+			args.eh->ether_dhost, ":",
+			(error == IP_FW_PASS ? "passed" : "droped"));
+
+	if (error == IP_FW_PASS)
+		return 0;
+
+	if (DUMMYNET_LOADED && (error == IP_FW_DUMMYNET)) {
+		int dn_dir;
+
+		if (ifp->if_type == IFT_BRIDGE)
+			dn_dir = DN_TO_IFB_FWD;
+		else
+			dn_dir = DN_TO_ETH_OUT;
+		ip_dn_io_ptr(m0, dn_dir, &args);
+		return 0;
+	}
+
+	/*
+	 * XXX at some point add support for divert/forward actions.
+	 * If none of the above matches, we have to drop the pkt.
+	 */
+
+	if (*m0)
+		m_freem(*m0);
+	*m0 = NULL;
+	return (EACCES);
+}
+
 static int
 ipfw_hook(void)
 {
-	struct pfil_head *pfh_inet;
+	struct pfil_head *pfh_inet, *pfh_ether;
 
 	pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
-	if (pfh_inet == NULL)
+	if (pfh_inet == NULL) {
+		pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet);
+		pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet);
+	}
+
+	pfh_ether = pfil_head_get(PFIL_TYPE_IFT, IFT_ETHER);
+	if (pfh_ether != NULL) {
+		pfil_add_hook(ipfw_ether_check_in, NULL, PFIL_IN, pfh_ether);
+		pfil_add_hook(ipfw_ether_check_out, NULL, PFIL_OUT, pfh_ether);
+	}
+
+	if (pfh_inet == NULL || pfh_ether == NULL)
 		return ENOENT;
 
-	pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet);
-	pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet);
-
 	return 0;
 }
 
 static int
 ipfw_unhook(void)
 {
-	struct pfil_head *pfh_inet;
+	struct pfil_head *pfh_inet, *pfh_ether;
 
 	pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
-	if (pfh_inet == NULL)
+	if (pfh_inet != NULL) {
+		pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet);
+		pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet);
+	}
+
+	pfh_ether = pfil_head_get(PFIL_TYPE_IFT, IFT_ETHER);
+	if (pfh_ether != NULL) {
+		pfil_remove_hook(ipfw_ether_check_in, NULL, PFIL_IN , pfh_ether);
+		pfil_remove_hook(ipfw_ether_check_out, NULL, PFIL_OUT, pfh_ether);
+	}
+
+	if (pfh_inet == NULL || pfh_ether == NULL)
 		return ENOENT;
 
-	pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet);
-	pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet);
-
 	return 0;
 }
 


More information about the p4-projects mailing list