From owner-freebsd-bugs@FreeBSD.ORG Sat Dec 15 19:20:02 2007 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 5A77516A46E for ; Sat, 15 Dec 2007 19:20:02 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:4f8:fff6::28]) by mx1.freebsd.org (Postfix) with ESMTP id 3956913C46B for ; Sat, 15 Dec 2007 19:20:02 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.14.2/8.14.2) with ESMTP id lBFJK2Ia066818 for ; Sat, 15 Dec 2007 19:20:02 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.2/8.14.1/Submit) id lBFJK2H0066817; Sat, 15 Dec 2007 19:20:02 GMT (envelope-from gnats) Resent-Date: Sat, 15 Dec 2007 19:20:02 GMT Resent-Message-Id: <200712151920.lBFJK2H0066817@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Ermal Luçi Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 1B61616A417 for ; Sat, 15 Dec 2007 19:10:50 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (www.freebsd.org [IPv6:2001:4f8:fff6::21]) by mx1.freebsd.org (Postfix) with ESMTP id 0A01B13C459 for ; Sat, 15 Dec 2007 19:10:50 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (localhost [127.0.0.1]) by www.freebsd.org (8.14.2/8.14.2) with ESMTP id lBFJAbSS041894 for ; Sat, 15 Dec 2007 19:10:37 GMT (envelope-from nobody@www.freebsd.org) Received: (from nobody@localhost) by www.freebsd.org (8.14.2/8.14.1/Submit) id lBFJAbPB041893; Sat, 15 Dec 2007 19:10:37 GMT (envelope-from nobody) Message-Id: <200712151910.lBFJAbPB041893@www.freebsd.org> Date: Sat, 15 Dec 2007 19:10:37 GMT From: Ermal Luçi To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-3.1 Cc: Subject: kern/118727: [PATCH] ng_pf module X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 15 Dec 2007 19:20:02 -0000 >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 +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 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 + * All rights reserved. + * + * Copyright 2005, Gleb Smirnoff + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +/* XXX: check why does not work with */ +#include +#include + +#include +#include +#include +#include + + +#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 + * All rights reserved. + * + * Copyright 2005, Gleb Smirnoff + * 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: