1d359a62dSAndrey V. Elsukov /*- 2bf909fc9SJulian Elischer * Copyright (c) 2010 Maxim Ignatenko <gelraen.ua@gmail.com> 3*426b3d04SJulian Elischer * Copyright (c) 2015 Dmitry Vagin <daemon.hammer@ya.ru> 4d359a62dSAndrey V. Elsukov * All rights reserved. 5d359a62dSAndrey V. Elsukov * 6d359a62dSAndrey V. Elsukov * Redistribution and use in source and binary forms, with or without 7d359a62dSAndrey V. Elsukov * modification, are permitted provided that the following conditions 8d359a62dSAndrey V. Elsukov * are met: 9d359a62dSAndrey V. Elsukov * 1. Redistributions of source code must retain the above copyright 10d359a62dSAndrey V. Elsukov * notice, this list of conditions and the following disclaimer. 11d359a62dSAndrey V. Elsukov * 2. Redistributions in binary form must reproduce the above copyright 12d359a62dSAndrey V. Elsukov * notice, this list of conditions and the following disclaimer in the 13d359a62dSAndrey V. Elsukov * documentation and/or other materials provided with the distribution. 14d359a62dSAndrey V. Elsukov * 15d359a62dSAndrey V. Elsukov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16d359a62dSAndrey V. Elsukov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17d359a62dSAndrey V. Elsukov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18d359a62dSAndrey V. Elsukov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19d359a62dSAndrey V. Elsukov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20d359a62dSAndrey V. Elsukov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21d359a62dSAndrey V. Elsukov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22d359a62dSAndrey V. Elsukov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23d359a62dSAndrey V. Elsukov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24d359a62dSAndrey V. Elsukov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25d359a62dSAndrey V. Elsukov * SUCH DAMAGE. 26d359a62dSAndrey V. Elsukov * 27d359a62dSAndrey V. Elsukov */ 28d359a62dSAndrey V. Elsukov 29d359a62dSAndrey V. Elsukov #include <sys/cdefs.h> 30d359a62dSAndrey V. Elsukov __FBSDID("$FreeBSD$"); 31d359a62dSAndrey V. Elsukov 32d359a62dSAndrey V. Elsukov #include <sys/param.h> 33cac2fe69SAndrey V. Elsukov #include <sys/systm.h> 34d359a62dSAndrey V. Elsukov #include <sys/kernel.h> 35cac2fe69SAndrey V. Elsukov #include <sys/endian.h> 366c0a2eb1SAndrey V. Elsukov #include <sys/malloc.h> 376c0a2eb1SAndrey V. Elsukov #include <sys/mbuf.h> 38*426b3d04SJulian Elischer 39*426b3d04SJulian Elischer #include <net/bpf.h> 40*426b3d04SJulian Elischer #include <net/ethernet.h> 41*426b3d04SJulian Elischer 42d359a62dSAndrey V. Elsukov #include <netgraph/ng_message.h> 43d359a62dSAndrey V. Elsukov #include <netgraph/ng_parse.h> 44d359a62dSAndrey V. Elsukov #include <netgraph/netgraph.h> 45d359a62dSAndrey V. Elsukov 46*426b3d04SJulian Elischer #include <netgraph/ng_patch.h> 47*426b3d04SJulian Elischer 48*426b3d04SJulian Elischer /* private data */ 49*426b3d04SJulian Elischer struct ng_patch_priv { 50*426b3d04SJulian Elischer hook_p in; 51*426b3d04SJulian Elischer hook_p out; 52*426b3d04SJulian Elischer uint8_t dlt; /* DLT_XXX from bpf.h */ 53*426b3d04SJulian Elischer struct ng_patch_stats stats; 54*426b3d04SJulian Elischer struct ng_patch_config *conf; 55*426b3d04SJulian Elischer }; 56*426b3d04SJulian Elischer 57*426b3d04SJulian Elischer typedef struct ng_patch_priv *priv_p; 58*426b3d04SJulian Elischer 59*426b3d04SJulian Elischer /* Netgraph methods */ 60d359a62dSAndrey V. Elsukov static ng_constructor_t ng_patch_constructor; 61d359a62dSAndrey V. Elsukov static ng_rcvmsg_t ng_patch_rcvmsg; 62d359a62dSAndrey V. Elsukov static ng_shutdown_t ng_patch_shutdown; 63d359a62dSAndrey V. Elsukov static ng_newhook_t ng_patch_newhook; 64d359a62dSAndrey V. Elsukov static ng_rcvdata_t ng_patch_rcvdata; 65d359a62dSAndrey V. Elsukov static ng_disconnect_t ng_patch_disconnect; 66d359a62dSAndrey V. Elsukov 67*426b3d04SJulian Elischer #define ERROUT(x) { error = (x); goto done; } 68*426b3d04SJulian Elischer 69d359a62dSAndrey V. Elsukov static int 706c0a2eb1SAndrey V. Elsukov ng_patch_config_getlen(const struct ng_parse_type *type, 716c0a2eb1SAndrey V. Elsukov const u_char *start, const u_char *buf) 72d359a62dSAndrey V. Elsukov { 73bf909fc9SJulian Elischer const struct ng_patch_config *conf; 74d359a62dSAndrey V. Elsukov 75bf909fc9SJulian Elischer conf = (const struct ng_patch_config *)(buf - 76cac2fe69SAndrey V. Elsukov offsetof(struct ng_patch_config, ops)); 77bf909fc9SJulian Elischer 78bf909fc9SJulian Elischer return (conf->count); 79d359a62dSAndrey V. Elsukov } 80d359a62dSAndrey V. Elsukov 81d359a62dSAndrey V. Elsukov static const struct ng_parse_struct_field ng_patch_op_type_fields[] 82*426b3d04SJulian Elischer = NG_PATCH_OP_TYPE; 83d359a62dSAndrey V. Elsukov static const struct ng_parse_type ng_patch_op_type = { 84d359a62dSAndrey V. Elsukov &ng_parse_struct_type, 85d359a62dSAndrey V. Elsukov &ng_patch_op_type_fields 86d359a62dSAndrey V. Elsukov }; 87d359a62dSAndrey V. Elsukov 88bf909fc9SJulian Elischer static const struct ng_parse_array_info ng_patch_ops_array_info = { 89d359a62dSAndrey V. Elsukov &ng_patch_op_type, 90d359a62dSAndrey V. Elsukov &ng_patch_config_getlen 91d359a62dSAndrey V. Elsukov }; 92bf909fc9SJulian Elischer static const struct ng_parse_type ng_patch_ops_array_type = { 93d359a62dSAndrey V. Elsukov &ng_parse_array_type, 94bf909fc9SJulian Elischer &ng_patch_ops_array_info 95d359a62dSAndrey V. Elsukov }; 96d359a62dSAndrey V. Elsukov 97d359a62dSAndrey V. Elsukov static const struct ng_parse_struct_field ng_patch_config_type_fields[] 98*426b3d04SJulian Elischer = NG_PATCH_CONFIG_TYPE; 99d359a62dSAndrey V. Elsukov static const struct ng_parse_type ng_patch_config_type = { 100d359a62dSAndrey V. Elsukov &ng_parse_struct_type, 101d359a62dSAndrey V. Elsukov &ng_patch_config_type_fields 102d359a62dSAndrey V. Elsukov }; 103d359a62dSAndrey V. Elsukov 104d359a62dSAndrey V. Elsukov static const struct ng_parse_struct_field ng_patch_stats_fields[] 105*426b3d04SJulian Elischer = NG_PATCH_STATS_TYPE; 106d359a62dSAndrey V. Elsukov static const struct ng_parse_type ng_patch_stats_type = { 107d359a62dSAndrey V. Elsukov &ng_parse_struct_type, 108d359a62dSAndrey V. Elsukov &ng_patch_stats_fields 109d359a62dSAndrey V. Elsukov }; 110d359a62dSAndrey V. Elsukov 111d359a62dSAndrey V. Elsukov static const struct ng_cmdlist ng_patch_cmdlist[] = { 112d359a62dSAndrey V. Elsukov { 113d359a62dSAndrey V. Elsukov NGM_PATCH_COOKIE, 114*426b3d04SJulian Elischer NGM_PATCH_GETDLT, 115*426b3d04SJulian Elischer "getdlt", 116*426b3d04SJulian Elischer NULL, 117*426b3d04SJulian Elischer &ng_parse_uint8_type 118*426b3d04SJulian Elischer }, 119*426b3d04SJulian Elischer { 120*426b3d04SJulian Elischer NGM_PATCH_COOKIE, 121*426b3d04SJulian Elischer NGM_PATCH_SETDLT, 122*426b3d04SJulian Elischer "setdlt", 123*426b3d04SJulian Elischer &ng_parse_uint8_type, 124*426b3d04SJulian Elischer NULL 125*426b3d04SJulian Elischer }, 126*426b3d04SJulian Elischer { 127*426b3d04SJulian Elischer NGM_PATCH_COOKIE, 128d359a62dSAndrey V. Elsukov NGM_PATCH_GETCONFIG, 129d359a62dSAndrey V. Elsukov "getconfig", 130d359a62dSAndrey V. Elsukov NULL, 131d359a62dSAndrey V. Elsukov &ng_patch_config_type 132d359a62dSAndrey V. Elsukov }, 133d359a62dSAndrey V. Elsukov { 134d359a62dSAndrey V. Elsukov NGM_PATCH_COOKIE, 135d359a62dSAndrey V. Elsukov NGM_PATCH_SETCONFIG, 136d359a62dSAndrey V. Elsukov "setconfig", 137d359a62dSAndrey V. Elsukov &ng_patch_config_type, 138d359a62dSAndrey V. Elsukov NULL 139d359a62dSAndrey V. Elsukov }, 140d359a62dSAndrey V. Elsukov { 141d359a62dSAndrey V. Elsukov NGM_PATCH_COOKIE, 142d359a62dSAndrey V. Elsukov NGM_PATCH_GET_STATS, 143d359a62dSAndrey V. Elsukov "getstats", 144d359a62dSAndrey V. Elsukov NULL, 145d359a62dSAndrey V. Elsukov &ng_patch_stats_type 146d359a62dSAndrey V. Elsukov }, 147d359a62dSAndrey V. Elsukov { 148d359a62dSAndrey V. Elsukov NGM_PATCH_COOKIE, 149d359a62dSAndrey V. Elsukov NGM_PATCH_CLR_STATS, 150d359a62dSAndrey V. Elsukov "clrstats", 151d359a62dSAndrey V. Elsukov NULL, 152d359a62dSAndrey V. Elsukov NULL 153d359a62dSAndrey V. Elsukov }, 154d359a62dSAndrey V. Elsukov { 155d359a62dSAndrey V. Elsukov NGM_PATCH_COOKIE, 156d359a62dSAndrey V. Elsukov NGM_PATCH_GETCLR_STATS, 157d359a62dSAndrey V. Elsukov "getclrstats", 158d359a62dSAndrey V. Elsukov NULL, 159d359a62dSAndrey V. Elsukov &ng_patch_stats_type 160d359a62dSAndrey V. Elsukov }, 161d359a62dSAndrey V. Elsukov { 0 } 162d359a62dSAndrey V. Elsukov }; 163d359a62dSAndrey V. Elsukov 164d359a62dSAndrey V. Elsukov static struct ng_type typestruct = { 165d359a62dSAndrey V. Elsukov .version = NG_ABI_VERSION, 166d359a62dSAndrey V. Elsukov .name = NG_PATCH_NODE_TYPE, 167d359a62dSAndrey V. Elsukov .constructor = ng_patch_constructor, 168d359a62dSAndrey V. Elsukov .rcvmsg = ng_patch_rcvmsg, 169d359a62dSAndrey V. Elsukov .shutdown = ng_patch_shutdown, 170d359a62dSAndrey V. Elsukov .newhook = ng_patch_newhook, 171d359a62dSAndrey V. Elsukov .rcvdata = ng_patch_rcvdata, 172d359a62dSAndrey V. Elsukov .disconnect = ng_patch_disconnect, 173d359a62dSAndrey V. Elsukov .cmdlist = ng_patch_cmdlist, 174d359a62dSAndrey V. Elsukov }; 175bf909fc9SJulian Elischer 176d359a62dSAndrey V. Elsukov NETGRAPH_INIT(patch, &typestruct); 177d359a62dSAndrey V. Elsukov 178d359a62dSAndrey V. Elsukov static int 179d359a62dSAndrey V. Elsukov ng_patch_constructor(node_p node) 180d359a62dSAndrey V. Elsukov { 181d359a62dSAndrey V. Elsukov priv_p privdata; 182d359a62dSAndrey V. Elsukov 183ffbfc0aaSAndrey V. Elsukov privdata = malloc(sizeof(*privdata), M_NETGRAPH, M_WAITOK | M_ZERO); 184*426b3d04SJulian Elischer privdata->dlt = DLT_RAW; 185*426b3d04SJulian Elischer 186d359a62dSAndrey V. Elsukov NG_NODE_SET_PRIVATE(node, privdata); 187*426b3d04SJulian Elischer 188d359a62dSAndrey V. Elsukov return (0); 189d359a62dSAndrey V. Elsukov } 190d359a62dSAndrey V. Elsukov 191d359a62dSAndrey V. Elsukov static int 192d359a62dSAndrey V. Elsukov ng_patch_newhook(node_p node, hook_p hook, const char *name) 193d359a62dSAndrey V. Elsukov { 194d359a62dSAndrey V. Elsukov const priv_p privp = NG_NODE_PRIVATE(node); 195d359a62dSAndrey V. Elsukov 196d359a62dSAndrey V. Elsukov if (strncmp(name, NG_PATCH_HOOK_IN, strlen(NG_PATCH_HOOK_IN)) == 0) { 197d359a62dSAndrey V. Elsukov privp->in = hook; 198d359a62dSAndrey V. Elsukov } else if (strncmp(name, NG_PATCH_HOOK_OUT, 199d359a62dSAndrey V. Elsukov strlen(NG_PATCH_HOOK_OUT)) == 0) { 200d359a62dSAndrey V. Elsukov privp->out = hook; 201d359a62dSAndrey V. Elsukov } else 202d359a62dSAndrey V. Elsukov return (EINVAL); 203*426b3d04SJulian Elischer 204d359a62dSAndrey V. Elsukov return (0); 205d359a62dSAndrey V. Elsukov } 206d359a62dSAndrey V. Elsukov 207d359a62dSAndrey V. Elsukov static int 208d359a62dSAndrey V. Elsukov ng_patch_rcvmsg(node_p node, item_p item, hook_p lasthook) 209d359a62dSAndrey V. Elsukov { 210d359a62dSAndrey V. Elsukov const priv_p privp = NG_NODE_PRIVATE(node); 2116c0a2eb1SAndrey V. Elsukov struct ng_patch_config *conf, *newconf; 212d359a62dSAndrey V. Elsukov struct ng_mesg *msg; 213*426b3d04SJulian Elischer struct ng_mesg *resp = NULL; 214*426b3d04SJulian Elischer int i, error = 0; 215d359a62dSAndrey V. Elsukov 216d359a62dSAndrey V. Elsukov NGI_GET_MSG(item, msg); 217*426b3d04SJulian Elischer 218*426b3d04SJulian Elischer if (msg->header.typecookie != NGM_PATCH_COOKIE) 219*426b3d04SJulian Elischer ERROUT(EINVAL); 220*426b3d04SJulian Elischer 221*426b3d04SJulian Elischer switch (msg->header.cmd) 222d359a62dSAndrey V. Elsukov { 223*426b3d04SJulian Elischer case NGM_PATCH_GETCONFIG: 224*426b3d04SJulian Elischer if (privp->conf == NULL) 225*426b3d04SJulian Elischer ERROUT(0); 226*426b3d04SJulian Elischer 227*426b3d04SJulian Elischer NG_MKRESPONSE(resp, msg, 228*426b3d04SJulian Elischer NG_PATCH_CONF_SIZE(privp->conf->count), M_WAITOK); 229*426b3d04SJulian Elischer 230*426b3d04SJulian Elischer if (resp == NULL) 231*426b3d04SJulian Elischer ERROUT(ENOMEM); 232*426b3d04SJulian Elischer 233*426b3d04SJulian Elischer bcopy(privp->conf, resp->data, 234*426b3d04SJulian Elischer NG_PATCH_CONF_SIZE(privp->conf->count)); 235*426b3d04SJulian Elischer 236*426b3d04SJulian Elischer conf = (struct ng_patch_config *) resp->data; 237*426b3d04SJulian Elischer 238*426b3d04SJulian Elischer for (i = 0; i < conf->count; i++) { 239*426b3d04SJulian Elischer switch (conf->ops[i].length) 240*426b3d04SJulian Elischer { 241*426b3d04SJulian Elischer case 1: 242*426b3d04SJulian Elischer conf->ops[i].val.v8 = conf->ops[i].val.v1; 243*426b3d04SJulian Elischer break; 244*426b3d04SJulian Elischer case 2: 245*426b3d04SJulian Elischer conf->ops[i].val.v8 = conf->ops[i].val.v2; 246*426b3d04SJulian Elischer break; 247*426b3d04SJulian Elischer case 4: 248*426b3d04SJulian Elischer conf->ops[i].val.v8 = conf->ops[i].val.v4; 249*426b3d04SJulian Elischer break; 250*426b3d04SJulian Elischer case 8: 251d359a62dSAndrey V. Elsukov break; 252d359a62dSAndrey V. Elsukov } 253*426b3d04SJulian Elischer } 254d359a62dSAndrey V. Elsukov 255*426b3d04SJulian Elischer break; 256*426b3d04SJulian Elischer 257*426b3d04SJulian Elischer case NGM_PATCH_SETCONFIG: 258d359a62dSAndrey V. Elsukov conf = (struct ng_patch_config *) msg->data; 259*426b3d04SJulian Elischer 260*426b3d04SJulian Elischer if (msg->header.arglen < sizeof(struct ng_patch_config) || 261*426b3d04SJulian Elischer msg->header.arglen < NG_PATCH_CONF_SIZE(conf->count)) 262*426b3d04SJulian Elischer ERROUT(EINVAL); 263d359a62dSAndrey V. Elsukov 264d359a62dSAndrey V. Elsukov for (i = 0; i < conf->count; i++) { 265*426b3d04SJulian Elischer switch (conf->ops[i].length) 266*426b3d04SJulian Elischer { 267d359a62dSAndrey V. Elsukov case 1: 268*426b3d04SJulian Elischer conf->ops[i].val.v1 = (uint8_t) conf->ops[i].val.v8; 269*426b3d04SJulian Elischer break; 270d359a62dSAndrey V. Elsukov case 2: 271*426b3d04SJulian Elischer conf->ops[i].val.v2 = (uint16_t) conf->ops[i].val.v8; 272*426b3d04SJulian Elischer break; 273d359a62dSAndrey V. Elsukov case 4: 274*426b3d04SJulian Elischer conf->ops[i].val.v4 = (uint32_t) conf->ops[i].val.v8; 275*426b3d04SJulian Elischer break; 276d359a62dSAndrey V. Elsukov case 8: 277d359a62dSAndrey V. Elsukov break; 278d359a62dSAndrey V. Elsukov default: 279*426b3d04SJulian Elischer ERROUT(EINVAL); 280d359a62dSAndrey V. Elsukov } 281d359a62dSAndrey V. Elsukov } 282d359a62dSAndrey V. Elsukov 283*426b3d04SJulian Elischer conf->csum_flags &= NG_PATCH_CSUM_IPV4|NG_PATCH_CSUM_IPV6; 284*426b3d04SJulian Elischer conf->relative_offset = !!conf->relative_offset; 285d359a62dSAndrey V. Elsukov 286*426b3d04SJulian Elischer newconf = malloc(NG_PATCH_CONF_SIZE(conf->count), M_NETGRAPH, M_WAITOK | M_ZERO); 287*426b3d04SJulian Elischer 288*426b3d04SJulian Elischer bcopy(conf, newconf, NG_PATCH_CONF_SIZE(conf->count)); 289*426b3d04SJulian Elischer 290*426b3d04SJulian Elischer if (privp->conf) 291*426b3d04SJulian Elischer free(privp->conf, M_NETGRAPH); 292*426b3d04SJulian Elischer 293*426b3d04SJulian Elischer privp->conf = newconf; 294*426b3d04SJulian Elischer 295d359a62dSAndrey V. Elsukov break; 296*426b3d04SJulian Elischer 297d359a62dSAndrey V. Elsukov case NGM_PATCH_GET_STATS: 298d359a62dSAndrey V. Elsukov case NGM_PATCH_CLR_STATS: 299*426b3d04SJulian Elischer case NGM_PATCH_GETCLR_STATS: 300*426b3d04SJulian Elischer if (msg->header.cmd != NGM_PATCH_CLR_STATS) { 301*426b3d04SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(struct ng_patch_stats), M_WAITOK); 302*426b3d04SJulian Elischer 303*426b3d04SJulian Elischer if (resp == NULL) 304*426b3d04SJulian Elischer ERROUT(ENOMEM); 305*426b3d04SJulian Elischer 306*426b3d04SJulian Elischer bcopy(&(privp->stats), resp->data, sizeof(struct ng_patch_stats)); 307d359a62dSAndrey V. Elsukov } 308d359a62dSAndrey V. Elsukov 309*426b3d04SJulian Elischer if (msg->header.cmd != NGM_PATCH_GET_STATS) 310*426b3d04SJulian Elischer bzero(&(privp->stats), sizeof(struct ng_patch_stats)); 311*426b3d04SJulian Elischer 312*426b3d04SJulian Elischer break; 313*426b3d04SJulian Elischer 314*426b3d04SJulian Elischer case NGM_PATCH_GETDLT: 315*426b3d04SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(uint8_t), M_WAITOK); 316*426b3d04SJulian Elischer 317*426b3d04SJulian Elischer if (resp == NULL) 318*426b3d04SJulian Elischer ERROUT(ENOMEM); 319*426b3d04SJulian Elischer 320*426b3d04SJulian Elischer *((uint8_t *) resp->data) = privp->dlt; 321*426b3d04SJulian Elischer 322*426b3d04SJulian Elischer break; 323*426b3d04SJulian Elischer 324*426b3d04SJulian Elischer case NGM_PATCH_SETDLT: 325*426b3d04SJulian Elischer if (msg->header.arglen != sizeof(uint8_t)) 326*426b3d04SJulian Elischer ERROUT(EINVAL); 327*426b3d04SJulian Elischer 328*426b3d04SJulian Elischer switch (*(uint8_t *) msg->data) 329*426b3d04SJulian Elischer { 330*426b3d04SJulian Elischer case DLT_EN10MB: 331*426b3d04SJulian Elischer case DLT_RAW: 332*426b3d04SJulian Elischer privp->dlt = *(uint8_t *) msg->data; 333*426b3d04SJulian Elischer break; 334*426b3d04SJulian Elischer 335*426b3d04SJulian Elischer default: 336*426b3d04SJulian Elischer ERROUT(EINVAL); 337*426b3d04SJulian Elischer } 338*426b3d04SJulian Elischer 339*426b3d04SJulian Elischer break; 340*426b3d04SJulian Elischer 341*426b3d04SJulian Elischer default: 342*426b3d04SJulian Elischer ERROUT(EINVAL); 343*426b3d04SJulian Elischer } 344*426b3d04SJulian Elischer 345*426b3d04SJulian Elischer done: 346d359a62dSAndrey V. Elsukov NG_RESPOND_MSG(error, node, item, resp); 347d359a62dSAndrey V. Elsukov NG_FREE_MSG(msg); 348*426b3d04SJulian Elischer 349d359a62dSAndrey V. Elsukov return (error); 350d359a62dSAndrey V. Elsukov } 351d359a62dSAndrey V. Elsukov 352d359a62dSAndrey V. Elsukov static void 353*426b3d04SJulian Elischer do_patch(priv_p privp, struct mbuf *m, int global_offset) 354d359a62dSAndrey V. Elsukov { 355*426b3d04SJulian Elischer int i, offset, patched = 0; 356*426b3d04SJulian Elischer union ng_patch_op_val val; 357d359a62dSAndrey V. Elsukov 358*426b3d04SJulian Elischer for (i = 0; i < privp->conf->count; i++) { 359*426b3d04SJulian Elischer offset = global_offset + privp->conf->ops[i].offset; 360*426b3d04SJulian Elischer 361*426b3d04SJulian Elischer if (offset + privp->conf->ops[i].length > m->m_pkthdr.len) 362d359a62dSAndrey V. Elsukov continue; 363d359a62dSAndrey V. Elsukov 364d359a62dSAndrey V. Elsukov /* for "=" operation we don't need to copy data from mbuf */ 365*426b3d04SJulian Elischer if (privp->conf->ops[i].mode != NG_PATCH_MODE_SET) 366*426b3d04SJulian Elischer m_copydata(m, offset, privp->conf->ops[i].length, (caddr_t) &val); 367d359a62dSAndrey V. Elsukov 368*426b3d04SJulian Elischer switch (privp->conf->ops[i].length) 369*426b3d04SJulian Elischer { 370d359a62dSAndrey V. Elsukov case 1: 371*426b3d04SJulian Elischer switch (privp->conf->ops[i].mode) 372*426b3d04SJulian Elischer { 373d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SET: 374*426b3d04SJulian Elischer val.v1 = privp->conf->ops[i].val.v1; 375d359a62dSAndrey V. Elsukov break; 376d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_ADD: 377*426b3d04SJulian Elischer val.v1 += privp->conf->ops[i].val.v1; 378d359a62dSAndrey V. Elsukov break; 379d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SUB: 380*426b3d04SJulian Elischer val.v1 -= privp->conf->ops[i].val.v1; 381d359a62dSAndrey V. Elsukov break; 382d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_MUL: 383*426b3d04SJulian Elischer val.v1 *= privp->conf->ops[i].val.v1; 384d359a62dSAndrey V. Elsukov break; 385d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_DIV: 386*426b3d04SJulian Elischer val.v1 /= privp->conf->ops[i].val.v1; 387d359a62dSAndrey V. Elsukov break; 388d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_NEG: 389*426b3d04SJulian Elischer *((int8_t *) &val) = - *((int8_t *) &val); 390d359a62dSAndrey V. Elsukov break; 391d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_AND: 392*426b3d04SJulian Elischer val.v1 &= privp->conf->ops[i].val.v1; 393d359a62dSAndrey V. Elsukov break; 394d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_OR: 395*426b3d04SJulian Elischer val.v1 |= privp->conf->ops[i].val.v1; 396d359a62dSAndrey V. Elsukov break; 397d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_XOR: 398*426b3d04SJulian Elischer val.v1 ^= privp->conf->ops[i].val.v1; 399d359a62dSAndrey V. Elsukov break; 400d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SHL: 401*426b3d04SJulian Elischer val.v1 <<= privp->conf->ops[i].val.v1; 402d359a62dSAndrey V. Elsukov break; 403d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SHR: 404*426b3d04SJulian Elischer val.v1 >>= privp->conf->ops[i].val.v1; 405d359a62dSAndrey V. Elsukov break; 406d359a62dSAndrey V. Elsukov } 407d359a62dSAndrey V. Elsukov break; 408*426b3d04SJulian Elischer 409d359a62dSAndrey V. Elsukov case 2: 410*426b3d04SJulian Elischer val.v2 = ntohs(val.v2); 411*426b3d04SJulian Elischer 412*426b3d04SJulian Elischer switch (privp->conf->ops[i].mode) 413*426b3d04SJulian Elischer { 414d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SET: 415*426b3d04SJulian Elischer val.v2 = privp->conf->ops[i].val.v2; 416d359a62dSAndrey V. Elsukov break; 417d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_ADD: 418*426b3d04SJulian Elischer val.v2 += privp->conf->ops[i].val.v2; 419d359a62dSAndrey V. Elsukov break; 420d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SUB: 421*426b3d04SJulian Elischer val.v2 -= privp->conf->ops[i].val.v2; 422d359a62dSAndrey V. Elsukov break; 423d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_MUL: 424*426b3d04SJulian Elischer val.v2 *= privp->conf->ops[i].val.v2; 425d359a62dSAndrey V. Elsukov break; 426d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_DIV: 427*426b3d04SJulian Elischer val.v2 /= privp->conf->ops[i].val.v2; 428d359a62dSAndrey V. Elsukov break; 429d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_NEG: 430*426b3d04SJulian Elischer *((int16_t *) &val) = - *((int16_t *) &val); 431d359a62dSAndrey V. Elsukov break; 432d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_AND: 433*426b3d04SJulian Elischer val.v2 &= privp->conf->ops[i].val.v2; 434d359a62dSAndrey V. Elsukov break; 435d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_OR: 436*426b3d04SJulian Elischer val.v2 |= privp->conf->ops[i].val.v2; 437d359a62dSAndrey V. Elsukov break; 438d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_XOR: 439*426b3d04SJulian Elischer val.v2 ^= privp->conf->ops[i].val.v2; 440d359a62dSAndrey V. Elsukov break; 441d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SHL: 442*426b3d04SJulian Elischer val.v2 <<= privp->conf->ops[i].val.v2; 443d359a62dSAndrey V. Elsukov break; 444d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SHR: 445*426b3d04SJulian Elischer val.v2 >>= privp->conf->ops[i].val.v2; 446d359a62dSAndrey V. Elsukov break; 447d359a62dSAndrey V. Elsukov } 448d359a62dSAndrey V. Elsukov 449*426b3d04SJulian Elischer val.v2 = htons(val.v2); 450*426b3d04SJulian Elischer 451*426b3d04SJulian Elischer break; 452*426b3d04SJulian Elischer 453*426b3d04SJulian Elischer case 4: 454*426b3d04SJulian Elischer val.v4 = ntohl(val.v4); 455*426b3d04SJulian Elischer 456*426b3d04SJulian Elischer switch (privp->conf->ops[i].mode) 457*426b3d04SJulian Elischer { 458*426b3d04SJulian Elischer case NG_PATCH_MODE_SET: 459*426b3d04SJulian Elischer val.v4 = privp->conf->ops[i].val.v4; 460*426b3d04SJulian Elischer break; 461*426b3d04SJulian Elischer case NG_PATCH_MODE_ADD: 462*426b3d04SJulian Elischer val.v4 += privp->conf->ops[i].val.v4; 463*426b3d04SJulian Elischer break; 464*426b3d04SJulian Elischer case NG_PATCH_MODE_SUB: 465*426b3d04SJulian Elischer val.v4 -= privp->conf->ops[i].val.v4; 466*426b3d04SJulian Elischer break; 467*426b3d04SJulian Elischer case NG_PATCH_MODE_MUL: 468*426b3d04SJulian Elischer val.v4 *= privp->conf->ops[i].val.v4; 469*426b3d04SJulian Elischer break; 470*426b3d04SJulian Elischer case NG_PATCH_MODE_DIV: 471*426b3d04SJulian Elischer val.v4 /= privp->conf->ops[i].val.v4; 472*426b3d04SJulian Elischer break; 473*426b3d04SJulian Elischer case NG_PATCH_MODE_NEG: 474*426b3d04SJulian Elischer *((int32_t *) &val) = - *((int32_t *) &val); 475*426b3d04SJulian Elischer break; 476*426b3d04SJulian Elischer case NG_PATCH_MODE_AND: 477*426b3d04SJulian Elischer val.v4 &= privp->conf->ops[i].val.v4; 478*426b3d04SJulian Elischer break; 479*426b3d04SJulian Elischer case NG_PATCH_MODE_OR: 480*426b3d04SJulian Elischer val.v4 |= privp->conf->ops[i].val.v4; 481*426b3d04SJulian Elischer break; 482*426b3d04SJulian Elischer case NG_PATCH_MODE_XOR: 483*426b3d04SJulian Elischer val.v4 ^= privp->conf->ops[i].val.v4; 484*426b3d04SJulian Elischer break; 485*426b3d04SJulian Elischer case NG_PATCH_MODE_SHL: 486*426b3d04SJulian Elischer val.v4 <<= privp->conf->ops[i].val.v4; 487*426b3d04SJulian Elischer break; 488*426b3d04SJulian Elischer case NG_PATCH_MODE_SHR: 489*426b3d04SJulian Elischer val.v4 >>= privp->conf->ops[i].val.v4; 490*426b3d04SJulian Elischer break; 491*426b3d04SJulian Elischer } 492*426b3d04SJulian Elischer 493*426b3d04SJulian Elischer val.v4 = htonl(val.v4); 494*426b3d04SJulian Elischer 495*426b3d04SJulian Elischer break; 496*426b3d04SJulian Elischer 497*426b3d04SJulian Elischer case 8: 498*426b3d04SJulian Elischer val.v8 = be64toh(val.v8); 499*426b3d04SJulian Elischer 500*426b3d04SJulian Elischer switch (privp->conf->ops[i].mode) 501*426b3d04SJulian Elischer { 502*426b3d04SJulian Elischer case NG_PATCH_MODE_SET: 503*426b3d04SJulian Elischer val.v8 = privp->conf->ops[i].val.v8; 504*426b3d04SJulian Elischer break; 505*426b3d04SJulian Elischer case NG_PATCH_MODE_ADD: 506*426b3d04SJulian Elischer val.v8 += privp->conf->ops[i].val.v8; 507*426b3d04SJulian Elischer break; 508*426b3d04SJulian Elischer case NG_PATCH_MODE_SUB: 509*426b3d04SJulian Elischer val.v8 -= privp->conf->ops[i].val.v8; 510*426b3d04SJulian Elischer break; 511*426b3d04SJulian Elischer case NG_PATCH_MODE_MUL: 512*426b3d04SJulian Elischer val.v8 *= privp->conf->ops[i].val.v8; 513*426b3d04SJulian Elischer break; 514*426b3d04SJulian Elischer case NG_PATCH_MODE_DIV: 515*426b3d04SJulian Elischer val.v8 /= privp->conf->ops[i].val.v8; 516*426b3d04SJulian Elischer break; 517*426b3d04SJulian Elischer case NG_PATCH_MODE_NEG: 518*426b3d04SJulian Elischer *((int64_t *) &val) = - *((int64_t *) &val); 519*426b3d04SJulian Elischer break; 520*426b3d04SJulian Elischer case NG_PATCH_MODE_AND: 521*426b3d04SJulian Elischer val.v8 &= privp->conf->ops[i].val.v8; 522*426b3d04SJulian Elischer break; 523*426b3d04SJulian Elischer case NG_PATCH_MODE_OR: 524*426b3d04SJulian Elischer val.v8 |= privp->conf->ops[i].val.v8; 525*426b3d04SJulian Elischer break; 526*426b3d04SJulian Elischer case NG_PATCH_MODE_XOR: 527*426b3d04SJulian Elischer val.v8 ^= privp->conf->ops[i].val.v8; 528*426b3d04SJulian Elischer break; 529*426b3d04SJulian Elischer case NG_PATCH_MODE_SHL: 530*426b3d04SJulian Elischer val.v8 <<= privp->conf->ops[i].val.v8; 531*426b3d04SJulian Elischer break; 532*426b3d04SJulian Elischer case NG_PATCH_MODE_SHR: 533*426b3d04SJulian Elischer val.v8 >>= privp->conf->ops[i].val.v8; 534*426b3d04SJulian Elischer break; 535*426b3d04SJulian Elischer } 536*426b3d04SJulian Elischer 537*426b3d04SJulian Elischer val.v8 = htobe64(val.v8); 538*426b3d04SJulian Elischer 539*426b3d04SJulian Elischer break; 540*426b3d04SJulian Elischer } 541*426b3d04SJulian Elischer 542*426b3d04SJulian Elischer m_copyback(m, offset, privp->conf->ops[i].length, (caddr_t) &val); 543d359a62dSAndrey V. Elsukov patched = 1; 544d359a62dSAndrey V. Elsukov } 545*426b3d04SJulian Elischer 546*426b3d04SJulian Elischer if (patched) 547d359a62dSAndrey V. Elsukov privp->stats.patched++; 548d359a62dSAndrey V. Elsukov } 549d359a62dSAndrey V. Elsukov 550d359a62dSAndrey V. Elsukov static int 551d359a62dSAndrey V. Elsukov ng_patch_rcvdata(hook_p hook, item_p item) 552d359a62dSAndrey V. Elsukov { 553d359a62dSAndrey V. Elsukov const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 554d359a62dSAndrey V. Elsukov struct mbuf *m; 555*426b3d04SJulian Elischer hook_p out; 556*426b3d04SJulian Elischer int pullup_len = 0; 557*426b3d04SJulian Elischer int error = 0; 558d359a62dSAndrey V. Elsukov 559d359a62dSAndrey V. Elsukov priv->stats.received++; 560*426b3d04SJulian Elischer 561d359a62dSAndrey V. Elsukov NGI_GET_M(item, m); 562*426b3d04SJulian Elischer 563*426b3d04SJulian Elischer #define PULLUP_CHECK(mbuf, length) do { \ 564*426b3d04SJulian Elischer pullup_len += length; \ 565*426b3d04SJulian Elischer if (((mbuf)->m_pkthdr.len < pullup_len) || \ 566*426b3d04SJulian Elischer (pullup_len > MHLEN)) { \ 567*426b3d04SJulian Elischer error = EINVAL; \ 568*426b3d04SJulian Elischer goto bypass; \ 569*426b3d04SJulian Elischer } \ 570*426b3d04SJulian Elischer if ((mbuf)->m_len < pullup_len && \ 571*426b3d04SJulian Elischer (((mbuf) = m_pullup((mbuf), pullup_len)) == NULL)) { \ 572*426b3d04SJulian Elischer error = ENOBUFS; \ 573*426b3d04SJulian Elischer goto drop; \ 574*426b3d04SJulian Elischer } \ 575*426b3d04SJulian Elischer } while (0) 576*426b3d04SJulian Elischer 577*426b3d04SJulian Elischer if (priv->conf && hook == priv->in && 578*426b3d04SJulian Elischer m && (m->m_flags & M_PKTHDR)) { 579*426b3d04SJulian Elischer 580d359a62dSAndrey V. Elsukov m = m_unshare(m, M_NOWAIT); 581*426b3d04SJulian Elischer 582*426b3d04SJulian Elischer if (m == NULL) 583*426b3d04SJulian Elischer ERROUT(ENOMEM); 584*426b3d04SJulian Elischer 585*426b3d04SJulian Elischer if (priv->conf->relative_offset) { 586*426b3d04SJulian Elischer struct ether_header *eh; 587*426b3d04SJulian Elischer struct ng_patch_vlan_header *vh; 588*426b3d04SJulian Elischer uint16_t etype; 589*426b3d04SJulian Elischer 590*426b3d04SJulian Elischer switch (priv->dlt) 591*426b3d04SJulian Elischer { 592*426b3d04SJulian Elischer case DLT_EN10MB: 593*426b3d04SJulian Elischer PULLUP_CHECK(m, sizeof(struct ether_header)); 594*426b3d04SJulian Elischer eh = mtod(m, struct ether_header *); 595*426b3d04SJulian Elischer etype = ntohs(eh->ether_type); 596*426b3d04SJulian Elischer 597*426b3d04SJulian Elischer for (;;) { /* QinQ support */ 598*426b3d04SJulian Elischer switch (etype) 599*426b3d04SJulian Elischer { 600*426b3d04SJulian Elischer case 0x8100: 601*426b3d04SJulian Elischer case 0x88A8: 602*426b3d04SJulian Elischer case 0x9100: 603*426b3d04SJulian Elischer PULLUP_CHECK(m, sizeof(struct ng_patch_vlan_header)); 604*426b3d04SJulian Elischer vh = (struct ng_patch_vlan_header *) mtodo(m, 605*426b3d04SJulian Elischer pullup_len - sizeof(struct ng_patch_vlan_header)); 606*426b3d04SJulian Elischer etype = ntohs(vh->etype); 607*426b3d04SJulian Elischer break; 608*426b3d04SJulian Elischer 609*426b3d04SJulian Elischer default: 610*426b3d04SJulian Elischer goto loopend; 611d359a62dSAndrey V. Elsukov } 612*426b3d04SJulian Elischer } 613*426b3d04SJulian Elischer loopend: 614*426b3d04SJulian Elischer break; 615*426b3d04SJulian Elischer 616*426b3d04SJulian Elischer case DLT_RAW: 617*426b3d04SJulian Elischer break; 618*426b3d04SJulian Elischer 619*426b3d04SJulian Elischer default: 620*426b3d04SJulian Elischer ERROUT(EINVAL); 621*426b3d04SJulian Elischer } 622d359a62dSAndrey V. Elsukov } 623d359a62dSAndrey V. Elsukov 624*426b3d04SJulian Elischer do_patch(priv, m, pullup_len); 625*426b3d04SJulian Elischer 626*426b3d04SJulian Elischer m->m_pkthdr.csum_flags |= priv->conf->csum_flags; 627*426b3d04SJulian Elischer } 628*426b3d04SJulian Elischer 629*426b3d04SJulian Elischer #undef PULLUP_CHECK 630*426b3d04SJulian Elischer 631*426b3d04SJulian Elischer bypass: 632*426b3d04SJulian Elischer out = NULL; 633*426b3d04SJulian Elischer 634d359a62dSAndrey V. Elsukov if (hook == priv->in) { 635d359a62dSAndrey V. Elsukov /* return frames on 'in' hook if 'out' not connected */ 636*426b3d04SJulian Elischer out = priv->out ? priv->out : priv->in; 637*426b3d04SJulian Elischer } else if (hook == priv->out && priv->in) { 638*426b3d04SJulian Elischer /* pass frames on 'out' hook if 'in' connected */ 639*426b3d04SJulian Elischer out = priv->in; 640d359a62dSAndrey V. Elsukov } 641d359a62dSAndrey V. Elsukov 642*426b3d04SJulian Elischer if (out == NULL) 643*426b3d04SJulian Elischer ERROUT(0); 644*426b3d04SJulian Elischer 645*426b3d04SJulian Elischer NG_FWD_NEW_DATA(error, item, out, m); 646*426b3d04SJulian Elischer 647*426b3d04SJulian Elischer return (error); 648*426b3d04SJulian Elischer 649*426b3d04SJulian Elischer done: 650*426b3d04SJulian Elischer drop: 651d359a62dSAndrey V. Elsukov NG_FREE_ITEM(item); 652d359a62dSAndrey V. Elsukov NG_FREE_M(m); 653*426b3d04SJulian Elischer 654*426b3d04SJulian Elischer priv->stats.dropped++; 655*426b3d04SJulian Elischer 656d359a62dSAndrey V. Elsukov return (error); 657d359a62dSAndrey V. Elsukov } 658d359a62dSAndrey V. Elsukov 659d359a62dSAndrey V. Elsukov static int 660d359a62dSAndrey V. Elsukov ng_patch_shutdown(node_p node) 661d359a62dSAndrey V. Elsukov { 662d359a62dSAndrey V. Elsukov const priv_p privdata = NG_NODE_PRIVATE(node); 663d359a62dSAndrey V. Elsukov 664d359a62dSAndrey V. Elsukov NG_NODE_SET_PRIVATE(node, NULL); 665d359a62dSAndrey V. Elsukov NG_NODE_UNREF(node); 666*426b3d04SJulian Elischer 667*426b3d04SJulian Elischer if (privdata->conf != NULL) 668*426b3d04SJulian Elischer free(privdata->conf, M_NETGRAPH); 669*426b3d04SJulian Elischer 670d359a62dSAndrey V. Elsukov free(privdata, M_NETGRAPH); 671*426b3d04SJulian Elischer 672d359a62dSAndrey V. Elsukov return (0); 673d359a62dSAndrey V. Elsukov } 674d359a62dSAndrey V. Elsukov 675d359a62dSAndrey V. Elsukov static int 676d359a62dSAndrey V. Elsukov ng_patch_disconnect(hook_p hook) 677d359a62dSAndrey V. Elsukov { 6786c0a2eb1SAndrey V. Elsukov priv_p priv; 679d359a62dSAndrey V. Elsukov 6806c0a2eb1SAndrey V. Elsukov priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 681*426b3d04SJulian Elischer 682d359a62dSAndrey V. Elsukov if (hook == priv->in) { 683d359a62dSAndrey V. Elsukov priv->in = NULL; 684d359a62dSAndrey V. Elsukov } 685*426b3d04SJulian Elischer 686d359a62dSAndrey V. Elsukov if (hook == priv->out) { 687d359a62dSAndrey V. Elsukov priv->out = NULL; 688d359a62dSAndrey V. Elsukov } 689*426b3d04SJulian Elischer 6906c0a2eb1SAndrey V. Elsukov if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 && 6916c0a2eb1SAndrey V. Elsukov NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) /* already shutting down? */ 692d359a62dSAndrey V. Elsukov ng_rmnode_self(NG_HOOK_NODE(hook)); 693*426b3d04SJulian Elischer 694d359a62dSAndrey V. Elsukov return (0); 695d359a62dSAndrey V. Elsukov } 696