Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 15 Dec 2007 19:10:37 GMT
From:      Ermal Luçi <eri@FreeBSD.org>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   kern/118727: [PATCH] ng_pf module
Message-ID:  <200712151910.lBFJAbPB041893@www.freebsd.org>
Resent-Message-ID: <200712151920.lBFJK2H0066817@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         118727
>Category:       kern
>Synopsis:       [PATCH] ng_pf module
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Sat Dec 15 19:20:01 UTC 2007
>Closed-Date:
>Last-Modified:
>Originator:     Ermal Luçi
>Release:        freebsd-CURRENT
>Organization:
>Environment:
FreeBSD  8.0-CURRENT FreeBSD 8.0-CURRENT #1: Fri Oct 26 21:54:10 CEST 2007     root@:/usr/obj/usr/src/sys/ERI  amd64

>Description:
This is ng_pf node based on ng_ipfw code and idea.
It allows interaction of PF and netgraph.
Below are the node features and a dummy example of how to use it.
Patch is attached.

Link to mailing-lists post:
http://lists.freebsd.org/pipermail/freebsd-pf/2007-May/003382.html

Features,
1- By default it sends any packet that matches the rule to netgraph.
Syntax: pass in from any to any netgraph 41 #41 is the hook number(it
needs to be a number)

2- You can specify how many packets will be sent to netgraph. This is
implemented as a keep state option.
Syntax: pass in from any to any netgraph 41 keep
state(max-packets-to-netgraph 4)

