Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 25 Jan 2009 19:25:41 +0000 (UTC)
From:      Andrew Thompson <thompsa@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r187696 - in projects/l2filter/sys: net netinet
Message-ID:  <200901251925.n0PJPfEu025860@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: thompsa
Date: Sun Jan 25 19:25:41 2009
New Revision: 187696
URL: http://svn.freebsd.org/changeset/base/187696

Log:
  Fix up merge conflicts (vimage, arpv2).
  
  Submitted by:	Gleb Kurtsou

Modified:
  projects/l2filter/sys/net/if_bridge.c
  projects/l2filter/sys/net/if_ethersubr.c
  projects/l2filter/sys/netinet/ip_fw2.c
  projects/l2filter/sys/netinet/ip_fw_pfil.c

Modified: projects/l2filter/sys/net/if_bridge.c
==============================================================================
--- projects/l2filter/sys/net/if_bridge.c	Sun Jan 25 19:18:20 2009	(r187695)
+++ projects/l2filter/sys/net/if_bridge.c	Sun Jan 25 19:25:41 2009	(r187696)
@@ -3013,6 +3013,7 @@ bridge_pfil(struct mbuf **mp, struct ifn
 	}
 
 	error = 0;
+
 	/* Add tag if member or bridge interface has IFF_L2TAG set */
 	if (((bifp ? bifp->if_flags : 0) | (ifp ? ifp->if_flags : 0)) & IFF_L2TAG) {
 		mtag_ether_header = m_tag_locate(*mp, MTAG_ETHER, MTAG_ETHER_HEADER,

Modified: projects/l2filter/sys/net/if_ethersubr.c
==============================================================================
--- projects/l2filter/sys/net/if_ethersubr.c	Sun Jan 25 19:18:20 2009	(r187695)
+++ projects/l2filter/sys/net/if_ethersubr.c	Sun Jan 25 19:25:41 2009	(r187696)
@@ -65,6 +65,7 @@
 #include <net/if_bridgevar.h>
 #include <net/if_vlan_var.h>
 #include <net/if_llatbl.h>
+#include <net/pfil.h>
 #include <net/pf_mtag.h>
 #include <net/vnet.h>
 
@@ -72,8 +73,6 @@
 #include <netinet/in.h>
 #include <netinet/in_var.h>
 #include <netinet/if_ether.h>
-#include <netinet/ip_fw.h>
-#include <netinet/ip_dummynet.h>
 #include <netinet/vinet.h>
 #endif
 #ifdef INET6
@@ -144,15 +143,7 @@ MALLOC_DEFINE(M_ARPCOM, "arpcom", "802.*
 
 #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);
-#ifdef VIMAGE_GLOBALS
-static int ether_ipfw;
-#endif
-#endif
-
+struct pfil_head ether_pfil_hook;	/* Packet filter hooks */
 
 /*
  * Ethernet output routine.
@@ -412,20 +403,12 @@ bad:			if (m != NULL)
 int
 ether_output_frame(struct ifnet *ifp, struct mbuf *m)
 {
-#if defined(INET) || defined(INET6)
-	INIT_VNET_NET(ifp->if_vnet);
-	struct ip_fw *rule = ip_dn_claim_rule(m);
+	int error = 0;
 
-	if (IPFW_LOADED && V_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
+	if (PFIL_HOOKED(&ether_pfil_hook) && (ifp->if_flags & IFF_L2FILTER))
+		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
@@ -434,103 +417,6 @@ ether_output_frame(struct ifnet *ifp, st
 	return ((ifp->if_transmit)(ifp, m));
 }
 
-#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)
-{
-	INIT_VNET_INET(dst->if_vnet);
-	struct ether_header *eh;
-	struct ether_header save_eh;
-	struct mbuf *m;
-	int i;
-	struct ip_fw_args args;
-
-	if (*rule != NULL && V_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.
@@ -728,6 +614,7 @@ void
 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)
@@ -736,22 +623,16 @@ ether_demux(struct ifnet *ifp, struct mb
 
 	KASSERT(ifp != NULL, ("%s: NULL interface pointer", __func__));
 
-#if defined(INET) || defined(INET6)
-	INIT_VNET_NET(ifp->if_vnet);
 	/*
-	 * 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 && V_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 */
-		}
+	if (PFIL_HOOKED(&ether_pfil_hook) && (ifp->if_flags & IFF_L2FILTER) &&
+			!(m->m_flags & M_PROMISC)) {
+		if (pfil_run_hooks(&ether_pfil_hook, &m, ifp, PFIL_IN, NULL) != 0 ||
+				m == NULL)
+			return;
 	}
-#endif
 	eh = mtod(m, struct ether_header *);
 	ether_type = ntohs(eh->ether_type);
 
@@ -783,6 +664,14 @@ ether_demux(struct ifnet *ifp, struct mb
 		return;
 	}
 
+	if (ifp->if_flags & IFF_L2TAG) {
+		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.
@@ -958,10 +847,6 @@ ether_ifdetach(struct ifnet *ifp)
 
 SYSCTL_DECL(_net_link);
 SYSCTL_NODE(_net_link, IFT_ETHER, ether, CTLFLAG_RW, 0, "Ethernet");
-#if defined(INET) || defined(INET6)
-SYSCTL_V_INT(V_NET, vnet_net, _net_link_ether, OID_AUTO, ipfw, CTLFLAG_RW,
-	     ether_ipfw, 0, "Pass ether pkts through firewall");
-#endif
 
 #if 0
 /*
@@ -1217,10 +1102,16 @@ ether_free(void *com, u_char type)
 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);

Modified: projects/l2filter/sys/netinet/ip_fw2.c
==============================================================================
--- projects/l2filter/sys/netinet/ip_fw2.c	Sun Jan 25 19:18:20 2009	(r187695)
+++ projects/l2filter/sys/netinet/ip_fw2.c	Sun Jan 25 19:25:41 2009	(r187696)
@@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/module.h>
 #include <sys/priv.h>
 #include <sys/proc.h>
+#include <sys/refcount.h>
 #include <sys/rwlock.h>
 #include <sys/socket.h>
 #include <sys/socketvar.h>
@@ -66,6 +67,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/ucred.h>
 #include <sys/vimage.h>
 #include <net/if.h>
+#include <net/if_arp.h>
 #include <net/radix.h>
 #include <net/route.h>
 #include <net/pf_mtag.h>
@@ -162,9 +164,47 @@ ipfw_nat_cfg_t *ipfw_nat_del_ptr;
 ipfw_nat_cfg_t *ipfw_nat_get_cfg_ptr;
 ipfw_nat_cfg_t *ipfw_nat_get_log_ptr;
 
+static __inline int ether_addr_allow(ipfw_ether_addr *want, 
+	ipfw_ether_addr *a)
+{
+	static ipfw_ether_addr mask = {
+		.octet = { 0xff, 0xff, 0xff, 0xff, 0xff,0xff },
+		.flags = 0
+	};
+	if ((want->flags & IPFW_EA_CHECK) == 0)
+		return (1);
+
+	if ((a->flags & IPFW_EA_CHECK) == 0)
+		return (0);
+
+	if (want->flags & IPFW_EA_MULTICAST) {
+		return (ETHER_IS_MULTICAST(a->octet));
+	}
+	
+#define EA_CMP(a) (*((u_int64_t*)(a)) & *((u_int64_t*)&mask))
+	return (EA_CMP(want) == EA_CMP(a));
+#undef EA_CMP
+}
+
+static __inline int ether_addr_allow_dyn(ipfw_ether_addr *want, ipfw_ether_addr *a)
+{
+	if ((a->flags & IPFW_EA_CHECK) == 0) {
+		return (1);
+	}
+	return (ether_addr_allow(want, a));
+}
+
+struct table_entry_addr {
+	u_char			len;
+	u_char			__reserved;
+	struct ether_addr	ether_addr;
+	in_addr_t		in_addr;
+};
+
 struct table_entry {
-	struct radix_node	rn[2];
-	struct sockaddr_in	addr, mask;
+	struct radix_node	in_rn[2], ether_rn[2];
+	struct table_entry_addr	addr, mask;
+	int			refcnt;
 	u_int32_t		value;
 };
 
@@ -744,18 +784,6 @@ send_reject6(struct ip_fw_args *args, in
 		 */
 		tcp_respond(NULL, ip6, tcp, m, ack, seq, flags);
 	} else if (code != ICMP6_UNREACH_RST) { /* Send an ICMPv6 unreach. */
-#if 0
-		/*
-		 * Unlike above, the mbufs need to line up with the ip6 hdr,
-		 * as the contents are read. We need to m_adj() the
-		 * needed amount.
-		 * The mbuf will however be thrown away so we can adjust it.
-		 * Remember we did an m_pullup on it already so we
-		 * can make some assumptions about contiguousness.
-		 */
-		if (args->L3offset)
-			m_adj(m, args->L3offset);
-#endif
 		icmp6_error(m, ICMP6_DST_UNREACH, code, 0);
 	} else
 		m_freem(m);
@@ -782,7 +810,6 @@ ipfw_log(struct ip_fw *f, u_int hlen, st
     struct ip *ip)
 {
 	INIT_VNET_IPFW(curvnet);
-	struct ether_header *eh = args->eh;
 	char *action;
 	int limit_reached = 0;
 	char action2[40], proto[128], fragment[32];
@@ -909,7 +936,7 @@ ipfw_log(struct ip_fw *f, u_int hlen, st
 	}
 
 	if (hlen == 0) {	/* non-ip */
-		snprintf(SNPARGS(proto, 0), "MAC");
+		snprintf(SNPARGS(proto, 0), "ether");
 
 	} else {
 		int len;
@@ -1011,13 +1038,8 @@ ipfw_log(struct ip_fw *f, u_int hlen, st
 #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)",
@@ -1244,7 +1266,21 @@ next:
 	if (q == NULL)
 		goto done; /* q = NULL, not found */
 
-	if ( prev != NULL) { /* found and not in front */
+	/*
+	 * Only check {src,dst}_ether if it was specified in rule and packet
+	 * mbuf has mtag_ether_header.
+	 */
+	if (dir == MATCH_NONE ||
+	    !ether_addr_allow_dyn(&q->id.src_ether,
+	    (dir == MATCH_FORWARD ? &pkt->src_ether : &pkt->dst_ether)) ||
+	    !ether_addr_allow_dyn(&q->id.dst_ether,
+	    (dir == MATCH_FORWARD ? &pkt->dst_ether : &pkt->src_ether))) {
+		q = NULL;
+		dir = MATCH_NONE;
+		goto done;
+	}
+
+	if (prev != NULL) { /* found and not in front */
 		prev->next = q->next;
 		q->next = V_ipfw_dyn_v[i];
 		V_ipfw_dyn_v[i] = q;
@@ -1370,7 +1406,7 @@ realloc_dynamic_table(void)
  * - "parent" rules for the above (O_LIMIT_PARENT).
  */
 static ipfw_dyn_rule *
-add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type, struct ip_fw *rule)
+add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type, struct ip_fw *rule, uint32_t stateopts)
 {
 	INIT_VNET_IPFW(curvnet);
 	ipfw_dyn_rule *r;
@@ -1403,6 +1439,10 @@ add_dyn_rule(struct ipfw_flow_id *id, u_
 	}
 
 	r->id = *id;
+	if ((stateopts & IP_FW_STATEOPT_ETHER) == 0) {
+		r->id.src_ether.flags = 0;
+		r->id.dst_ether.flags = 0;
+	}
 	r->expire = time_uptime + V_dyn_syn_lifetime;
 	r->rule = rule;
 	r->dyn_type = dyn_type;
@@ -1426,7 +1466,7 @@ add_dyn_rule(struct ipfw_flow_id *id, u_
  * If the lookup fails, then install one.
  */
 static ipfw_dyn_rule *
-lookup_dyn_parent(struct ipfw_flow_id *pkt, struct ip_fw *rule)
+lookup_dyn_parent(struct ipfw_flow_id *pkt, struct ip_fw *rule, uint32_t stateopts)
 {
 	INIT_VNET_IPFW(curvnet);
 	ipfw_dyn_rule *q;
@@ -1459,7 +1499,7 @@ lookup_dyn_parent(struct ipfw_flow_id *p
 				return q;
 			}
 	}
-	return add_dyn_rule(pkt, O_LIMIT_PARENT, rule);
+	return add_dyn_rule(pkt, O_LIMIT_PARENT, rule, stateopts);
 }
 
 /**
@@ -1469,7 +1509,7 @@ lookup_dyn_parent(struct ipfw_flow_id *p
  * session limitations are enforced.
  */
 static int
-install_state(struct ip_fw *rule, ipfw_insn_limit *cmd,
+install_state(struct ip_fw *rule, uint32_t stateopts, ipfw_insn_limit *cmd,
     struct ip_fw_args *args, uint32_t tablearg)
 {
 	INIT_VNET_IPFW(curvnet);
@@ -1517,7 +1557,7 @@ install_state(struct ip_fw *rule, ipfw_i
 
 	switch (cmd->o.opcode) {
 	case O_KEEP_STATE:	/* bidir rule */
-		add_dyn_rule(&args->f_id, O_KEEP_STATE, rule);
+		add_dyn_rule(&args->f_id, O_KEEP_STATE, rule, stateopts);
 		break;
 
 	case O_LIMIT: {		/* limit number of sessions */
@@ -1558,7 +1598,7 @@ install_state(struct ip_fw *rule, ipfw_i
 			id.src_port = args->f_id.src_port;
 		if (limit_mask & DYN_DST_PORT)
 			id.dst_port = args->f_id.dst_port;
-		if ((parent = lookup_dyn_parent(&id, rule)) == NULL) {
+		if ((parent = lookup_dyn_parent(&id, rule, stateopts)) == NULL) {
 			printf("ipfw: %s: add parent failed\n", __func__);
 			IPFW_DYN_UNLOCK();
 			return (1);
@@ -1605,7 +1645,7 @@ install_state(struct ip_fw *rule, ipfw_i
 				return (1);
 			}
 		}
-		add_dyn_rule(&args->f_id, O_LIMIT, (struct ip_fw *)parent);
+		add_dyn_rule(&args->f_id, O_LIMIT, (struct ip_fw *)parent, stateopts);
 		break;
 	}
 	default:
@@ -1725,22 +1765,7 @@ static void
 send_reject(struct ip_fw_args *args, int code, int ip_len, struct ip *ip)
 {
 
-#if 0
-	/* XXX When ip is not guaranteed to be at mtod() we will
-	 * need to account for this */
-	 * The mbuf will however be thrown away so we can adjust it.
-	 * Remember we did an m_pullup on it already so we
-	 * can make some assumptions about contiguousness.
-	 */
-	if (args->L3offset)
-		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 =
@@ -1807,86 +1832,149 @@ lookup_next_rule(struct ip_fw *me, u_int
 	return rule;
 }
 
+static void
+init_table_entry_addr(struct table_entry_addr *addr, struct table_entry_addr *mask,
+    in_addr_t in_addr, uint8_t mlen, ipfw_ether_addr *ether_addr)
+{
+	addr->len = mask->len = sizeof(struct table_entry_addr);
+	mask->in_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
+	addr->in_addr = in_addr & mask->in_addr;
+	if (ether_addr && (ether_addr->flags & IPFW_EA_CHECK)) {
+		if (ether_addr->flags & IPFW_EA_MULTICAST) {
+			bzero(addr->ether_addr.octet, ETHER_ADDR_LEN);
+			addr->ether_addr.octet[0] = 0x01;
+			bzero(mask->ether_addr.octet, ETHER_ADDR_LEN);
+			mask->ether_addr.octet[0] = 0x01;
+		} else {
+			memcpy(addr->ether_addr.octet, ether_addr->octet, ETHER_ADDR_LEN);
+			memset(mask->ether_addr.octet, 0xff, ETHER_ADDR_LEN);
+		}
+	} else {
+		/* set any ether addr */
+		bzero(addr->ether_addr.octet, ETHER_ADDR_LEN);
+		memset(mask->ether_addr.octet, 0xff, ETHER_ADDR_LEN);
+	}
+}
+
+static __inline struct table_entry *
+__rn_to_table_entry(struct radix_node *_rn, int off)
+{
+	char *rn = (char*) _rn;
+
+	if (rn == NULL)
+		return NULL;
+	return (struct table_entry*)(rn - off);
+
+}
+
+#define RN_TO_ENT(e, r) (__rn_to_table_entry(e, __offsetof(struct table_entry, r)))
+
+static __inline void
+release_table_entry(struct ipfw_table_head *th, struct table_entry *ent)
+{
+	IPFW_WLOCK_ASSERT(&V_layer3_chain);	/* FIXME */
+
+	if (refcount_release(&ent->refcnt)) {
+		if (ent->in_rn[0].rn_flags)
+			th->in_rnh->rnh_deladdr(&ent->addr, &ent->mask, th->in_rnh);
+		free(ent, M_IPFW_TBL);
+	}
+}
+
 static int
 add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
-    uint8_t mlen, uint32_t value)
+    uint8_t mlen, ipfw_ether_addr *ether_addr, uint32_t value)
 {
 	INIT_VNET_IPFW(curvnet);
-	struct radix_node_head *rnh;
-	struct table_entry *ent;
-	struct radix_node *rn;
+	struct ipfw_table_head *th;
+	struct table_entry *ent, *in_ent;
 
 	if (tbl >= IPFW_TABLES_MAX)
 		return (EINVAL);
-	rnh = ch->tables[tbl];
+	th = &ch->tables[tbl];
 	ent = malloc(sizeof(*ent), M_IPFW_TBL, M_NOWAIT | M_ZERO);
 	if (ent == NULL)
 		return (ENOMEM);
+	refcount_init(&ent->refcnt, 1);
 	ent->value = value;
-	ent->addr.sin_len = ent->mask.sin_len = 8;
-	ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
-	ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr;
+	init_table_entry_addr(&ent->addr, &ent->mask, addr, mlen, ether_addr);
 	IPFW_WLOCK(ch);
-	rn = rnh->rnh_addaddr(&ent->addr, &ent->mask, rnh, (void *)ent);
-	if (rn == NULL) {
+	if (th->ether_rnh->rnh_addaddr(&ent->addr, &ent->mask, th->ether_rnh, 
+	    ent->ether_rn) == NULL) {
 		IPFW_WUNLOCK(ch);
 		free(ent, M_IPFW_TBL);
 		return (EEXIST);
 	}
+	in_ent = RN_TO_ENT(th->in_rnh->rnh_lookup(&ent->addr, &ent->mask, th->in_rnh),
+	    in_rn);
+	if (in_ent == NULL) {
+		in_ent = RN_TO_ENT(th->in_rnh->rnh_addaddr(&ent->addr, &ent->mask,
+		    th->in_rnh, ent->in_rn), in_rn);
+		if (in_ent == NULL) {
+			th->ether_rnh->rnh_deladdr(&ent->addr, &ent->mask, th->ether_rnh);
+			IPFW_WUNLOCK(ch);
+			free(ent, M_IPFW_TBL);
+			return (EEXIST);
+		}
+	}
+	refcount_acquire(&in_ent->refcnt);
 	IPFW_WUNLOCK(ch);
 	return (0);
 }
 
+static __inline int
+delete_table_entry_rn(struct ipfw_table_head *th, void *addr, void *mask)
+{
+	struct table_entry *ent, *in_ent;
+
+	ent = RN_TO_ENT(th->ether_rnh->rnh_deladdr(addr, mask, th->ether_rnh),
+	    ether_rn);
+	if (ent == NULL)
+		return (ESRCH);
+	in_ent = RN_TO_ENT(th->in_rnh->rnh_lookup(&ent->addr, &ent->mask, th->in_rnh),
+	    in_rn);
+	release_table_entry(th, in_ent);
+	release_table_entry(th, ent);
+	return (0);
+}
+
 static int
 del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
-    uint8_t mlen)
+    uint8_t mlen, ipfw_ether_addr *ether_addr)
 {
-	struct radix_node_head *rnh;
-	struct table_entry *ent;
-	struct sockaddr_in sa, mask;
+	struct ipfw_table_head *th;
+	struct table_entry_addr sa, mask;
+	int err;
 
 	if (tbl >= IPFW_TABLES_MAX)
 		return (EINVAL);
-	rnh = ch->tables[tbl];
-	sa.sin_len = mask.sin_len = 8;
-	mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
-	sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr;
+	th = &ch->tables[tbl];
+	init_table_entry_addr(&sa, &mask, addr, mlen, ether_addr);
 	IPFW_WLOCK(ch);
-	ent = (struct table_entry *)rnh->rnh_deladdr(&sa, &mask, rnh);
-	if (ent == NULL) {
-		IPFW_WUNLOCK(ch);
-		return (ESRCH);
-	}
+	err = delete_table_entry_rn(th, &sa, &mask);
 	IPFW_WUNLOCK(ch);
-	free(ent, M_IPFW_TBL);
-	return (0);
+	return (err);
 }
 
 static int
 flush_table_entry(struct radix_node *rn, void *arg)
 {
-	struct radix_node_head * const rnh = arg;
-	struct table_entry *ent;
-
-	ent = (struct table_entry *)
-	    rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh);
-	if (ent != NULL)
-		free(ent, M_IPFW_TBL);
+	delete_table_entry_rn((struct ipfw_table_head *)arg, rn->rn_key, rn->rn_mask);
 	return (0);
 }
 
 static int
 flush_table(struct ip_fw_chain *ch, uint16_t tbl)
 {
-	struct radix_node_head *rnh;
+	struct ipfw_table_head *th;
 
 	IPFW_WLOCK_ASSERT(ch);
 
 	if (tbl >= IPFW_TABLES_MAX)
 		return (EINVAL);
-	rnh = ch->tables[tbl];
-	KASSERT(rnh != NULL, ("NULL IPFW table"));
-	rnh->rnh_walktree(rnh, flush_table_entry, rnh);
+	th = &ch->tables[tbl];
+	KASSERT(th->ether_rnh != NULL, ("NULL IPFW table"));
+	th->ether_rnh->rnh_walktree(th->ether_rnh, flush_table_entry, th);
 	return (0);
 }
 
@@ -1908,7 +1996,12 @@ init_tables(struct ip_fw_chain *ch)
 	uint16_t j;
 
 	for (i = 0; i < IPFW_TABLES_MAX; i++) {
-		if (!rn_inithead((void **)&ch->tables[i], 32)) {
+		struct ipfw_table_head *th = &ch->tables[i];
+
+		if (!rn_inithead((void**)&(th->in_rnh), 
+		    __offsetof(struct table_entry_addr, in_addr) * 8) ||
+		    !rn_inithead((void**)&(th->ether_rnh),
+		    __offsetof(struct table_entry_addr, ether_addr) * 8)) {
 			for (j = 0; j < i; j++) {
 				(void) flush_table(ch, j);
 			}
@@ -1920,18 +2013,34 @@ init_tables(struct ip_fw_chain *ch)
 
 static int
 lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
-    uint32_t *val)
+    ipfw_ether_addr *ether_addr, uint32_t *val)
 {
-	struct radix_node_head *rnh;
-	struct table_entry *ent;
-	struct sockaddr_in sa;
+	struct ipfw_table_head *th;
+	struct table_entry_addr sa, mask;
+	struct table_entry *ent = NULL;
+	const int has_ether_addr = (ether_addr && (ether_addr->flags & IPFW_EA_CHECK));
+	const int has_in_addr = (addr != INADDR_ANY);
 
 	if (tbl >= IPFW_TABLES_MAX)
 		return (0);
-	rnh = ch->tables[tbl];
-	sa.sin_len = 8;
-	sa.sin_addr.s_addr = addr;
-	ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh));
+	th = &ch->tables[tbl];
+	init_table_entry_addr(&sa, &mask, addr, (addr == INADDR_ANY ? 0 : 32), ether_addr);
+	if (has_ether_addr) {
+		ent = RN_TO_ENT(th->ether_rnh->rnh_lookup(&sa, NULL, th->ether_rnh),
+		    ether_rn);
+		if (ent == NULL && has_in_addr) {
+			/* 
+			 * Try to lookup entry with any (zero) ether_addr. It's
+			 * handled this way not to deal with non-continuous
+			 * masks in radix trees.
+			 */
+			bzero(sa.ether_addr.octet, ETHER_ADDR_LEN);
+			ent = RN_TO_ENT(th->ether_rnh->rnh_lookup(&sa, NULL, th->ether_rnh),
+			    ether_rn);
+		}
+	} else if (has_in_addr) {
+		ent = RN_TO_ENT(th->in_rnh->rnh_lookup(&sa, NULL, th->in_rnh), in_rn);
+	}
 	if (ent != NULL) {
 		*val = ent->value;
 		return (1);
@@ -1951,20 +2060,20 @@ count_table_entry(struct radix_node *rn,
 static int
 count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt)
 {
-	struct radix_node_head *rnh;
+	struct ipfw_table_head *th;
 
 	if (tbl >= IPFW_TABLES_MAX)
 		return (EINVAL);
-	rnh = ch->tables[tbl];
+	th = &ch->tables[tbl];
 	*cnt = 0;
-	rnh->rnh_walktree(rnh, count_table_entry, cnt);
+	th->ether_rnh->rnh_walktree(th->ether_rnh, count_table_entry, cnt);
 	return (0);
 }
 
 static int
 dump_table_entry(struct radix_node *rn, void *arg)
 {
-	struct table_entry * const n = (struct table_entry *)rn;
+	struct table_entry * const n = RN_TO_ENT(rn, ether_rn);
 	ipfw_table * const tbl = arg;
 	ipfw_table_entry *ent;
 
@@ -1972,11 +2081,23 @@ dump_table_entry(struct radix_node *rn, 
 		return (1);
 	ent = &tbl->ent[tbl->cnt];
 	ent->tbl = tbl->tbl;
-	if (in_nullhost(n->mask.sin_addr))
+	if (n->mask.in_addr == INADDR_ANY)
 		ent->masklen = 0;
 	else
-		ent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr));
-	ent->addr = n->addr.sin_addr.s_addr;
+		ent->masklen = 33 - ffs(ntohl(n->mask.in_addr));
+	ent->addr = n->addr.in_addr;
+	memcpy(ent->ether_addr.octet, n->addr.ether_addr.octet, ETHER_ADDR_LEN);
+	ent->ether_addr.flags = 0;
+
+#define __ETHER_IS_ZERO(a) (((a)[0] | (a)[1] | (a)[2] | (a)[3] | (a)[4] | (a)[5]) == 0)
+	if (!__ETHER_IS_ZERO(n->mask.ether_addr.octet) &&
+	    !__ETHER_IS_ZERO(n->addr.ether_addr.octet)) {
+		ent->ether_addr.flags = IPFW_EA_CHECK;
+		/* Should be fixed after adding new flags */
+		if (n->mask.ether_addr.octet[0] == 0x01)
+			ent->ether_addr.flags |= IPFW_EA_MULTICAST;
+	}
+#undef __ETHER_IS_ZERO
 	ent->value = n->value;
 	tbl->cnt++;
 	return (0);
@@ -1985,13 +2106,13 @@ dump_table_entry(struct radix_node *rn, 
 static int
 dump_table(struct ip_fw_chain *ch, ipfw_table *tbl)
 {
-	struct radix_node_head *rnh;
+	struct ipfw_table_head *th;
 
 	if (tbl->tbl >= IPFW_TABLES_MAX)
 		return (EINVAL);
-	rnh = ch->tables[tbl->tbl];
+	th = &ch->tables[tbl->tbl];
 	tbl->cnt = 0;
-	rnh->rnh_walktree(rnh, dump_table_entry, tbl);
+	th->ether_rnh->rnh_walktree(th->ether_rnh, dump_table_entry, tbl);
 	return (0);
 }
 
@@ -2099,10 +2220,9 @@ check_uidgid(ipfw_insn_u32 *insn, int pr
  * Parameters:
  *
  *	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, or NULL for layer3 packet.
- *	args->L3offset	Number of bytes bypassed if we came from L2.
- *			e.g. often sizeof(eh)  ** NOTYET **
+ *		Starts with the IP header or with layer2 header if IP_FW_ARGS_LAYER2
+ *		is set in args->flags.
+ *	args->eh (in)	ethernet header if present, or NULL for layer3 packet.
  *	args->oif	Outgoing interface, or NULL if packet is incoming.
  *		The incoming interface is in the mbuf. (in)
  *	args->divert_rule (in/out)
@@ -2113,6 +2233,7 @@ check_uidgid(ipfw_insn_u32 *insn, int pr
  *	args->next_hop	Socket we are forwarding to (out).
  *	args->f_id	Addresses grabbed from the packet (out)
  * 	args->cookie	a cookie depending on rule action
+ * 	args->flags	Flags
  *
  * Return value:
  *
@@ -2139,10 +2260,8 @@ 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
+	 * args->eh	The ethernet 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
@@ -2150,12 +2269,10 @@ ipfw_chk(struct ip_fw_args *args)
 	 *	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).
 	 */
 	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
@@ -2251,6 +2368,9 @@ ipfw_chk(struct ip_fw_args *args)
 	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 */
@@ -2276,12 +2396,22 @@ do {									\
 	/*
 	 * if we have an ether header,
 	 */
-	if (args->eh)
+	if (args->eh != NULL) {
 		etype = ntohs(args->eh->ether_type);
+		memcpy(args->f_id.src_ether.octet, args->eh->ether_shost, 
+				ETHER_ADDR_LEN);
+		args->f_id.src_ether.flags = IPFW_EA_CHECK;
+		memcpy(args->f_id.dst_ether.octet, args->eh->ether_dhost, 
+				ETHER_ADDR_LEN);
+		args->f_id.dst_ether.flags = IPFW_EA_CHECK;
+	} else {
+		args->f_id.src_ether.flags = 0;
+		args->f_id.dst_ether.flags = 0;
+	}
 
 	/* 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;
@@ -2446,7 +2576,7 @@ do {									\
 		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;
@@ -2457,13 +2587,8 @@ do {									\
 		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) {
@@ -2494,6 +2619,13 @@ do {									\
 		ip = mtod(m, struct ip *);
 		args->f_id.src_ip = ntohl(src_ip.s_addr);
 		args->f_id.dst_ip = ntohl(dst_ip.s_addr);
+	} else if (pktlen >= ETHER_HDR_LEN && args->eh != NULL &&
+	    (args->flags & IP_FW_ARGS_LAYER2)) {
+		void *hdr;
+		switch (ntohs(args->eh->ether_type)) {
+		case ETHERTYPE_ARP:
+			PULLUP_TO(ETHER_HDR_LEN, hdr, struct arphdr);
+		}
 	}
 #undef PULLUP_TO
 	if (proto) { /* we may have port numbers, store them */
@@ -2529,7 +2661,7 @@ do {									\
 		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 */
@@ -2555,6 +2687,7 @@ do {									\
 	for (; f; f = f->next) {
 		ipfw_insn *cmd;
 		uint32_t tablearg = 0;
+		uint32_t stateopts = 0;
 		int l, cmdlen, skip_or; /* skip rest of OR block */
 
 again:
@@ -2600,11 +2733,6 @@ check_body:
 				match = 1;
 				break;
 
-			case O_FORWARD_MAC:
-				printf("ipfw: opcode %d unimplemented\n",
-				    cmd->opcode);
-				break;
-
 			case O_GID:
 			case O_UID:
 			case O_JAIL:
@@ -2641,23 +2769,20 @@ check_body:
 				    m->m_pkthdr.rcvif, (ipfw_insn_if *)cmd);
 				break;
 
-			case O_MACADDR2:
-				if (args->eh != NULL) {	/* have MAC header */
-					u_int32_t *want = (u_int32_t *)
-						((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;
-
-					match =
-					    ( want[0] == (hdr[0] & mask[0]) &&
-					      want[1] == (hdr[1] & mask[1]) &&
-					      want[2] == (hdr[2] & mask[2]) );
+			case O_ETHER_SRC:
+			case O_ETHER_DST:
+				if (args->eh != NULL) {	/* have ethernet header */
+					ipfw_ether_addr *want = 
+						&(((ipfw_insn_ether *)cmd)->ether);
+					ipfw_ether_addr *a = (cmd->opcode == O_ETHER_SRC ?
+						&args->f_id.src_ether :
+						&args->f_id.dst_ether);
+					match = ether_addr_allow(want, a);
 				}
 				break;
 
-			case O_MAC_TYPE:
-				if (args->eh != NULL) {
+			case O_ETHER_TYPE:
+				if (args->eh != NULL) {	/* have ethernet header */
 					u_int16_t *p =
 					    ((ipfw_insn_u16 *)cmd)->ports;
 					int i;
@@ -2678,7 +2803,7 @@ check_body:
 				break;
 
 			case O_LAYER2:
-				match = (args->eh != NULL);
+				match = ((args->flags & IP_FW_ARGS_LAYER2) != 0);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



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