From owner-freebsd-net@FreeBSD.ORG Mon Jan 11 23:06:13 2010 Return-Path: Delivered-To: freebsd-net@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id AFA391065692 for ; Mon, 11 Jan 2010 23:06:13 +0000 (UTC) (envelope-from gelraen.ua@gmail.com) Received: from mail-bw0-f213.google.com (mail-bw0-f213.google.com [209.85.218.213]) by mx1.freebsd.org (Postfix) with ESMTP id 0DE1C8FC14 for ; Mon, 11 Jan 2010 23:06:12 +0000 (UTC) Received: by bwz5 with SMTP id 5so14182098bwz.3 for ; Mon, 11 Jan 2010 15:06:07 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:received:from:date:message-id :subject:to:content-type; bh=FsNrR4g8RHGVuGNPuWVv3zWK+BWa4cQOdgqVlt+MMyc=; b=M2RT6wjI1fJQnwk8TMalfTsgrrPrScTS7Hs4KySUgK+QntQpQ9iViAw7CflAc8HocP uHgEuRWUznjmy5HRQ7g4FXVpSZb8FI4hM/cPfW+YGd+ioH+grqj3spuSZESWHcLaEnyx at9FGbc9CjziVNU9DrBqpQ4GA00bBhw0Jad9k= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:from:date:message-id:subject:to:content-type; b=U+4xotVc4kQ1dZwjnzNNnm9E7u9PL8gm5dGrO49kXaCiubUiqfaI7CUrKWRMQsdXjg 4zS0OPLuPwSfjm9i7fiORbLmDrnfo0LFnEWlwsgy/0iyDqQ2isbxOIj9CyrSJZ4iJS92 uxQTlKcC7E+UAUsVBfodv6MTVaLxolGSwOHsA= MIME-Version: 1.0 Received: by 10.204.157.24 with SMTP id z24mr4520786bkw.208.1263249670237; Mon, 11 Jan 2010 14:41:10 -0800 (PST) From: Maxim Ignatenko Date: Tue, 12 Jan 2010 00:40:50 +0200 Message-ID: To: freebsd-net@freebsd.org Content-Type: text/plain; charset=UTF-8 Subject: ng_patch node X-BeenThere: freebsd-net@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Networking and TCP/IP with FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 11 Jan 2010 23:06:13 -0000 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 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 + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include + +#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; + +/* 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 + * + * $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_ */