3- You can specify flags when adding the tag to the node.
Syntax ngctl msg pf: addtag { tagname=\"TEST\" tag=60 flags=4 }


There are 4 flags for now:
    NG_PF_KILL_STATE (actually removes state from the state pool
directly from the node
   NG_PF_IGNORE_STATE (it schedules the state to be removed later but
behaves as the above and is really faster and safer)
   NG_PF_SKIP_FIREWALLS (skips firewalls; the way it is implemented
this really skips any firewall on freebsd at least ipfw and pf). This
is per tag setting
meaning you can specify which packet whith a specific tag should skip
reparssing the rules.
  NG_PF_DROP_PACKET (really drops packet; faster than telling a rule
on PF to drop it, i don't like it personally cause is kinda magic; but
it is there)


4- The node has these messages:
       NGM_PF_ADD_TAG         (needs tagname, tagid, flags)
       struct ng_pf_command {
            char            name[NG_PF_TAG_NAME_SIZE];
            u_int16_t       tag;
            int             flags;
       };

       NGM_PF_RMV_TAG,        (needs tagname)

       NGM_PF_GET_TAGS,       (no arguments)
#ifdef NG_PF_DEBUG
       NGM_PF_GET_STATS,       (number of packets in/out)
       NGM_PF_CLR_STATS,
       NGM_PF_GETCLR_STATS,
#endif
       NGM_PF_SET_TAGZONE,
       NGM_PF_GET_TAGZONE,
       NGM_PF_DEFAULT_TAGZONE  (for help)


5- You can send back and forth a packet(reparse ruleset multiple
times) by resending a packet that has already passed once to netgraph
by a matching rule with a different hook number. I.e.:
...
....
pass in on $int from any to any netgraph 41
pass in on $int tagged ONCE_TO_NETGRAPH netgraph 42
pass in on $int tagged TWICE_TO_NETGRAPH netgraph 43
...
..

For an example how to find DC++ packets with ng_bpf tag these packets
with ng_tag is available at ng_tag manual page. After that just
connect a hook to ng_pf and you're done. Surely even the rule that
sends the packet to the correct queue on PF side.

For more discussion on ng_bpf and packet matching for P2P packets follow,
http://lists.freebsd.org/pipermail/freebsd-current/2006-June/063863.html.

Sample configuration of the node.

1 - kldload ng_pf (after compiling).
2 - create a sample pf.conf file like the following:

pass out quick on $INT all tagged TRY keep state
pass out quick  on $INT proto tcp from any to any port 80 netgraph 41

# or even this. It does the same thing.
pass out on $INT proto tcp from any to any port 80 netgraph 41
pass out on $INT all tagged TRY keep state

#The tag TRY is added to ng_pf list of tags to translate and the tag
is added to the #packet with ng_tag.

3 - configure netgraph part of things. I  used the following commands
on my tests:

# You understand the first 2 commands :).
pfctl -e
pfctl -F all -f /etc/pf.test

# Here we configure a tag to be translated on ng_pf node. The node translates
# tagname=TRY as known by PF.
ngctl msg pf: addtag { tagname=\"TRY\" tag=52 flags=0 }

# Create a hook with a ng_tag node.
ngctl mkpeer pf: tag 41 th1

# Give a name to the hook for simplicity.
ngctl name pf:41 match

# Configure ng_tag node.

# We tell ng_tag to not touch the packets incoming/entering on hook = "th1"
ngctl msg match: sethookin { thisHook=\"th1\" ifNotMatch=\"th1\" }

# ng_tag will tag packets leaving hook="th1" with tagname=TRY.
ngctl msg match: sethookout { \
       thisHook=\"th1\" \  #hookname
       tag_id=24            \  # PACKET_TAG_PF_TAG.(1)
       tag_len=4            \  # usually 4 bytes since we only pass a
number/tag_id.
       tag_data=[ 52 ] }   # the tag we want to apply packets on this hook.
{ (1) PACKET_TAG_PF_TAG = 24 is taken from sys/mbuf.h }

After this if you try to connect to port 80 of any webserver if you
check the PF
statistics on rule matches with:
pfctl -s rules -v
you'll see that packets have gone through 'match by tag' rule after
passing through netgraph.


>How-To-Repeat:

>Fix:


Patch attached with submission follows:

diff -uNr ng_pf_orig/src/sys/contrib/pf/net/pf.c ng_pf/src/sys/contrib/pf/net/pf.c
--- ng_pf_orig/src/sys/contrib/pf/net/pf.c	2007-11-21 11:12:52.000000000 +0100
+++ ng_pf/src/sys/contrib/pf/net/pf.c	2007-12-15 18:48:47.000000000 +0100
@@ -148,6 +148,9 @@
 
 extern int ip_optcopy(struct ip *, struct ip *);
 extern int debug_pfugidhack;
+
+#include <netgraph/ng_pf.h>
+ng_pf_input_t *ng_pf_input_p = NULL;
 #endif
 
 #define DPFPRINTF(n, x)	if (pf_status.debug >= (n)) printf x
@@ -6726,6 +6729,7 @@
 	int			 off, dirndx, pqid = 0;
 
 #ifdef __FreeBSD__
+	struct ng_pf_tag *ng;
 	PF_LOCK();
 #endif
 	if (!pf_status.running)
@@ -7091,6 +7095,27 @@
 		pf_route(m0, r, dir, ifp, s, &pd);
 
 #ifdef __FreeBSD__
+      if (r->netgraph && *m0 != NULL && NG_PF_LOADED) {
+              if (r->rule_flag & PFRULE_RULESRCTRACK &&
+                      s->packets[dir == PF_OUT] > r->max_packets_to_netgraph) {
+                      PF_UNLOCK();
+                      return (action);
+              }
+
+              ng = (struct ng_pf_tag *)m_tag_locate(*m0,
+                      NGM_PF_COOKIE, 0, NULL);
+              if (ng != NULL) {
+                       if (r->netgraph != ng->hooknum) {
+                              m_tag_delete(*m0, (struct m_tag *)ng);
+                              goto sendit;
+                       }
+              } else {
+sendit:
+                      PF_UNLOCK();
+                      return (ng_pf_input_p(m0, dir, r->netgraph, &s));
+              }
+      }
+                                                                 
 	PF_UNLOCK();
 #endif
 
@@ -7119,6 +7144,7 @@
 	int			 off, terminal = 0, dirndx, rh_cnt = 0;
 
 #ifdef __FreeBSD__
+	struct ng_pf_tag *ng;
 	PF_LOCK();
 #endif
 
@@ -7537,7 +7563,7 @@
 			    tr->dst.neg);
 	}
 
-
+	
 	if (action == PF_SYNPROXY_DROP) {
 		m_freem(*m0);
 		*m0 = NULL;
@@ -7547,8 +7573,31 @@
 		pf_route6(m0, r, dir, ifp, s, &pd);
 
 #ifdef __FreeBSD__
+	/* XXX: Is this true for IPv6?! */
+	if (r->netgraph && *m0 != NULL && NG_PF_LOADED) {
+		if (r->rule_flag & PFRULE_RULESRCTRACK &&
+                         s->packets[dir == PF_OUT] > r->max_packets_to_netgraph) {
+                        PF_UNLOCK();
+                        return (action);
+                }
+				
+		ng = (struct ng_pf_tag *)m_tag_locate(*m0, 
+			NGM_PF_COOKIE, 0, NULL);
+		if (ng != NULL) {
+		        if (r->netgraph != ng->hooknum) {
+			         m_tag_delete(*m0, (struct m_tag *)ng);
+			         goto sendit;
+			}
+                } else {
+sendit:										 
+			PF_UNLOCK();
+        	       	return (ng_pf_input_p(m0, dir, r->netgraph, &s));
+		}
+	}
+
 	PF_UNLOCK();
 #endif
+
 	return (action);
 }
 #endif /* INET6 */
diff -uNr ng_pf_orig/src/sys/contrib/pf/net/pfvar.h ng_pf/src/sys/contrib/pf/net/pfvar.h
--- ng_pf_orig/src/sys/contrib/pf/net/pfvar.h	2007-07-03 14:58:33.000000000 +0200
+++ ng_pf/src/sys/contrib/pf/net/pfvar.h	2007-12-15 18:50:19.000000000 +0100
@@ -624,7 +624,7 @@
 	u_int32_t		 src_nodes;
 	u_int32_t		 max_src_nodes;
 	u_int32_t		 max_src_states;
-	u_int32_t		 spare1;		/* netgraph */
+	u_int32_t                max_packets_to_netgraph; /* netgraph */
 	u_int32_t		 max_src_conn;
 	struct {
 		u_int32_t		limit;
@@ -643,7 +643,7 @@
 	u_int16_t		 max_mss;
 	u_int16_t		 tag;
 	u_int16_t		 match_tag;
-	u_int16_t		 spare2;		/* netgraph */
+	u_int16_t		 netgraph;		/* netgraph */
 
 	struct pf_rule_uid	 uid;
 	struct pf_rule_gid	 gid;
diff -uNr ng_pf_orig/src/sys/modules/netgraph/Makefile ng_pf/src/sys/modules/netgraph/Makefile
--- ng_pf_orig/src/sys/modules/netgraph/Makefile	2007-05-15 18:24:49.000000000 +0200
+++ ng_pf/src/sys/modules/netgraph/Makefile	2007-12-15 18:39:10.000000000 +0100
@@ -34,6 +34,7 @@
 	netflow \
 	netgraph \
 	one2many \
+	pf \
 	ppp \
 	pppoe \
 	pptpgre \
diff -uNr ng_pf_orig/src/sys/modules/netgraph/pf/Makefile ng_pf/src/sys/modules/netgraph/pf/Makefile
--- ng_pf_orig/src/sys/modules/netgraph/pf/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ ng_pf/src/sys/modules/netgraph/pf/Makefile	2007-12-15 18:39:10.000000000 +0100
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+KMOD=	ng_pf
+SRCS= 	ng_pf.c
+
+.include <bsd.kmod.mk>
diff -uNr ng_pf_orig/src/sys/netgraph/ng_pf.c ng_pf/src/sys/netgraph/ng_pf.c
--- ng_pf_orig/src/sys/netgraph/ng_pf.c	1970-01-01 01:00:00.000000000 +0100
+++ ng_pf/src/sys/netgraph/ng_pf.c	2007-12-15 19:08:37.000000000 +0100
@@ -0,0 +1,821 @@
+/*
+ * Copyright 2007, Ermal Luçi <ermal.luci@gmail.com>
+ * All rights reserved.
+ *
+ * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* 
+ * author: Ermal Luçi 
+ */ 
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/ctype.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+
+/* XXX: check why does not work with <net/pfvar.h> */
+#include <contrib/pf/net/pfvar.h> 
+#include <contrib/pf/net/pf_mtag.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph/ng_parse.h>
+#include <netgraph/ng_pf.h>
+#include <netgraph/netgraph.h>
+
+
+#ifdef NG_SEPARATE_MALLOC
+MALLOC_DEFINE(M_NETGRAPH_PF, "netgraph_pf", "netgraph pf node ");
+#else
+#define M_NETGRAPH_PF M_NETGRAPH
+#endif
+
+static int		ng_pf_mod_event(module_t mod, int event, void *data);
+static ng_constructor_t	ng_pf_constructor;
+static ng_shutdown_t	ng_pf_shutdown;
+static ng_newhook_t	ng_pf_newhook;
+static ng_connect_t	ng_pf_connect;
+static ng_findhook_t	ng_pf_findhook;
+static ng_rcvdata_t	ng_pf_rcvdata;
+static ng_disconnect_t	ng_pf_disconnect;
+static ng_rcvmsg_t	ng_pf_rcvmsg;
+
+static hook_p		ng_pf_findhook1(node_p, u_int16_t );
+static int		ng_pf_input(struct mbuf **, int, u_int16_t,
+				struct pf_state **);
+
+void 			ng_pf_translate_tag(struct mbuf **, int, int *);
+
+/* We have only one node */
+static node_p	fw_node;
+/*
+ * Private node data. Curently number of tags just for speeding
+ * things up.
+*/
+typedef struct {
+	u_int32_t	ntags;
+} *priv_p;
+
+/* XXX: maybe make these sysctl(9) aware! */
+static u_int32_t	tagzone = NGM_PF_COOKIE;
+
+static const struct ng_parse_struct_field ng_pf_command_field[] = 
+	NG_PF_COMMAND;
+static const struct ng_parse_type ng_pf_command_type = {
+          &ng_parse_struct_type,             
+          &ng_pf_command_field              
+};
+
+static int
+ng_pf_getTableLength(const struct ng_parse_type *type,
+		const u_char *start, const u_char *buf) {
+	const struct ng_pf_get_tags *const table = 
+		(const struct ng_pf_get_tags *)(buf - sizeof(u_int32_t));
+
+	return (table->n);
+}
+
+static const struct ng_parse_array_info ng_pf_struct_array_info = {
+	&ng_pf_command_type,
+	ng_pf_getTableLength
+};
+static const struct ng_parse_type ng_pf_struct_array_type = {
+	&ng_parse_array_type,
+	&ng_pf_struct_array_info
+};
+	
+static const struct ng_parse_struct_field ng_pf_get_tags_fields[] = 
+	NG_PF_GET_TAGS;
+static const struct ng_parse_type ng_pf_get_tags_type = {
+	&ng_parse_struct_type,
+	&ng_pf_get_tags_fields
+};
+	
+static const struct ng_parse_struct_field ng_pf_stats_fields[] =
+	NG_PF_GET_STATS;
+static const struct ng_parse_type ng_pf_stats_type = {
+	&ng_parse_struct_type,
+	&ng_pf_stats_fields
+};
+
+/* List of commands and how to convert arguments to/from ASCII */
+static const struct ng_cmdlist ng_pf_cmdlist[] = {
+        {
+          NGM_PF_COOKIE,
+          NGM_PF_ADD_TAG,
+          "addtag",
+          &ng_pf_command_type,
+	  NULL
+        },
+	{
+	  NGM_PF_COOKIE,
+	  NGM_PF_CHG_TAG_FLAGS,
+	  "chgtagflag",
+	  &ng_parse_uint32_type,
+	  NULL
+	},
+        {
+          NGM_PF_COOKIE,
+          NGM_PF_RMV_TAG,
+          "rmvtag",
+          &ng_pf_command_type,
+          NULL
+        },
+	{
+	  NGM_PF_COOKIE,
+	  NGM_PF_GET_TAGS,
+	  "gettags",
+	  NULL,
+	  &ng_pf_get_tags_type
+	},
+	{
+	  NGM_PF_COOKIE,
+	  NGM_PF_GET_STATS,
+	  "getstats",
+	  &ng_parse_hookbuf_type,
+	  &ng_pf_stats_type
+	},
+	{
+	  NGM_PF_COOKIE,
+	  NGM_PF_CLR_STATS,
+	  "clrstats",
+	  &ng_parse_hookbuf_type,
+	  NULL 
+	},
+        {
+	  NGM_PF_COOKIE,
+	  NGM_PF_GETCLR_STATS,
+	  "getclrstats",
+	  &ng_parse_hookbuf_type,
+	  &ng_pf_stats_type
+	},
+	{
+	  NGM_PF_COOKIE,
+	  NGM_PF_SET_TAGZONE,
+	  "settagzone",
+	  &ng_parse_uint32_type,
+	  NULL 
+	},
+	{
+	  NGM_PF_COOKIE,
+	  NGM_PF_GET_TAGZONE,
+	  "gettagzone",
+	  NULL,
+	  &ng_parse_uint32_type
+	},
+	{
+	  NGM_PF_COOKIE,
+	  NGM_PF_DEFAULT_TAGZONE,
+	  "defaulttagzone",
+	  NULL,
+	  NULL 
+	},
+     	{ 0 }
+};
+
+/* Netgraph node type descriptor */
+static struct ng_type ng_pf_typestruct = {
+	.version =	NG_ABI_VERSION,
+	.name =		NG_PF_NODE_TYPE,
+	.mod_event =	ng_pf_mod_event,
+	.constructor =	ng_pf_constructor,
+	.rcvmsg =	ng_pf_rcvmsg,
+	.shutdown =	ng_pf_shutdown,
+	.newhook =	ng_pf_newhook,
+	.connect =	ng_pf_connect,
+	.findhook =	ng_pf_findhook,
+	.rcvdata =	ng_pf_rcvdata,
+	.disconnect =	ng_pf_disconnect,
+	.cmdlist =	ng_pf_cmdlist,	
+};
+NETGRAPH_INIT(pf, &ng_pf_typestruct);
+MODULE_DEPEND(ng_pf, pf, PF_MODVER, PF_MODVER
+		, PF_MODVER); 
+
+/* Information we store for each hook */
+struct ng_pf_hook_priv {
+        hook_p			hook;
+	u_int16_t		hooknum;
+	struct ng_pf_stats	stats; 
+};
+typedef struct ng_pf_hook_priv *hpriv_p;
+
+
+static int
+ng_pf_mod_event(module_t mod, int event, void *data)
+{
+	priv_p priv;
+	int error = 0;
+	switch (event) {
+	case MOD_LOAD:
+
+		if (ng_pf_input_p != NULL) {
+			error = EEXIST;
+			break;
+		}
+
+		/* Setup node without any private data */
+		if ((error = ng_make_node_common(&ng_pf_typestruct, &fw_node))
+		    != 0) {
+			log(LOG_ERR, "%s: can't create ng_pf node", __func__);
+                	break;
+		}
+
+		/* Try to name node */
+		if (ng_name_node(fw_node, "pf") != 0)
+			log(LOG_WARNING, "%s: failed to name node \"pf\"",
+			    __func__);
+
+		/* Allocate memory for this hook's private data */
+		MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, 
+			M_NOWAIT | M_ZERO);
+		if (priv== NULL)
+			return (ENOMEM);
+
+		NG_NODE_SET_PRIVATE(fw_node, priv);
+
+		/* Register hook */
+		ng_pf_input_p = ng_pf_input;
+		TAILQ_INIT(&ng_pf_tags);
+		break;
+
+	case MOD_UNLOAD:
+		 /*
+		  * This won't happen if a node exists.
+		  * ng_pf_input_p is already cleared.
+		  */
+		break;
+
+	default:
+		error = EOPNOTSUPP;
+		break;
+	}
+
+	return (error);
+}
+
+static int
+ng_pf_constructor(node_p node)
+{
+	return (EINVAL);	/* Only one node */
+}
+
+static int
+ng_pf_newhook(node_p node, hook_p hook, const char *name)
+{
+	hpriv_p	hpriv;
+	u_int16_t hooknum;
+	const char *cp;
+	char *endptr;
+
+	/* Protect from leading zero */
+	if (name[0] == '0' && name[1] != '\0')
+		return (EINVAL);
+
+	/* Check that name contains only digits */
+	for (cp = name; *cp != '\0'; cp++)
+		if (!isdigit(*cp))
+			return (EINVAL);
+
+	/* Convert it to integer */
+	hooknum = (u_int16_t)strtol(name, &endptr, 10);
+	if (*endptr != '\0')
+		return (EINVAL);
+
+	/* Allocate memory for this hook's private data */
+	MALLOC(hpriv, hpriv_p, sizeof(*hpriv), M_NETGRAPH, M_NOWAIT | M_ZERO);
+	if (hpriv== NULL)
+		return (ENOMEM);
+
+	hpriv->hook = hook;
+	hpriv->hooknum = hooknum;
+
+	NG_HOOK_SET_PRIVATE(hook, hpriv);
+
+	return(0);
+}
+
+/*
+ * Set hooks into queueing mode, to avoid recursion between
+ * netgraph layer and ip_{input,output}.
+ */
+static int
+ng_pf_connect(hook_p hook)
+{
+	NG_HOOK_FORCE_QUEUE(hook);
+	return (0);
+}
+
+
+/*
+ * Receive a control message.
+ */
+static int
+ng_pf_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+	const priv_p priv = NG_NODE_PRIVATE(node);
+	struct ng_mesg *msg;
+	struct ng_mesg *resp = NULL; 
+	struct ng_pf_get_tags *tags = NULL;	
+	struct ng_pf_command *comm = NULL;
+	struct ng_pf_tagnames *tag = NULL;
+	int error = 0;
+
+	NGI_GET_MSG(item, msg);
+
+	switch (msg->header.typecookie) {			
+	case NGM_PF_COOKIE:
+                switch (msg->header.cmd) {
+                case NGM_PF_ADD_TAG:
+                    {
+			if (msg->header.arglen < sizeof(*comm)) {
+                                error = EINVAL;
+				break;
+			}
+                        comm = (struct ng_pf_command *)msg->data;
+			
+#ifdef NG_PF_DEBUG 
+			printf("%s: message recieved tag=%d tagname=%s\n",
+				__func__, comm->tag, comm->name);
+#endif
+			
+			/* this is adappted from pf_ioctl.c :) */
+        		TAILQ_FOREACH(tag, &ng_pf_tags, entries)
+                	if (strcmp(comm->name, tag->tagname) == 0)  {
+				error = EINVAL; /* find smth better */
+				goto done;
+			}
+                	
+        		/* allocate and fill new struct ng_pf_tagname */
+        		tag = (struct ng_pf_tagnames *)
+				malloc(sizeof(struct ng_pf_tagnames),
+            			M_NETGRAPH, M_NOWAIT);
+        		if (tag == NULL) { 
+				error = EINVAL; /* find smth better */
+				goto done;
+			}	
+
+        		bzero(tag, sizeof(struct ng_pf_tagnames));
+        		strlcpy(tag->tagname, comm->name, sizeof(tag->tagname));
+        		tag->tag = comm->tag;
+			tag->flags = comm->flags;	
+                	TAILQ_INSERT_TAIL(&ng_pf_tags, tag, entries);
+			
+			/* Increment number of tags node knows about */
+			priv->ntags++;
+			
+			break;
+		}
+		case NGM_PF_RMV_TAG:
+			{
+               		        if (msg->header.arglen < sizeof(*comm)) {
+					error = EINVAL;
+					break;
+				}
+       		                comm = (struct ng_pf_command *)msg->data;
+
+				TAILQ_FOREACH(tag, &ng_pf_tags, entries) {
+					if (!strcmp(comm->name, tag->tagname)
+						&& comm->tag == tag->tag) {
+						TAILQ_REMOVE(&ng_pf_tags, 
+							tag, entries);
+						FREE(tag, M_NETGRAPH);
+						priv->ntags--;
+						break;
+					}
+				}
+
+				break;	
+			}
+		case NGM_PF_CHG_TAG_FLAGS:
+			{
+				/* XXX: TODO Implement this?! */
+				break;
+			}
+		case NGM_PF_GET_TAGS:
+			{
+				NG_MKRESPONSE(resp, msg, sizeof(*tags) +
+					priv->ntags * 
+					sizeof(*tags->tags),
+					M_NOWAIT);
+				if (resp == NULL) {
+					error = ENOMEM;
+					break;
+				}
+				tags = (struct ng_pf_get_tags *)resp->data;
+				tags->n = priv->ntags;
+				comm = &tags->tags[0];
+				TAILQ_FOREACH(tag, &ng_pf_tags, entries) {
+					comm->tag = tag->tag;
+					comm->flags = tag->flags;
+					strncpy(comm->name, tag->tagname,
+						NG_PF_TAG_NAME_SIZE);
+#ifdef NG_PF_DEBUG
+					printf("%s: comm->tag=%d "
+						"comm->name=%s"
+						"comm->flags=%x\n"
+						"tag->tag=%d "
+						"tag->tagname=%s" 
+						"tag->flags=%x\n", __func__,
+						comm->tag, comm->name,
+					       	comm->flags, tag->tag, 
+						tag->tagname, tag->flags);
+#endif
+					comm++;
+				}
+				break;
+			}
+		case NGM_PF_GET_STATS:
+		case NGM_PF_CLR_STATS:
+		case NGM_PF_GETCLR_STATS:
+			{
+				struct ng_pf_stats *stats;
+				hook_p hook;
+
+				/* Sanity check */
+				if (msg->header.arglen == 0) {
+					error = EINVAL;
+					goto done;
+				}	
+				msg->data[msg->header.arglen - 1] = '\0';
+
+				/* Find hook */
+				if ( (hook = ng_pf_findhook(node, msg->data)) 
+						== NULL) {
+						error = EINVAL;
+						goto done;
+				}
+				stats = &((hpriv_p)
+					NG_HOOK_PRIVATE(hook))->stats;
+
+				if (msg->header.cmd != NGM_PF_CLR_STATS) {
+					NG_MKRESPONSE(resp, msg, 
+						sizeof(*stats), M_WAITOK);
+					bcopy(stats, resp->data, 
+							sizeof(*stats));
+				}
+
+				if (msg->header.cmd != NGM_PF_GET_STATS) {
+					bzero(stats, sizeof(*stats));
+				}
+				
+				break;
+			}
+		case NGM_PF_SET_TAGZONE:
+			{
+				/* Sanity check */
+				if (msg->header.arglen < sizeof(u_int32_t)) {
+					error = EINVAL;
+					goto done;
+				}
+				tagzone = (u_int32_t)*msg->data;
+				
+				break;	
+			}
+		case NGM_PF_GET_TAGZONE:
+			{
+				NG_MKRESPONSE(resp, msg, sizeof(u_int32_t),
+					M_WAITOK);
+				bcopy(&tagzone, resp->data, 
+					sizeof(u_int32_t));			
+
+				break;
+			}
+		case NGM_PF_DEFAULT_TAGZONE:
+			{
+				tagzone = NGM_PF_COOKIE;
+
+				break;
+			}
+		default:
+			error = EINVAL;
+			break;
+		}
+		break;
+	default:
+		error = EINVAL;
+		break;
+	}
+	
+        NG_RESPOND_MSG(error, node, item, resp); 
+done:
+        NG_FREE_MSG(msg);
+        return (error);
+}
+
+
+
+/* Look up hook by name */
+hook_p
+ng_pf_findhook(node_p node, const char *name)
+{
+	u_int16_t n;	/* numeric representation of hook */
+	char *endptr;
+
+	n = (u_int16_t)strtol(name, &endptr, 10);
+	if (*endptr != '\0')
+		return NULL;
+	return ng_pf_findhook1(node, n);
+}
+
+/* Look up hook by rule number */
+static hook_p
+ng_pf_findhook1(node_p node, u_int16_t hooknum)
+{
+	hook_p	hook;
+	hpriv_p	hpriv;
+
+	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
+		hpriv = NG_HOOK_PRIVATE(hook);
+		if (NG_HOOK_IS_VALID(hook) && (hpriv->hooknum == hooknum))
+                        return (hook);
+	}
+
+	return (NULL);
+}
+
+
+static int
+ng_pf_rcvdata(hook_p hook, item_p item)
+{
+	struct ng_pf_tag	*ngit;
+	struct mbuf 		*m;
+	struct ip		*ip;
+	int			flags = 0;
+	
+	NGI_GET_M(item, m);
+	NG_FREE_ITEM(item);
+
+	if ((ngit = (struct ng_pf_tag *)m_tag_locate(m, NGM_PF_COOKIE, 0,
+	    NULL)) == NULL) {
+		NG_FREE_M(m);
+		return (EINVAL);	/* XXX: find smth better */
+	};
+
+	/* translation to netgraph aware tag */
+	ng_pf_translate_tag(&m, PF_OUT, &flags);
+
+	if (flags & NG_PF_SKIP_FIREWALLS)
+		m->m_flags |= M_SKIP_FIREWALL;
+	
+	/* XXX: redundant? */
+	if (ngit->s != NULL) {
+		/* 
+		 * It speeds dropping the packet but is
+		 * somewhat magic and i don't like it 
+		 * at all, but anybody who likes can use
+		 * this behaviour!
+		 * 
+		 * really drop packet 
+		 */
+		if (flags & NG_PF_DROP_PACKET) {
+		       NG_FREE_M(m);
+		       ngit->s->timeout = PFTM_PURGE;
+		       return (0);
+		}
+			
+		/* instantly kill/remove state */
+		if (flags & NG_PF_KILL_STATE) 
+			pf_purge_expired_state(ngit->s);
+	
+		/* expire state */
+		if (flags & NG_PF_IGNORE_STATE)	
+			ngit->s->timeout = PFTM_PURGE; 
+	}
+			
+	
+	((hpriv_p)
+	NG_HOOK_PRIVATE(hook))->stats.packets_out++;
+
+	switch (ngit->direction) {
+	case PF_OUT:
+	    {
+		if (m->m_len < sizeof(struct ip) &&
+	    		(m = m_pullup(m, sizeof(struct ip))) == NULL)
+			return (PF_DROP);
+
+		ip = mtod(m, struct ip *);
+		ip->ip_len = ntohs(ip->ip_len);
+		ip->ip_off = ntohs(ip->ip_off);    
+
+		return ip_output(m, NULL, NULL, IP_FORWARDING, 
+					NULL, NULL);
+	    }
+	case PF_IN:
+	    {
+		ip_input(m);
+		return (0);
+	    }
+	default:
+		panic("ng_pf_rcvdata: bad dir %u", ngit->direction);
+	}	
+
+	/* not reached */
+	return (0);
+}
+
+static int
+ng_pf_input(struct mbuf **m0, int dir, u_int16_t hooknum, 
+		struct pf_state **state)
+{
+	struct mbuf *m;
+	struct ng_pf_tag *ngit;
+	/*struct ip *ip;*/
+	struct pf_state *s = *state;
+	hook_p	hook;
+	int error = PF_PASS;
+
+	/*
+	 * Node must be loaded and corresponding hook must be present.
+	 */
+	if (fw_node == NULL || 
+	   (hook = ng_pf_findhook1(fw_node, hooknum)) == NULL) {
+		m_freem(*m0);
+		return (PF_DROP); /* no hook associated with this rule */
+	}
+	
+
+	/* translation to netgraph aware tag */	
+	ng_pf_translate_tag(m0, PF_IN, NULL);
+
+	m = *m0;
+	*m0 = NULL;	/* it belongs now to netgraph */
+
+
+	if ((ngit = (struct ng_pf_tag *)m_tag_alloc(NGM_PF_COOKIE,
+	    0, TAGSIZ, M_NOWAIT|M_ZERO)) == NULL) {
+		m_freem(m);
+		return (PF_DROP);
+	}
+
+	((hpriv_p)
+	 NG_HOOK_PRIVATE(hook))->stats.packets_in++;
+
+	ngit->direction = dir;
+	ngit->hooknum = hooknum;
+	ngit->s = s;
+	m_tag_prepend(m, &ngit->mt);
+
+	NG_SEND_DATA_ONLY(error, hook, m);
+
+	if (error != 0 && error != PF_PASS) 
+		return(PF_DROP);
+
+	return (error);
+}
+
+static int
+ng_pf_shutdown(node_p node)
+{
+
+	/*
+	 * After our single node has been removed,
+	 * the only thing that can be done is
+	 * 'kldunload ng_pf.ko'
+	 */
+	struct ng_pf_tagnames *n1, *n2;
+	n1 = TAILQ_FIRST(&ng_pf_tags);
+        while (n1 != NULL) {
+             n2 = TAILQ_NEXT(n1, entries);
+             FREE(n1, M_NETGRAPH);
+             n1 = n2;
+        }
+
+	ng_pf_input_p = NULL;
+	NG_NODE_UNREF(node);
+	return (0);
+}
+
+static int
+ng_pf_disconnect(hook_p hook)
+{
+	const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
+
+	NG_HOOK_SET_PRIVATE(hook, NULL);
+	FREE(hpriv, M_NETGRAPH);
+
+	return (0);
+}
+
+/* This is the workhorse in this node */
+void
+ng_pf_translate_tag(struct mbuf **m, int direction, int *flags) {
+	char tagname[PF_TAG_NAME_SIZE];
+	u_int16_t tagid;
+	struct ng_pf_tagnames *tagn;
+	struct mbuf *mtag = *m;
+	struct pf_mtag *tag = NULL;
+	struct m_tag *r_tag = NULL;
+
+#ifdef NG_PF_DEBUG
+	printf("%s: Packet tags && tag_cookies\n", __func__);
+	for (r_tag = m_tag_first(mtag); r_tag != NULL;
+		r_tag = m_tag_next(mtag, r_tag))
+		if (r_tag == NULL) {
+			printf("%s: r_tag == NULL grrrr!!!!!\n", __func__);
+			continue;
+		}
+		else
+			printf("\ttag_id=%d tag_cookie=%d\n",
+				r_tag->m_tag_id, r_tag->m_tag_cookie);
+
+	/* Just to be safe. */
+	mtag = *m;
+    	r_tag = NULL;
+#endif
+	r_tag = m_tag_locate(mtag, tagzone, PACKET_TAG_PF_TAG, NULL);
+	/* 
+         * Basically the loop is not needed since PF uses only the first
+	 * tag it finds, but we can use it later to do some trick maybe!
+	 */
+	while (r_tag != NULL) { 
+		tag = (struct pf_mtag *)(r_tag+1);	
+		/* Can this happen?! */
+		if (tag == NULL)
+			continue;
+		if (direction == PF_IN) {
+			pf_tag2tagname(tag->tag, tagname);
+			TAILQ_FOREACH(tagn, &ng_pf_tags, entries)
+				if (strcmp(tagname, tagn->tagname) == 0) {
+					tag->tag = tagn->tag;
+					r_tag->m_tag_cookie = tagzone;
+       			                break; 
+       		       		  }
+#ifdef NG_PF_DEBUG
+			printf("%s: Translating PF to netgraph\n "
+				"\ttagname=%s tag_id=%d ng_tag_id=%d\n",
+				__func__, tagn->tagname, tagn->tag, tag->tag);
+#endif
+		} 
+		else {
+		
+          	        TAILQ_FOREACH(tagn, &ng_pf_tags, entries)
+                        if (tag->tag == tagn->tag) { 
+                                break;
+                          }
+			
+			if (tagn != NULL) {
+			/* XXX: this might allocate a tag on PF tags
+			 * so be aware of this. It also allows to have
+			 * tags preconfigured on the node that can be
+			 * used later on ruleset(easy administration+
+			 * preventing the check if each tagname configured
+			 * exists in PF.)
+			*/
+				tagid = pf_tagname2tag(tagn->tagname);
+      		        	tag->tag = tagid;
+				r_tag->m_tag_cookie = MTAG_ABI_COMPAT;
+				*flags = tagn->flags;
+				
+			}
+#ifdef NG_PF_DEBUG
+			printf("%s: Translating netgraph to PF\n "
+				"\ttagname=%s tag_id=%d tag_flags=%x "
+				"pf_tag_id=%d\n", __func__, tagn->tagname,
+			       	tagn->tag, tagn->flags, tag->tag);
+#endif
+		}	
+		r_tag = m_tag_locate(mtag, tagzone, PACKET_TAG_PF_TAG, 
+				r_tag);	
+     	} 
+
+     return;
+}
+
diff -uNr ng_pf_orig/src/sys/netgraph/ng_pf.h ng_pf/src/sys/netgraph/ng_pf.h
--- ng_pf_orig/src/sys/netgraph/ng_pf.h	1970-01-01 01:00:00.000000000 +0100
+++ ng_pf/src/sys/netgraph/ng_pf.h	2007-12-15 19:06:49.000000000 +0100
@@ -0,0 +1,124 @@
+/*-
+ * Copyright 2007, Ermal Luçi <ermal.luci@gmail.org>
+ * All rights reserved.
+ *
+ * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+
+#define NG_PF_NODE_TYPE    "pf"
+#define NGM_PF_COOKIE      1105988990
+
+#define NG_PF_TAG_NAME_SIZE         32
+
+/* messages this node understands, plus ordinary ones */
+enum {
+        NGM_PF_ADD_TAG = 1,
+	NGM_PF_CHG_TAG_FLAGS,
+        NGM_PF_RMV_TAG,
+        NGM_PF_GET_TAGS,
+	NGM_PF_GET_STATS,
+	NGM_PF_CLR_STATS,
+	NGM_PF_GETCLR_STATS,
+	NGM_PF_SET_TAGZONE,
+	NGM_PF_GET_TAGZONE,
+	NGM_PF_DEFAULT_TAGZONE
+};
+
+struct ng_pf_command {
+	char 		name[NG_PF_TAG_NAME_SIZE];
+	u_int16_t	tag;
+	int		flags;
+};
+ 
+#define NG_PF_COMMAND { 					\
+	{ "tagname", 	&ng_parse_hookbuf_type }, 		\
+	{ "tag",	&ng_parse_uint16_type }, 		\
+	{ "flags",	&ng_parse_uint32_type },		\
+	{ NULL } 						\
+}
+
+/* Structur for GET_TAGS command */
+struct ng_pf_get_tags {
+	u_int32_t n;	/* numer of tags */
+	struct ng_pf_command tags[];
+};
+
+#define NG_PF_GET_TAGS { 					\
+	{ "n",		&ng_parse_uint32_type }, 		\
+	{ "tags",	&ng_pf_struct_array_type }, 		\
+	{ NULL } 						\
+}
+
+struct ng_pf_stats {
+	u_int32_t packets_in;
+	u_int32_t packets_out;
+};
+
+#define NG_PF_GET_STATS {					\
+	{ "packets_in",		&ng_parse_uint32_type },	\
+	{ "packets_out",	&ng_parse_uint32_type },	\
+	{ NULL }						\
+}
+
+#define	NG_PF_SKIP_RULES	0x0002
+#define NG_PF_KILL_STATE	0x0004
+#define NG_PF_IGNORE_STATE	0x0008
+#define NG_PF_DROP_PACKET	0x0010
+#define NG_PF_SKIP_FIREWALLS	0x0020
+
+#ifdef _KERNEL
+
+struct pf_state;
+
+typedef int ng_pf_input_t(struct mbuf **, int, u_int16_t, struct pf_state **);
+extern	ng_pf_input_t	*ng_pf_input_p;
+#define	NG_PF_LOADED	(ng_pf_input_p != NULL)
+
+
+
+/* XXX: Should we lock this?! */
+struct ng_pf_tagnames {
+	TAILQ_ENTRY(ng_pf_tagnames) entries;
+	char tagname[NG_PF_TAG_NAME_SIZE];
+	u_int16_t	tag;
+	int		flags;
+};
+
+TAILQ_HEAD(ng_pf_tags, ng_pf_tagnames) ng_pf_tags =
+         TAILQ_HEAD_INITIALIZER(ng_pf_tags);
+
+struct ng_pf_tag {
+	struct m_tag	mt;		/* tag header */
+	struct pf_state	*s;		/* PF state so we can do tricks */
+	u_int16_t	hooknum;	
+	int		direction;
+};
+
+#define	TAGSIZ	(sizeof(struct ng_pf_tag) - sizeof(struct m_tag))
+
+#endif /* _KERNEL */


>Release-Note:
>Audit-Trail:
>Unformatted:



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