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