kern/118727: [PATCH] ng_pf module
Ermal Luçi
eri at FreeBSD.org
Sat Dec 15 11:20:02 PST 2007
>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 at gmail.com>
+ * All rights reserved.
+ *
+ * Copyright 2005, Gleb Smirnoff <glebius at 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 at gmail.org>
+ * All rights reserved.
+ *
+ * Copyright 2005, Gleb Smirnoff <glebius at 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:
More information about the freebsd-bugs
mailing list