Hi, I've written netgraph node able to modify arbitrary (8|16|32)-bit unsigned integer in passing packets. Node applies one of =,+,-,&,| and ^ operations to number at given offset. Modification applied to each packet received on "in" hook. If "out" hook is connected - resulting packets passed on it, otherwise - returned back on "in" (for more easy use with ng_ipfw). Packets received on "out" hook passed on "in" unmodified. Node supports two control messages: "getconfig" and "setconfig". Configuration represented in next structure: struct ng_patch_config { uint32_t value; /* argument passed to requested operation */ uint32_t offset; /* offset in bytes */ uint32_t length; /* 1,2 or 4 bytes */ uint32_t mode; /* operation code: 1 - "=", 2 - "+", 3 - "-", 4 - "&", 5 - "|", 6 - "^" */ }; Same names used in ASCII representation.
I wanted to make ipfw able to modify TTL and ToS fields in IP packets, but after some generalization idea looked like described above. Next patch made against 8-STABLE r200201 Index: modules/netgraph/patch/Makefile =================================================================== --- modules/netgraph/patch/Makefile (revision 0) +++ modules/netgraph/patch/Makefile (revision 0) @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= ng_patch +SRCS= ng_patch.c + +.include <bsd.kmod.mk> Index: netgraph/ng_patch.c =================================================================== --- netgraph/ng_patch.c (revision 0) +++ netgraph/ng_patch.c (revision 0) @@ -0,0 +1,393 @@ +/* + * ng_patch.c + */ + +/*- + * 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: * + * * Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the following disclaimer. * + * * 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 COPYRIGHT HOLDERS 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 COPYRIGHT * + * OWNER 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. * + * + * Author: Maxim Ignatenko <gelraen...@gmail.com> + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/ctype.h> +#include <sys/errno.h> + +#include <netgraph/ng_message.h> +#include <netgraph/ng_parse.h> +#include <netgraph/ng_patch.h> +#include <netgraph/netgraph.h> + +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; + +/* Parse type for struct ngpatchstat */ +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 +}; + +/* List of commands and how to convert arguments to/from ASCII */ +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 + }, + { 0 } +}; + +/* Netgraph node type descriptor */ +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); + +/* Information we store for each node */ +struct ng_patch_priv { + hook_p in; + hook_p out; + node_p node; /* back pointer to node */ + uint8_t value1; + uint16_t value2; + uint32_t value4; + struct ng_patch_config config; +}; +typedef struct ng_patch_priv *priv_p; + +static int +ng_patch_constructor(node_p node) +{ + priv_p privdata; + + /* Initialize private descriptor */ + privdata = malloc(sizeof(*privdata), M_NETGRAPH, + M_NOWAIT | M_ZERO); + if (privdata == NULL) + return (ENOMEM); + + /* Link structs together; this counts as our one reference to *nodep */ + NG_NODE_SET_PRIVATE(node, privdata); + privdata->node = node; + privdata->in = NULL; + privdata->out = NULL; + return (0); +} + +static int +ng_patch_newhook(node_p node, hook_p hook, const char *name) +{ + const priv_p patchp = NG_NODE_PRIVATE(node); + + if (strncmp(name, NG_PATCH_HOOK_IN, + strlen(NG_PATCH_HOOK_IN)) == 0) { + patchp->in = hook; + } else if (strncmp(name, NG_PATCH_HOOK_OUT, + strlen(NG_PATCH_HOOK_OUT)) == 0) { + patchp->out = hook; + } else + return (EINVAL); + return(0); +} + +/* + * Get a netgraph control message. + */ +static int +ng_patch_rcvmsg(node_p node, item_p item, hook_p lasthook) +{ + const priv_p patchp = NG_NODE_PRIVATE(node); + struct ng_mesg *resp = NULL; + int error = 0; + struct ng_mesg *msg; + + NGI_GET_MSG(item, msg); + /* Deal with message according to cookie and command */ + switch (msg->header.typecookie) { + case NGM_PATCH_COOKIE: + switch (msg->header.cmd) { + case NGM_PATCH_GETCONFIG: + { + struct ng_patch_config *conf; + + NG_MKRESPONSE(resp, msg, sizeof(*conf), M_NOWAIT); + if (!resp) { + error = ENOMEM; + break; + } + conf = (struct ng_patch_config *) resp->data; + conf->value = patchp->config.value; + conf->offset = patchp->config.offset; + conf->length = patchp->config.length; + conf->mode = patchp->config.mode; + break; + } + case NGM_PATCH_SETCONFIG: + if (msg->header.arglen != sizeof(struct ng_patch_config)) { + error = EINVAL; + break; + } + switch (((struct ng_patch_config *)msg->data)->length) + { + case 1: + case 2: + case 4: + break; + default: + error = EINVAL; + } + if (((struct ng_patch_config *)msg->data)->offset < 0) + error = EINVAL; + if (error == 0) { + patchp->config = *((struct ng_patch_config *) msg->data); + patchp->value1 = patchp->config.value; + patchp->value2 = patchp->config.value; + patchp->value4 = patchp->config.value; + } + break; + default: + error = EINVAL; /* unknown command */ + break; + } + break; + default: + error = EINVAL; /* unknown cookie type */ + break; + } + + /* Take care of synchronous response, if any */ + NG_RESPOND_MSG(error, node, item, resp); + /* Free the message and return */ + NG_FREE_MSG(msg); + return(error); +} + +/* + * Receive data, and do something with it. + */ +static int +ng_patch_rcvdata(hook_p hook, item_p item ) +{ + const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + int error; + struct mbuf *m; + hook_p target; + uint8_t *dst; + + + NGI_GET_M(item, m); + if (hook == priv->in && + m->m_pkthdr.len >= priv->config.offset + priv->config.length) { + /* TODO: maybe better to use m_pulldown here */ + if ((m = m_pullup(m,priv->config.offset + priv->config.length)) == NULL) { + NG_FREE_ITEM(item); + return (ENOBUFS); + } + dst = mtod(m, uint8_t *); + dst = dst + priv->config.offset; + switch (priv->config.mode) + { + case NG_PATCH_MODE_SET: + switch (priv->config.length) + { + case 1: + *dst = priv->value1; + break; + case 2: + *((uint16_t *)dst) = priv->value2; + break; + case 4: + *((uint32_t *)dst) = priv->value4; + break; + } + break; + case NG_PATCH_MODE_ADD: + switch (priv->config.length) + { + case 1: + *dst += priv->value1; + break; + case 2: + *((uint16_t *)dst) += priv->value2; + break; + case 4: + *((uint32_t *)dst) += priv->value4; + break; + } + break; + case NG_PATCH_MODE_SUB: + switch (priv->config.length) + { + case 1: + *dst -= priv->value1; + break; + case 2: + *((uint16_t *)dst) -= priv->value2; + break; + case 4: + *((uint32_t *)dst) -= priv->value4; + break; + } + break; + case NG_PATCH_MODE_AND: + switch (priv->config.length) + { + case 1: + *dst &= priv->value1; + break; + case 2: + *((uint16_t *)dst) &= priv->value2; + break; + case 4: + *((uint32_t *)dst) &= priv->value4; + break; + } + break; + case NG_PATCH_MODE_OR: + switch (priv->config.length) + { + case 1: + *dst |= priv->value1; + break; + case 2: + *((uint16_t *)dst) |= priv->value2; + break; + case 4: + *((uint32_t *)dst) |= priv->value4; + break; + } + break; + case NG_PATCH_MODE_XOR: + switch (priv->config.length) + { + case 1: + *dst ^= priv->value1; + break; + case 2: + *((uint16_t *)dst) ^= priv->value2; + break; + case 4: + *((uint32_t *)dst) ^= priv->value4; + break; + } + break; + } + } + + target = NULL; + if (hook == priv->in) { + if (priv->out != NULL) + target = priv->out; + else + target = priv->in; /* return frames on 'in' hook if 'out' not connected*/ + } + if (hook == priv->out && priv->in != NULL) + target = priv->in; + + if (target == NULL) { + NG_FREE_ITEM(item); + NG_FREE_M(m); + return 0; + } + + NG_FWD_NEW_DATA(error, item, target, m); + + return (error); +} + +/* + * Do local shutdown processing.. + */ +static int +ng_patch_shutdown(node_p node) +{ + const priv_p privdata = NG_NODE_PRIVATE(node); + +#ifndef PERSISTANT_NODE + NG_NODE_SET_PRIVATE(node, NULL); + NG_NODE_UNREF(node); + free(privdata, M_NETGRAPH); +#else + if (node->nd_flags & NGF_REALLY_DIE) { + NG_NODE_SET_PRIVATE(node, NULL); + NG_NODE_UNREF(privdata->node); + free(privdata, M_NETGRAPH); + return (0); + } + NG_NODE_REVIVE(node); /* tell ng_rmnode() we will persist */ +#endif /* PERSISTANT_NODE */ + return (0); +} + +/* + * Hook disconnection + * + * For this type, removal of the last link destroys the node + */ +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); +} + Index: netgraph/ng_patch.h =================================================================== --- netgraph/ng_patch.h (revision 0) +++ netgraph/ng_patch.h (revision 0) @@ -0,0 +1,78 @@ +/* + * ng_patch.h + */ + +/*- + * 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: * + * * Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the following disclaimer. * + * * 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 COPYRIGHT HOLDERS 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 COPYRIGHT * + * OWNER 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. * + * + * Author: Maxim Ignatenko <gelraen...@gmail.com> + * + * $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 1262445507 + +/* 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, +}; + +/* Patching modes */ +#define NG_PATCH_MODE_SET 1 +#define NG_PATCH_MODE_ADD 2 +#define NG_PATCH_MODE_SUB 3 +#define NG_PATCH_MODE_AND 4 +#define NG_PATCH_MODE_OR 5 +#define NG_PATCH_MODE_XOR 6 + +struct ng_patch_config { + uint32_t value; + uint32_t offset; + uint32_t length; /* 1,2 or 4 bytes */ + uint32_t mode; +}; + +#define NG_PATCH_CONFIG_TYPE_INFO { \ + { "value", &ng_parse_uint32_type }, \ + { "offset", &ng_parse_uint32_type }, \ + { "length", &ng_parse_uint32_type }, \ + { "mode", &ng_parse_uint32_type }, \ + { NULL } \ +} + +#endif /* _NETGRAPH_NG_PATCH_H_ */ _______________________________________________ freebsd-net@freebsd.org mailing list http://lists.freebsd.org/mailman/listinfo/freebsd-net To unsubscribe, send any mail to "freebsd-net-unsubscr...@freebsd.org"