From owner-svn-src-all@FreeBSD.ORG Wed Jun 9 12:25:57 2010 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id E4ED01065676; Wed, 9 Jun 2010 12:25:57 +0000 (UTC) (envelope-from ae@FreeBSD.org) Received: from svn.freebsd.org (unknown [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id D1E208FC12; Wed, 9 Jun 2010 12:25:57 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id o59CPvi6020829; Wed, 9 Jun 2010 12:25:57 GMT (envelope-from ae@svn.freebsd.org) Received: (from ae@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id o59CPv85020819; Wed, 9 Jun 2010 12:25:57 GMT (envelope-from ae@svn.freebsd.org) Message-Id: <201006091225.o59CPv85020819@svn.freebsd.org> From: "Andrey V. Elsukov" Date: Wed, 9 Jun 2010 12:25:57 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r208946 - in head: share/man/man4 sys/conf sys/modules/netgraph sys/modules/netgraph/patch sys/netgraph X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 09 Jun 2010 12:25:58 -0000 Author: ae Date: Wed Jun 9 12:25:57 2010 New Revision: 208946 URL: http://svn.freebsd.org/changeset/base/208946 Log: New netgraph node ng_patch(4). It performs data modification of packets passing through. Modifications are restricted to a subset of C language operations on unsigned integers of 8, 16, 32 or 64 bit size. These are: set to new value (=), addition (+=), subtraction (-=), multiplication (*=), division (/=), negation (= -), bitwise AND (&=), bitwise OR (|=), bitwise eXclusive OR (^=), shift left (<<=), shift right (>>=). Several operations are all applied to a packet sequentially in order they were specified by user. Submitted by: Maxim Ignatenko Vadim Goncharov Discussed with: net@ Approved by: mav (mentor) MFC after: 1 month Added: head/share/man/man4/ng_patch.4 (contents, props changed) head/sys/modules/netgraph/patch/ head/sys/modules/netgraph/patch/Makefile (contents, props changed) head/sys/netgraph/ng_patch.c (contents, props changed) head/sys/netgraph/ng_patch.h (contents, props changed) Modified: head/share/man/man4/Makefile head/sys/conf/NOTES head/sys/conf/files head/sys/conf/options head/sys/modules/netgraph/Makefile Modified: head/share/man/man4/Makefile ============================================================================== --- head/share/man/man4/Makefile Wed Jun 9 12:06:08 2010 (r208945) +++ head/share/man/man4/Makefile Wed Jun 9 12:25:57 2010 (r208946) @@ -274,6 +274,7 @@ MAN= aac.4 \ ng_nat.4 \ ng_netflow.4 \ ng_one2many.4 \ + ng_patch.4 \ ng_ppp.4 \ ng_pppoe.4 \ ng_pptpgre.4 \ Added: head/share/man/man4/ng_patch.4 ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/share/man/man4/ng_patch.4 Wed Jun 9 12:25:57 2010 (r208946) @@ -0,0 +1,235 @@ +.\" Copyright (c) 2010 Maxim Ignatenko +.\" Copyright (c) 2010 Vadim Goncharov +.\" 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$ +.\" +.Dd June 9, 2010 +.Dt NG_PATCH 4 +.Os +.Sh NAME +.Nm ng_patch +.Nd "trivial mbuf data modifying netgraph node type" +.Sh SYNOPSIS +.In netgraph/ng_patch.h +.Sh DESCRIPTION +The +.Nm patch +node performs data modification of packets passing through it. +Modifications are restricted to a subset of C language operations +on unsigned integers of 8, 16, 32 or 64 bit size. +These are: set to new value (=), addition (+=), subtraction (-=), +multiplication (*=), division (/=), negation (= -), +bitwise AND (&=), bitwise OR (|=), bitwise eXclusive OR (^=), +shift left (<<=), shift right (>>=). +A negation operation is the one exception: integer is treated as signed +and second operand (the +.Va value ) +is not used. +There may be several modification operations, they are all applied +to a packet sequentially in order they were specified by user. +Data payload of packet is viewed as array of bytes, with zero offset +corresponding to the very first byte of packet headers, and +.Va length +bytes beginning from +.Va offset +are taken as a single integer in network byte order. +.Sh HOOKS +This node type has two hooks: +.Bl -tag -width indent +.It Va in +Packets received on this hook are modified according to rules specified +in config and then forwarded to +.Ar out +hook, if it exists and connected. +Otherwise they are reflected back to the +.Ar in +hook. +.It Va out +Packets received on this hook are forwarded to +.Ar in +hook without any changes. +.El +.Sh CONTROL MESSAGES +This node type supports the generic control messages, plus the following: +.Bl -tag -width indent +.It Dv NGM_PATCH_SETCONFIG Pq Li setconfig +This command sets the sequence of modify operations +that will be applied to incoming data on a hook. +The following +.Vt "struct ng_patch_config" +must be supplied as an argument: +.Bd -literal -offset 4n +struct ng_patch_op { + uint64_t value; + uint32_t offset; + uint16_t length; /* 1,2,4 or 8 bytes */ + uint16_t mode; +}; +/* Patching modes */ +#define NG_PATCH_MODE_SET 1 +#define NG_PATCH_MODE_ADD 2 +#define NG_PATCH_MODE_SUB 3 +#define NG_PATCH_MODE_MUL 4 +#define NG_PATCH_MODE_DIV 5 +#define NG_PATCH_MODE_NEG 6 +#define NG_PATCH_MODE_AND 7 +#define NG_PATCH_MODE_OR 8 +#define NG_PATCH_MODE_XOR 9 +#define NG_PATCH_MODE_SHL 10 +#define NG_PATCH_MODE_SHR 11 + +struct ng_patch_config { + uint32_t count; + uint32_t csum_flags; + struct ng_patch_op ops[]; +}; +.Ed +.Pp +The +.Va csum_flags +can be set to any combination of CSUM_IP, CSUM_TCP, CSUM_SCTP and CSUM_UDP +(other values are ignored) for instructing the IP stack to recalculate the +corresponding checksum before transmitting packet on output interface. +The +.Nm +node does not do any checksum correction by itself. +.It Dv NGM_PATCH_GETCONFIG Pq Li getconfig +This control message obtains current set of modify operations, +returned as +.Vt "struct ng_patch_config" . +.It Dv NGM_PATCH_GET_STATS Pq Li getstats +Returns node statistics as a +.Vt "struct ng_patch_stats" . +.It Dv NGM_PATCH_CLR_STATS Pq Li clrstats +Clear node statistics. +.It Dv NGM_PATCH_GETCLR_STATS Pq Li getclrstats +This command is identical to +.Dv NGM_PATCH_GET_STATS , +except that the statistics are also atomically cleared. +.El +.Sh SHUTDOWN +This node shuts down upon receipt of a +.Dv NGM_SHUTDOWN +control message, or when all hooks have been disconnected. +.Sh EXAMPLES +The +.Nm +node allows to modify TTL and TOS/DSCP fields in IP packets. +Suppose you have two adjacent simplex links to remote network +(e.g.\& satellite), so that the packets expiring in between +will generate unwanted ICMP-replies which have to go forth, not back. +Thus you need to raise TTL of every packet entering link link by 2 +to ensure the TTL will not reach zero there. +So you setup +.Xr ipfw 8 +rule with +.Cm netgraph +action to inject packets going to other end of simplex link by the +following +.Xr ngctl 8 +script: +.Bd -literal -offset 4n +/usr/sbin/ngctl -f- <<-SEQ + mkpeer ipfw: patch 200 in + name ipfw:200 ttl_add + msg ttl_add: setconfig { count=1 csum_flags=1 ops=[ \e + { mode=2 value=3 length=1 offset=8 } ] } +SEQ +/sbin/ipfw add 150 netgraph 200 ip from any to simplex.remote.net +.Ed +.Pp +Here +.Dq Li ttl_add +node of type +.Nm +configured to add (mode +.Dv NG_PATCH_MODE_ADD ) +a +.Va value +of 3 to a one-byte TTL field, which is 9th byte of IP packet header. +.Pp +Another example would be two consecutive modifications of packet TOS +field: say, you need to clear the +.Dv IPTOS_THROUGHPUT +bit and set the +.Dv IPTOS_MINCOST +bit. +So you do: +.Bd -literal -offset 4n +/usr/sbin/ngctl -f- <<-SEQ + mkpeer ipfw: patch 300 in + name ipfw:300 tos_chg + msg tos_chg: setconfig { count=2 csum_flags=1 ops=[ \e + { mode=7 value=0xf7 length=1 offset=1 } \e + { mode=8 value=0x02 length=1 offset=1 } ] } +SEQ +/sbin/ipfw add 160 netgraph 600 ip from any to any not dst-port 80 +.Ed +.Pp +This first does +.Dv NG_PATCH_MODE_AND +clearing the fourth bit and then +.Dv NG_PATCH_MODE_OR +setting the third bit. +.Pp +In both examples the +.Va csum_flags +field indicates that IP checksum (but not TCP or UDP checksum) should be +recalculated before transmit. +.Pp +Note: one should ensure that packets are returned to ipfw after processing +inside +.Xr netgraph 4 , +by setting appropriate +.Xr sysctl 8 +variable: +.Bd -literal -offset 4n +sysctl net.inet.ip.fw.one_pass=0 +.Ed +.Sh SEE ALSO +.Xr netgraph 4 , +.Xr ng_ipfw 4 , +.Xr ngctl 8 +.Sh HISTORY +The +.Nm +node type was implemented in +.Fx 8.1 . +.Sh AUTHORS +.An "Maxim Ignatenko" Aq gelraen.ua@gmail.com . +This manual page was written by +.An "Vadim Goncharov" Aq vadimnuclight@tpu.ru . +.Sh BUGS +Node blindly tries to apply every patching operation to each packet +(except those which offset if greater than length of the packet), +so be sure that you supply only the right packets to it (e.g. changing +bytes in the ARP packets meant to be in IP header could corrupt +them and make your machine unreachable from the network). +.Pp +.Em !!! WARNING !!! +.Pp +Output path of the IP stack assumes correct fields and lengths in the +packets - changing them by mistake to incorrect values can cause +unpredictable results including kernel panics. Modified: head/sys/conf/NOTES ============================================================================== --- head/sys/conf/NOTES Wed Jun 9 12:06:08 2010 (r208945) +++ head/sys/conf/NOTES Wed Jun 9 12:25:57 2010 (r208946) @@ -706,6 +706,7 @@ options NETGRAPH_MPPC_ENCRYPTION options NETGRAPH_NETFLOW options NETGRAPH_NAT options NETGRAPH_ONE2MANY +options NETGRAPG_PATCH options NETGRAPH_PIPE options NETGRAPH_PPP options NETGRAPH_PPPOE Modified: head/sys/conf/files ============================================================================== --- head/sys/conf/files Wed Jun 9 12:06:08 2010 (r208945) +++ head/sys/conf/files Wed Jun 9 12:25:57 2010 (r208946) @@ -2470,6 +2470,7 @@ netgraph/ng_mppc.c optional netgraph_mp netgraph/ng_nat.c optional netgraph_nat inet libalias netgraph/ng_one2many.c optional netgraph_one2many netgraph/ng_parse.c optional netgraph +netgraph/ng_patch.c optional netgraph_patch netgraph/ng_pipe.c optional netgraph_pipe netgraph/ng_ppp.c optional netgraph_ppp netgraph/ng_pppoe.c optional netgraph_pppoe Modified: head/sys/conf/options ============================================================================== --- head/sys/conf/options Wed Jun 9 12:06:08 2010 (r208945) +++ head/sys/conf/options Wed Jun 9 12:25:57 2010 (r208946) @@ -486,6 +486,7 @@ NETGRAPH_MPPC_ENCRYPTION opt_netgraph.h NETGRAPH_NAT opt_netgraph.h NETGRAPH_NETFLOW opt_netgraph.h NETGRAPH_ONE2MANY opt_netgraph.h +NETGRAPH_PATCH opt_netgraph.h NETGRAPH_PIPE opt_netgraph.h NETGRAPH_PPP opt_netgraph.h NETGRAPH_PPPOE opt_netgraph.h Modified: head/sys/modules/netgraph/Makefile ============================================================================== --- head/sys/modules/netgraph/Makefile Wed Jun 9 12:06:08 2010 (r208945) +++ head/sys/modules/netgraph/Makefile Wed Jun 9 12:25:57 2010 (r208946) @@ -35,6 +35,7 @@ SUBDIR= async \ netflow \ netgraph \ one2many \ + patch \ pipe \ ppp \ pppoe \ Added: head/sys/modules/netgraph/patch/Makefile ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/modules/netgraph/patch/Makefile Wed Jun 9 12:25:57 2010 (r208946) @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= ng_patch +SRCS= ng_patch.c + +.include Added: head/sys/netgraph/ng_patch.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/netgraph/ng_patch.c Wed Jun 9 12:25:57 2010 (r208946) @@ -0,0 +1,566 @@ +/*- + * Copyright (C) 2010 by Maxim Ignatenko + * 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include /* be64toh(), htobe64() */ +#include +#include +#include +#include + +static ng_constructor_t ng_patch_constructor; +static ng_rcvmsg_t ng_patch_rcvmsg; +static ng_shutdown_t ng_patch_shutdown; +static ng_newhook_t ng_patch_newhook; +static ng_rcvdata_t ng_patch_rcvdata; +static ng_disconnect_t ng_patch_disconnect; + +#define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0)) + +static int +ng_patch_config_getlen(const struct ng_parse_type *type, const u_char *start, + const u_char *buf) +{ + const struct ng_patch_config *p; + + p = (const struct ng_patch_config *)(buf - + OFFSETOF(struct ng_patch_config, ops)); + return (p->count); +} + +static const struct ng_parse_struct_field ng_patch_op_type_fields[] + = NG_PATCH_OP_TYPE_INFO; +static const struct ng_parse_type ng_patch_op_type = { + &ng_parse_struct_type, + &ng_patch_op_type_fields +}; + +static const struct ng_parse_array_info ng_patch_confarr_info = { + &ng_patch_op_type, + &ng_patch_config_getlen +}; +static const struct ng_parse_type ng_patch_confarr_type = { + &ng_parse_array_type, + &ng_patch_confarr_info +}; + +static const struct ng_parse_struct_field ng_patch_config_type_fields[] + = NG_PATCH_CONFIG_TYPE_INFO; +static const struct ng_parse_type ng_patch_config_type = { + &ng_parse_struct_type, + &ng_patch_config_type_fields +}; + +static const struct ng_parse_struct_field ng_patch_stats_fields[] + = NG_PATCH_STATS_TYPE_INFO; +static const struct ng_parse_type ng_patch_stats_type = { + &ng_parse_struct_type, + &ng_patch_stats_fields +}; + +static const struct ng_cmdlist ng_patch_cmdlist[] = { + { + NGM_PATCH_COOKIE, + NGM_PATCH_GETCONFIG, + "getconfig", + NULL, + &ng_patch_config_type + }, + { + NGM_PATCH_COOKIE, + NGM_PATCH_SETCONFIG, + "setconfig", + &ng_patch_config_type, + NULL + }, + { + NGM_PATCH_COOKIE, + NGM_PATCH_GET_STATS, + "getstats", + NULL, + &ng_patch_stats_type + }, + { + NGM_PATCH_COOKIE, + NGM_PATCH_CLR_STATS, + "clrstats", + NULL, + NULL + }, + { + NGM_PATCH_COOKIE, + NGM_PATCH_GETCLR_STATS, + "getclrstats", + NULL, + &ng_patch_stats_type + }, + { 0 } +}; + +static struct ng_type typestruct = { + .version = NG_ABI_VERSION, + .name = NG_PATCH_NODE_TYPE, + .constructor = ng_patch_constructor, + .rcvmsg = ng_patch_rcvmsg, + .shutdown = ng_patch_shutdown, + .newhook = ng_patch_newhook, + .rcvdata = ng_patch_rcvdata, + .disconnect = ng_patch_disconnect, + .cmdlist = ng_patch_cmdlist, +}; +NETGRAPH_INIT(patch, &typestruct); + +union patch_val { + uint8_t v1; + uint16_t v2; + uint32_t v4; + uint64_t v8; +}; + +struct ng_patch_priv { + hook_p in; + hook_p out; + struct ng_patch_config *config; + union patch_val *val; + struct ng_patch_stats stats; +}; +typedef struct ng_patch_priv *priv_p; + +#define NG_PATCH_CONF_SIZE(count) (sizeof(struct ng_patch_config) + \ + (count) * sizeof(struct ng_patch_op)) + +static void do_patch(priv_p conf, struct mbuf *m); + +static int +ng_patch_constructor(node_p node) +{ + priv_p privdata; + + privdata = malloc(sizeof(*privdata), M_NETGRAPH, M_WAIT | M_ZERO); + NG_NODE_SET_PRIVATE(node, privdata); + privdata->in = NULL; + privdata->out = NULL; + privdata->config = NULL; + return (0); +} + +static int +ng_patch_newhook(node_p node, hook_p hook, const char *name) +{ + const priv_p privp = NG_NODE_PRIVATE(node); + + if (strncmp(name, NG_PATCH_HOOK_IN, strlen(NG_PATCH_HOOK_IN)) == 0) { + privp->in = hook; + } else if (strncmp(name, NG_PATCH_HOOK_OUT, + strlen(NG_PATCH_HOOK_OUT)) == 0) { + privp->out = hook; + } else + return (EINVAL); + return(0); +} + +static int +ng_patch_rcvmsg(node_p node, item_p item, hook_p lasthook) +{ + const priv_p privp = NG_NODE_PRIVATE(node); + struct ng_patch_config *conf; + struct ng_mesg *msg; + struct ng_mesg *resp = NULL; + int i, clear = 0; + int error = 0; + + NGI_GET_MSG(item, msg); + switch (msg->header.typecookie) { + case NGM_PATCH_COOKIE: + switch (msg->header.cmd) { + case NGM_PATCH_GETCONFIG: + if (privp->config == NULL) + break; + NG_MKRESPONSE(resp, msg, + NG_PATCH_CONF_SIZE(privp->config->count), M_WAIT); + bcopy(privp->config, resp->data, + NG_PATCH_CONF_SIZE(privp->config->count)); + break; + case NGM_PATCH_SETCONFIG: + { + struct ng_patch_config *newconf; + union patch_val *newval; + + if (msg->header.arglen < sizeof(struct ng_patch_config)) { + error = EINVAL; + break; + } + + conf = (struct ng_patch_config *)msg->data; + if (msg->header.arglen < NG_PATCH_CONF_SIZE(conf->count)) { + error = EINVAL; + break; + } + + for(i = 0; i < conf->count; i++) { + switch(conf->ops[i].length) { + case 1: + case 2: + case 4: + case 8: + break; + default: + error = EINVAL; + break; + } + if (error != 0) + break; + } + + conf->csum_flags &= CSUM_IP | CSUM_TCP | CSUM_UDP | + CSUM_SCTP; + + if (error == 0) { + newconf = malloc(NG_PATCH_CONF_SIZE(conf->count), + M_NETGRAPH, M_WAIT); + newval = malloc(conf->count * sizeof(union patch_val), + M_NETGRAPH, M_WAIT); + for(i = 0; i < conf->count; i++) { + switch (conf->ops[i].length) { + case 1: + newval[i].v1 = conf->ops[i].value; + break; + case 2: + newval[i].v2 = conf->ops[i].value; + break; + case 4: + newval[i].v4 = conf->ops[i].value; + break; + case 8: + newval[i].v8 = conf->ops[i].value; + break; + } + } + bcopy(conf, newconf, NG_PATCH_CONF_SIZE(conf->count)); + if (privp->val != NULL) + free(privp->val, M_NETGRAPH); + privp->val = newval; + if (privp->config != NULL) + free(privp->config, M_NETGRAPH); + privp->config = newconf; + } + break; + } + case NGM_PATCH_GETCLR_STATS: + clear = 1; + /* FALLTHROUGH */ + case NGM_PATCH_GET_STATS: + NG_MKRESPONSE(resp, msg, sizeof(struct ng_patch_stats), + M_WAIT); + bcopy(&(privp->stats), resp->data, + sizeof(struct ng_patch_stats)); + if (clear == 0) + break; + /* else FALLTHROUGH */ + case NGM_PATCH_CLR_STATS: + bzero(&(privp->stats), sizeof(struct ng_patch_stats)); + break; + default: + error = EINVAL; + break; + } + break; + default: + error = EINVAL; + break; + } + + NG_RESPOND_MSG(error, node, item, resp); + NG_FREE_MSG(msg); + return(error); +} + +static void +do_patch(priv_p privp, struct mbuf *m) +{ + struct ng_patch_config *conf = privp->config; + uint64_t buf; + int i, patched = 0; + + for(i = 0; i < conf->count; i++) { + if (conf->ops[i].offset + conf->ops[i].length > m->m_pkthdr.len) + continue; + + /* for "=" operation we don't need to copy data from mbuf */ + if (conf->ops[i].mode != NG_PATCH_MODE_SET) { + m_copydata(m, conf->ops[i].offset, + conf->ops[i].length, (caddr_t)&buf); + } + + switch (conf->ops[i].length) { + case 1: + switch (conf->ops[i].mode) { + case NG_PATCH_MODE_SET: + *((uint8_t *)&buf) = privp->val[i].v1; + break; + case NG_PATCH_MODE_ADD: + *((uint8_t *)&buf) += privp->val[i].v1; + break; + case NG_PATCH_MODE_SUB: + *((uint8_t *)&buf) -= privp->val[i].v1; + break; + case NG_PATCH_MODE_MUL: + *((uint8_t *)&buf) *= privp->val[i].v1; + break; + case NG_PATCH_MODE_DIV: + *((uint8_t *)&buf) /= privp->val[i].v1; + break; + case NG_PATCH_MODE_NEG: + *((int8_t *)&buf) = - *((int8_t *)&buf); + break; + case NG_PATCH_MODE_AND: + *((uint8_t *)&buf) &= privp->val[i].v1; + break; + case NG_PATCH_MODE_OR: + *((uint8_t *)&buf) |= privp->val[i].v1; + break; + case NG_PATCH_MODE_XOR: + *((uint8_t *)&buf) ^= privp->val[i].v1; + break; + case NG_PATCH_MODE_SHL: + *((uint8_t *)&buf) <<= privp->val[i].v1; + break; + case NG_PATCH_MODE_SHR: + *((uint8_t *)&buf) >>= privp->val[i].v1; + break; + } + break; + case 2: + *((int16_t *)&buf) = ntohs(*((int16_t *)&buf)); + switch (conf->ops[i].mode) { + case NG_PATCH_MODE_SET: + *((uint16_t *)&buf) = privp->val[i].v2; + break; + case NG_PATCH_MODE_ADD: + *((uint16_t *)&buf) += privp->val[i].v2; + break; + case NG_PATCH_MODE_SUB: + *((uint16_t *)&buf) -= privp->val[i].v2; + break; + case NG_PATCH_MODE_MUL: + *((uint16_t *)&buf) *= privp->val[i].v2; + break; + case NG_PATCH_MODE_DIV: + *((uint16_t *)&buf) /= privp->val[i].v2; + break; + case NG_PATCH_MODE_NEG: + *((int16_t *)&buf) = - *((int16_t *)&buf); + break; + case NG_PATCH_MODE_AND: + *((uint16_t *)&buf) &= privp->val[i].v2; + break; + case NG_PATCH_MODE_OR: + *((uint16_t *)&buf) |= privp->val[i].v2; + break; + case NG_PATCH_MODE_XOR: + *((uint16_t *)&buf) ^= privp->val[i].v2; + break; + case NG_PATCH_MODE_SHL: + *((uint16_t *)&buf) <<= privp->val[i].v2; + break; + case NG_PATCH_MODE_SHR: + *((uint16_t *)&buf) >>= privp->val[i].v2; + break; + } + *((int16_t *)&buf) = htons(*((int16_t *)&buf)); + break; + case 4: + *((int32_t *)&buf) = ntohl(*((int32_t *)&buf)); + switch (conf->ops[i].mode) { + case NG_PATCH_MODE_SET: + *((uint32_t *)&buf) = privp->val[i].v4; + break; + case NG_PATCH_MODE_ADD: + *((uint32_t *)&buf) += privp->val[i].v4; + break; + case NG_PATCH_MODE_SUB: + *((uint32_t *)&buf) -= privp->val[i].v4; + break; + case NG_PATCH_MODE_MUL: + *((uint32_t *)&buf) *= privp->val[i].v4; + break; + case NG_PATCH_MODE_DIV: + *((uint32_t *)&buf) /= privp->val[i].v4; + break; + case NG_PATCH_MODE_NEG: + *((int32_t *)&buf) = - *((int32_t *)&buf); + break; + case NG_PATCH_MODE_AND: + *((uint32_t *)&buf) &= privp->val[i].v4; + break; + case NG_PATCH_MODE_OR: + *((uint32_t *)&buf) |= privp->val[i].v4; + break; + case NG_PATCH_MODE_XOR: + *((uint32_t *)&buf) ^= privp->val[i].v4; + break; + case NG_PATCH_MODE_SHL: + *((uint32_t *)&buf) <<= privp->val[i].v4; + break; + case NG_PATCH_MODE_SHR: + *((uint32_t *)&buf) >>= privp->val[i].v4; + break; + } + *((int32_t *)&buf) = htonl(*((int32_t *)&buf)); + break; + case 8: + *((int64_t *)&buf) = be64toh(*((int64_t *)&buf)); + switch (conf->ops[i].mode) { + case NG_PATCH_MODE_SET: + *((uint64_t *)&buf) = privp->val[i].v8; + break; + case NG_PATCH_MODE_ADD: + *((uint64_t *)&buf) += privp->val[i].v8; + break; + case NG_PATCH_MODE_SUB: + *((uint64_t *)&buf) -= privp->val[i].v8; + break; + case NG_PATCH_MODE_MUL: + *((uint64_t *)&buf) *= privp->val[i].v8; + break; + case NG_PATCH_MODE_DIV: + *((uint64_t *)&buf) /= privp->val[i].v8; + break; + case NG_PATCH_MODE_NEG: + *((int64_t *)&buf) = - *((int64_t *)&buf); + break; + case NG_PATCH_MODE_AND: + *((uint64_t *)&buf) &= privp->val[i].v8; + break; + case NG_PATCH_MODE_OR: + *((uint64_t *)&buf) |= privp->val[i].v8; + break; + case NG_PATCH_MODE_XOR: + *((uint64_t *)&buf) ^= privp->val[i].v8; + break; + case NG_PATCH_MODE_SHL: + *((uint64_t *)&buf) <<= privp->val[i].v8; + break; + case NG_PATCH_MODE_SHR: + *((uint64_t *)&buf) >>= privp->val[i].v8; + break; + } + *((int64_t *)&buf) = htobe64(*((int64_t *)&buf)); + break; + } + + m_copyback(m, conf->ops[i].offset, conf->ops[i].length, + (caddr_t)&buf); + patched = 1; + } + if (patched > 0) + privp->stats.patched++; +} + +static int +ng_patch_rcvdata(hook_p hook, item_p item) +{ + const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + struct mbuf *m; + hook_p target; + int error; + + priv->stats.received++; + NGI_GET_M(item, m); + if (priv->config != NULL && hook == priv->in && + (m->m_flags & M_PKTHDR) != 0) { + m = m_unshare(m,M_NOWAIT); + if (m == NULL) { + priv->stats.dropped++; + NG_FREE_ITEM(item); + return (ENOMEM); + } + do_patch(priv, m); + m->m_flags |= priv->config->csum_flags; + } + + target = NULL; + if (hook == priv->in) { + /* return frames on 'in' hook if 'out' not connected */ + if (priv->out != NULL) + target = priv->out; + else + target = priv->in; + } + if (hook == priv->out && priv->in != NULL) + target = priv->in; + + if (target == NULL) { + priv->stats.dropped++; + NG_FREE_ITEM(item); + NG_FREE_M(m); + return (0); + } + NG_FWD_NEW_DATA(error, item, target, m); + return (error); +} + +static int +ng_patch_shutdown(node_p node) +{ + const priv_p privdata = NG_NODE_PRIVATE(node); + + if (privdata->val != NULL) + free(privdata->val, M_NETGRAPH); + if (privdata->config != NULL) + free(privdata->config, M_NETGRAPH); + NG_NODE_SET_PRIVATE(node, NULL); + NG_NODE_UNREF(node); + free(privdata, M_NETGRAPH); + return (0); +} + +static int +ng_patch_disconnect(hook_p hook) +{ + priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + + if (hook == priv->in) { + priv->in = NULL; + } + if (hook == priv->out) { + priv->out = NULL; + } + if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) + && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) /* already shutting down? */ + ng_rmnode_self(NG_HOOK_NODE(hook)); + return (0); +} + Added: head/sys/netgraph/ng_patch.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/netgraph/ng_patch.h Wed Jun 9 12:25:57 2010 (r208946) @@ -0,0 +1,107 @@ +/*- + * Copyright (C) 2010 by Maxim Ignatenko + * 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$ + */ + +#ifndef _NETGRAPH_NG_PATCH_H_ +#define _NETGRAPH_NG_PATCH_H_ + +/* Node type name. */ +#define NG_PATCH_NODE_TYPE "patch" + +/* Node type cookie. */ +#define NGM_PATCH_COOKIE 1262445509 + +/* Hook names */ +#define NG_PATCH_HOOK_IN "in" +#define NG_PATCH_HOOK_OUT "out" + +/* Netgraph commands understood by this node type */ +enum { + NGM_PATCH_SETCONFIG = 1, + NGM_PATCH_GETCONFIG, + NGM_PATCH_GET_STATS, + NGM_PATCH_CLR_STATS, + NGM_PATCH_GETCLR_STATS +}; + +/* Patching modes */ +enum { + NG_PATCH_MODE_SET = 1, + NG_PATCH_MODE_ADD = 2, + NG_PATCH_MODE_SUB = 3, + NG_PATCH_MODE_MUL = 4, + NG_PATCH_MODE_DIV = 5, + NG_PATCH_MODE_NEG = 6, + NG_PATCH_MODE_AND = 7, + NG_PATCH_MODE_OR = 8, + NG_PATCH_MODE_XOR = 9, + NG_PATCH_MODE_SHL = 10, + NG_PATCH_MODE_SHR = 11 +}; + +struct ng_patch_op { + uint64_t value; + uint32_t offset; + uint16_t length; /* 1,2,4 or 8 (bytes) */ + uint16_t mode; +}; + +#define NG_PATCH_OP_TYPE_INFO { \ + { "value", &ng_parse_uint64_type }, \ + { "offset", &ng_parse_uint32_type }, \ + { "length", &ng_parse_uint16_type }, \ + { "mode", &ng_parse_uint16_type }, \ + { NULL } \ +} + +struct ng_patch_config { + uint32_t count; + uint32_t csum_flags; + struct ng_patch_op ops[]; +}; + +#define NG_PATCH_CONFIG_TYPE_INFO { \ + { "count", &ng_parse_uint32_type }, \ + { "csum_flags", &ng_parse_uint32_type }, \ + { "ops", &ng_patch_confarr_type }, \ + { NULL } \ +} + +struct ng_patch_stats { + uint64_t received; + uint64_t patched; + uint64_t dropped; +}; + +#define NG_PATCH_STATS_TYPE_INFO { \ + { "received", &ng_parse_uint64_type }, \ + { "patched", &ng_parse_uint64_type }, \ + { "dropped", &ng_parse_uint64_type }, \ + { NULL } \ +} + +#endif /* _NETGRAPH_NG_PATCH_H_ */