ng_patch node
Maxim Ignatenko
gelraen.ua at gmail.com
Mon Jan 11 23:06:13 UTC 2010
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.ua at 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.ua at 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_ */
More information about the freebsd-net
mailing list