svn commit: r198045 - in user/eri/pf45/head/sys: contrib/pf/net netinet netinet/ipfw

Ermal Luçi eri at FreeBSD.org
Tue Oct 13 19:49:33 UTC 2009


Author: eri
Date: Tue Oct 13 19:49:33 2009
New Revision: 198045
URL: http://svn.freebsd.org/changeset/base/198045

Log:
  Make pf(4) divert target usable with FreeBSD divert(4).
  The AF_INET only divert(4) limitation is inherited.
  
  The divert(4) for pf(4) implementation has been tested/used
  on pfSense.org since long time and is belived to be stable.
  
  As a side not the divert(4) module is not anymore dependent
  on ipfw(4).
  
  TODO: Update the man page of divert(4).

Modified:
  user/eri/pf45/head/sys/contrib/pf/net/pf.c
  user/eri/pf45/head/sys/netinet/ip_divert.c
  user/eri/pf45/head/sys/netinet/ip_divert.h
  user/eri/pf45/head/sys/netinet/ip_input.c
  user/eri/pf45/head/sys/netinet/ipfw/ip_fw_pfil.c

Modified: user/eri/pf45/head/sys/contrib/pf/net/pf.c
==============================================================================
--- user/eri/pf45/head/sys/contrib/pf/net/pf.c	Tue Oct 13 19:04:01 2009	(r198044)
+++ user/eri/pf45/head/sys/contrib/pf/net/pf.c	Tue Oct 13 19:49:33 2009	(r198045)
@@ -136,6 +136,9 @@ __FBSDID("$FreeBSD$");
 #include <netinet/udp_var.h>
 #include <netinet/icmp_var.h>
 #include <netinet/if_ether.h>
+#ifdef __FreeBSD__
+#include <netinet/ip_divert.h>
+#endif
 
 #ifndef __FreeBSD__
 #include <dev/rndvar.h>
@@ -371,6 +374,13 @@ struct pf_pool_limit pf_pool_limits[PF_L
 #endif
 
 #ifdef __FreeBSD__
+#define	PF_FREEBSD_DIVERT()							\
+	do {									\
+		r = (*state)->rule.ptr;						\
+		if (r->divert.port && !(pd->pf_mtag->flags & PF_TAG_DIVERTED))	\
+			return (PF_PASS);					\
+	} while (0)
+
 #define STATE_LOOKUP(i, k, d, s, m, pt)					\
         do {                                                            \
                 s = pf_find_state(i, k, d, m, pt);                      \
@@ -4342,6 +4352,9 @@ pf_test_state_tcp(struct pf_state **stat
 	int			 copyback = 0;
 	struct pf_state_peer	*src, *dst;
 	struct pf_state_key	*sk;
+#ifdef __FreeBSD__
+	struct pf_rule		*r;
+#endif
 
 	key.af = pd->af;
 	key.proto = IPPROTO_TCP;
@@ -4359,6 +4372,8 @@ pf_test_state_tcp(struct pf_state **stat
 
 #ifdef __FreeBSD__
 	STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag);
+
+	PF_FREEBSD_DIVERT();
 #else
 	STATE_LOOKUP(kif, &key, direction, *state, m);
 #endif
@@ -4371,6 +4386,19 @@ pf_test_state_tcp(struct pf_state **stat
 		dst = &(*state)->src;
 	}
 
+#ifdef __FreeBSD__
+	/*
+	 * The inital state is created when searching in the ruleset when
+	 * the packet reloops the state checking will drop it.
+	 * We take measure here for this special case.
+	 */
+	if ((th->th_flags & TH_SYN) && src->state == TCPS_SYN_SENT &&
+	    dst->state == TCPS_CLOSED) {
+		if (pd->pf_mtag->flags & PF_TAG_DIVERTED)
+			return (PF_PASS);
+	}
+#endif
+
 	sk = (*state)->key[pd->didx];
 
 	if ((*state)->src.state == PF_TCPS_PROXY_SRC) {
@@ -4536,6 +4564,9 @@ pf_test_state_udp(struct pf_state **stat
 	struct pf_state_peer	*src, *dst;
 	struct pf_state_key_cmp	 key;
 	struct udphdr		*uh = pd->hdr.udp;
+#ifdef __FreeBSD__
+	struct pf_rule		*r;
+#endif
 
 	key.af = pd->af;
 	key.proto = IPPROTO_UDP;
@@ -4553,6 +4584,8 @@ pf_test_state_udp(struct pf_state **stat
 
 #ifdef __FreeBSD__
         STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag);
+
+	PF_FREEBSD_DIVERT();
 #else
         STATE_LOOKUP(kif, &key, direction, *state, m);
 #endif
@@ -4609,6 +4642,7 @@ pf_test_state_icmp(struct pf_state **sta
 {
 	struct pf_addr  *saddr = pd->src, *daddr = pd->dst;
 #ifdef __FreeBSD__
+	struct pf_rule	*r;
 	u_int16_t	 icmpid = 0, *icmpsum;
 #else
 	u_int16_t	 icmpid, *icmpsum;
@@ -4666,6 +4700,8 @@ pf_test_state_icmp(struct pf_state **sta
 
 #ifdef __FreeBSD__
 		STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag);
+
+		PF_FREEBSD_DIVERT();
 #else
 		STATE_LOOKUP(kif, &key, direction, *state, m);
 #endif
@@ -4881,6 +4917,8 @@ pf_test_state_icmp(struct pf_state **sta
 
 #ifdef __FreeBSD__
 			STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag);
+
+			PF_FREEBSD_DIVERT();
 #else
 			STATE_LOOKUP(kif, &key, direction, *state, m);
 #endif
@@ -5031,6 +5069,8 @@ pf_test_state_icmp(struct pf_state **sta
 
 #ifdef __FreeBSD__
 			STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag);
+
+			PF_FREEBSD_DIVERT();
 #else
 			STATE_LOOKUP(kif, &key, direction, *state, m);
 #endif
@@ -5120,6 +5160,8 @@ pf_test_state_icmp(struct pf_state **sta
 
 #ifdef __FreeBSD__
 			STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag);
+
+			PF_FREEBSD_DIVERT();
 #else
 			STATE_LOOKUP(kif, &key, direction, *state, m);
 #endif
@@ -5183,6 +5225,8 @@ pf_test_state_icmp(struct pf_state **sta
 
 #ifdef __FreeBSD__
 			STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag);
+
+			PF_FREEBSD_DIVERT();
 #else
 			STATE_LOOKUP(kif, &key, direction, *state, m);
 #endif
@@ -5239,6 +5283,8 @@ pf_test_state_icmp(struct pf_state **sta
 
 #ifdef __FreeBSD__
 			STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag);
+
+			PF_FREEBSD_DIVERT();
 #else
 			STATE_LOOKUP(kif, &key, direction, *state, m);
 #endif
@@ -5308,6 +5354,9 @@ pf_test_state_other(struct pf_state **st
 {
 	struct pf_state_peer	*src, *dst;
 	struct pf_state_key_cmp	 key;
+#ifdef __FreeBSD__
+	struct pf_rule		*r;
+#endif
 
 	key.af = pd->af;
 	key.proto = pd->proto;
@@ -5323,6 +5372,8 @@ pf_test_state_other(struct pf_state **st
 
 #ifdef __FreeBSD__
         STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag);
+
+	PF_FREEBSD_DIVERT();
 #else
         STATE_LOOKUP(kif, &key, direction, *state, m);
 #endif
@@ -6351,6 +6402,7 @@ pf_test(int dir, struct ifnet *ifp, stru
 	struct mbuf		*m = *m0;
 #ifdef __FreeBSD__
 	struct ip		*h = NULL;
+	struct m_tag		*dvtag;
 #else
 	struct ip		*h;
 #endif
@@ -6431,7 +6483,14 @@ pf_test(int dir, struct ifnet *ifp, stru
 	if (m->m_pkthdr.pf.flags & PF_TAG_GENERATED)
 		return (PF_PASS);
 #endif
-
+	
+#ifdef __FreeBSD__
+	if (ip_divert_ptr != NULL &&
+	    ((dvtag = m_tag_find(m, PACKET_TAG_DIVERT, NULL)) != NULL)) {
+		pd.pf_mtag->flags |= PF_TAG_DIVERTED;
+		m_tag_delete(m, dvtag);
+	} else
+#endif
 	/* We do IP header normalization and packet reassembly here */
 	if (pf_normalize_ip(m0, dir, kif, &reason, &pd) != PF_PASS) {
 		action = PF_DROP;
@@ -6670,19 +6729,48 @@ done:
 		m->m_pkthdr.pf.flags |= PF_TAG_TRANSLATE_LOCALHOST;
 #endif
 
+#ifdef __FreeBSD__
+	if (action == PF_PASS && r->divert.port &&
+	    ip_divert_ptr != NULL && !(pd.pf_mtag->flags & PF_TAG_DIVERTED)) {
+                struct divert_tag *dt;
+
+                dvtag = m_tag_get(PACKET_TAG_DIVERT,
+                        sizeof(struct divert_tag), M_NOWAIT);
+                if (dvtag != NULL) {
+                        dt = (struct divert_tag *)(dvtag+1);
+                        dt->cookie = 0;
+                        dt->info = r->divert.port;
+                        m_tag_prepend(m, dvtag);
+
+			pd.pf_mtag->flags |= PF_TAG_DIVERTED;
+
+                        PF_UNLOCK();
+
+                        ip_divert_ptr(*m0,
+                            dir == PF_IN ? DIV_DIR_IN : DIV_DIR_OUT);
+
+                        *m0 = NULL;
+                        return (action);
+                } else {
+                        /* XXX: ipfw has the same behaviour! */
+			action = PF_DROP;
+			REASON_SET(&reason, PFRES_MEMORY);
+			log = 1;
+                        DPFPRINTF(PF_DEBUG_MISC,
+                                ("pf: failed to allocate divert tag\n"));
+		}
+        }
+#else
 	if (dir == PF_IN && action == PF_PASS && r->divert.port) {
 		struct pf_divert *divert;
 
 		if ((divert = pf_get_divert(m))) {
-#ifdef __FreeBSD__
-			pd.pf_mtag->flags |= PF_TAG_DIVERTED;
-#else
 			m->m_pkthdr.pf.flags |= PF_TAG_DIVERTED;
-#endif
 			divert->port = r->divert.port;
 			divert->addr.ipv4 = r->divert.addr.v4;
 		}
 	}
+#endif
 
 	if (log) {
 		struct pf_rule *lr;
@@ -7156,19 +7244,21 @@ done:
 		m->m_pkthdr.pf.flags |= PF_TAG_TRANSLATE_LOCALHOST;
 #endif
 
+#ifdef __FreeBSD__
+	/* XXX: Anybody working on it?! */
+	if (r->divert.port)
+		printf("pf: divert(9) is not supported for IPv6\n");
+#else
 	if (dir == PF_IN && action == PF_PASS && r->divert.port) {
 		struct pf_divert *divert;
 
 		if ((divert = pf_get_divert(m))) {
-#ifdef __FreeBSD__
-			pd.pf_mtag->flags |= PF_TAG_DIVERTED;
-#else
 			m->m_pkthdr.pf.flags |= PF_TAG_DIVERTED;
-#endif
 			divert->port = r->divert.port;
 			divert->addr.ipv6 = r->divert.addr.v6;
 		}
 	}
+#endif
 
 	if (log) {
 		struct pf_rule *lr;

Modified: user/eri/pf45/head/sys/netinet/ip_divert.c
==============================================================================
--- user/eri/pf45/head/sys/netinet/ip_divert.c	Tue Oct 13 19:04:01 2009	(r198044)
+++ user/eri/pf45/head/sys/netinet/ip_divert.c	Tue Oct 13 19:49:33 2009	(r198045)
@@ -800,5 +800,4 @@ static moduledata_t ipdivertmod = {
 };
 
 DECLARE_MODULE(ipdivert, ipdivertmod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
-MODULE_DEPEND(dummynet, ipfw, 2, 2, 2);
 MODULE_VERSION(ipdivert, 1);

Modified: user/eri/pf45/head/sys/netinet/ip_divert.h
==============================================================================
--- user/eri/pf45/head/sys/netinet/ip_divert.h	Tue Oct 13 19:04:01 2009	(r198044)
+++ user/eri/pf45/head/sys/netinet/ip_divert.h	Tue Oct 13 19:49:33 2009	(r198045)
@@ -50,6 +50,9 @@ struct divert_tag {
 	u_int16_t	cookie;		/* ipfw rule number */
 };
 
+#define DIV_DIR_IN	1
+#define DIV_DIR_OUT	0
+
 /*
  * Return the divert cookie associated with the mbuf; if any.
  */

Modified: user/eri/pf45/head/sys/netinet/ip_input.c
==============================================================================
--- user/eri/pf45/head/sys/netinet/ip_input.c	Tue Oct 13 19:04:01 2009	(r198044)
+++ user/eri/pf45/head/sys/netinet/ip_input.c	Tue Oct 13 19:49:33 2009	(r198045)
@@ -80,6 +80,7 @@ __FBSDID("$FreeBSD$");
 #ifdef IPSEC
 #include <netinet/ip_ipsec.h>
 #endif /* IPSEC */
+#include <netinet/ip_divert.h>
 
 #include <sys/socketvar.h>
 
@@ -183,6 +184,9 @@ extern	struct domain inetdomain;
 extern	struct protosw inetsw[];
 u_char	ip_protox[IPPROTO_MAX];
 
+/* Divert hooks. */
+ip_divert_packet_t *ip_divert_ptr = NULL;
+
 SYSCTL_VNET_STRUCT(_net_inet_ip, IPCTL_STATS, stats, CTLFLAG_RW,
     &VNET_NAME(ipstat), ipstat,
     "IP statistics (struct ipstat, netinet/ip_var.h)");

Modified: user/eri/pf45/head/sys/netinet/ipfw/ip_fw_pfil.c
==============================================================================
--- user/eri/pf45/head/sys/netinet/ipfw/ip_fw_pfil.c	Tue Oct 13 19:04:01 2009	(r198044)
+++ user/eri/pf45/head/sys/netinet/ipfw/ip_fw_pfil.c	Tue Oct 13 19:49:33 2009	(r198045)
@@ -74,16 +74,11 @@ VNET_DEFINE(int, fw6_enable) = 1;
 
 int ipfw_chg_hook(SYSCTL_HANDLER_ARGS);
 
-/* Divert hooks. */
-ip_divert_packet_t *ip_divert_ptr = NULL;
-
 /* ng_ipfw hooks. */
 ng_ipfw_input_t *ng_ipfw_input_p = NULL;
 
 /* Forward declarations. */
 static int	ipfw_divert(struct mbuf **, int, int);
-#define	DIV_DIR_IN	1
-#define	DIV_DIR_OUT	0
 
 int
 ipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,


More information about the svn-src-user mailing list