1d359a62dSAndrey V. Elsukov /*- 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3fe267a55SPedro F. Giffuni * 4bf909fc9SJulian Elischer * Copyright (c) 2010 Maxim Ignatenko <gelraen.ua@gmail.com> 5426b3d04SJulian Elischer * Copyright (c) 2015 Dmitry Vagin <daemon.hammer@ya.ru> 6d359a62dSAndrey V. Elsukov * All rights reserved. 7d359a62dSAndrey V. Elsukov * 8d359a62dSAndrey V. Elsukov * Redistribution and use in source and binary forms, with or without 9d359a62dSAndrey V. Elsukov * modification, are permitted provided that the following conditions 10d359a62dSAndrey V. Elsukov * are met: 11d359a62dSAndrey V. Elsukov * 1. Redistributions of source code must retain the above copyright 12d359a62dSAndrey V. Elsukov * notice, this list of conditions and the following disclaimer. 13d359a62dSAndrey V. Elsukov * 2. Redistributions in binary form must reproduce the above copyright 14d359a62dSAndrey V. Elsukov * notice, this list of conditions and the following disclaimer in the 15d359a62dSAndrey V. Elsukov * documentation and/or other materials provided with the distribution. 16d359a62dSAndrey V. Elsukov * 17d359a62dSAndrey V. Elsukov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18d359a62dSAndrey V. Elsukov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19d359a62dSAndrey V. Elsukov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20d359a62dSAndrey V. Elsukov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21d359a62dSAndrey V. Elsukov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22d359a62dSAndrey V. Elsukov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23d359a62dSAndrey V. Elsukov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24d359a62dSAndrey V. Elsukov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25d359a62dSAndrey V. Elsukov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26d359a62dSAndrey V. Elsukov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27d359a62dSAndrey V. Elsukov * SUCH DAMAGE. 28d359a62dSAndrey V. Elsukov * 29d359a62dSAndrey V. Elsukov */ 30d359a62dSAndrey V. Elsukov 31d359a62dSAndrey V. Elsukov #include <sys/cdefs.h> 32d359a62dSAndrey V. Elsukov __FBSDID("$FreeBSD$"); 33d359a62dSAndrey V. Elsukov 34d359a62dSAndrey V. Elsukov #include <sys/param.h> 35cac2fe69SAndrey V. Elsukov #include <sys/systm.h> 36d359a62dSAndrey V. Elsukov #include <sys/kernel.h> 37cac2fe69SAndrey V. Elsukov #include <sys/endian.h> 386c0a2eb1SAndrey V. Elsukov #include <sys/malloc.h> 396c0a2eb1SAndrey V. Elsukov #include <sys/mbuf.h> 40426b3d04SJulian Elischer 41426b3d04SJulian Elischer #include <net/bpf.h> 42426b3d04SJulian Elischer #include <net/ethernet.h> 43426b3d04SJulian Elischer 44d359a62dSAndrey V. Elsukov #include <netgraph/ng_message.h> 45d359a62dSAndrey V. Elsukov #include <netgraph/ng_parse.h> 46d359a62dSAndrey V. Elsukov #include <netgraph/netgraph.h> 47d359a62dSAndrey V. Elsukov 48426b3d04SJulian Elischer #include <netgraph/ng_patch.h> 49426b3d04SJulian Elischer 50426b3d04SJulian Elischer /* private data */ 51426b3d04SJulian Elischer struct ng_patch_priv { 52426b3d04SJulian Elischer hook_p in; 53426b3d04SJulian Elischer hook_p out; 54426b3d04SJulian Elischer uint8_t dlt; /* DLT_XXX from bpf.h */ 55426b3d04SJulian Elischer struct ng_patch_stats stats; 56426b3d04SJulian Elischer struct ng_patch_config *conf; 57426b3d04SJulian Elischer }; 58426b3d04SJulian Elischer 59426b3d04SJulian Elischer typedef struct ng_patch_priv *priv_p; 60426b3d04SJulian Elischer 61426b3d04SJulian Elischer /* Netgraph methods */ 62d359a62dSAndrey V. Elsukov static ng_constructor_t ng_patch_constructor; 63d359a62dSAndrey V. Elsukov static ng_rcvmsg_t ng_patch_rcvmsg; 64d359a62dSAndrey V. Elsukov static ng_shutdown_t ng_patch_shutdown; 65d359a62dSAndrey V. Elsukov static ng_newhook_t ng_patch_newhook; 66d359a62dSAndrey V. Elsukov static ng_rcvdata_t ng_patch_rcvdata; 67d359a62dSAndrey V. Elsukov static ng_disconnect_t ng_patch_disconnect; 68426b3d04SJulian Elischer #define ERROUT(x) { error = (x); goto done; } 69426b3d04SJulian Elischer 70d359a62dSAndrey V. Elsukov static int 716c0a2eb1SAndrey V. Elsukov ng_patch_config_getlen(const struct ng_parse_type *type, 726c0a2eb1SAndrey V. Elsukov const u_char *start, const u_char *buf) 73d359a62dSAndrey V. Elsukov { 74bf909fc9SJulian Elischer const struct ng_patch_config *conf; 75d359a62dSAndrey V. Elsukov 76bf909fc9SJulian Elischer conf = (const struct ng_patch_config *)(buf - 77cac2fe69SAndrey V. Elsukov offsetof(struct ng_patch_config, ops)); 78bf909fc9SJulian Elischer 79bf909fc9SJulian Elischer return (conf->count); 80d359a62dSAndrey V. Elsukov } 81d359a62dSAndrey V. Elsukov 82d359a62dSAndrey V. Elsukov static const struct ng_parse_struct_field ng_patch_op_type_fields[] 83426b3d04SJulian Elischer = NG_PATCH_OP_TYPE; 84d359a62dSAndrey V. Elsukov static const struct ng_parse_type ng_patch_op_type = { 85d359a62dSAndrey V. Elsukov &ng_parse_struct_type, 86d359a62dSAndrey V. Elsukov &ng_patch_op_type_fields 87d359a62dSAndrey V. Elsukov }; 88d359a62dSAndrey V. Elsukov 89bf909fc9SJulian Elischer static const struct ng_parse_array_info ng_patch_ops_array_info = { 90d359a62dSAndrey V. Elsukov &ng_patch_op_type, 91d359a62dSAndrey V. Elsukov &ng_patch_config_getlen 92d359a62dSAndrey V. Elsukov }; 93bf909fc9SJulian Elischer static const struct ng_parse_type ng_patch_ops_array_type = { 94d359a62dSAndrey V. Elsukov &ng_parse_array_type, 95bf909fc9SJulian Elischer &ng_patch_ops_array_info 96d359a62dSAndrey V. Elsukov }; 97d359a62dSAndrey V. Elsukov 98d359a62dSAndrey V. Elsukov static const struct ng_parse_struct_field ng_patch_config_type_fields[] 99426b3d04SJulian Elischer = NG_PATCH_CONFIG_TYPE; 100d359a62dSAndrey V. Elsukov static const struct ng_parse_type ng_patch_config_type = { 101d359a62dSAndrey V. Elsukov &ng_parse_struct_type, 102d359a62dSAndrey V. Elsukov &ng_patch_config_type_fields 103d359a62dSAndrey V. Elsukov }; 104d359a62dSAndrey V. Elsukov 105d359a62dSAndrey V. Elsukov static const struct ng_parse_struct_field ng_patch_stats_fields[] 106426b3d04SJulian Elischer = NG_PATCH_STATS_TYPE; 107d359a62dSAndrey V. Elsukov static const struct ng_parse_type ng_patch_stats_type = { 108d359a62dSAndrey V. Elsukov &ng_parse_struct_type, 109d359a62dSAndrey V. Elsukov &ng_patch_stats_fields 110d359a62dSAndrey V. Elsukov }; 111d359a62dSAndrey V. Elsukov 112d359a62dSAndrey V. Elsukov static const struct ng_cmdlist ng_patch_cmdlist[] = { 113d359a62dSAndrey V. Elsukov { 114d359a62dSAndrey V. Elsukov NGM_PATCH_COOKIE, 115426b3d04SJulian Elischer NGM_PATCH_GETDLT, 116426b3d04SJulian Elischer "getdlt", 117426b3d04SJulian Elischer NULL, 118426b3d04SJulian Elischer &ng_parse_uint8_type 119426b3d04SJulian Elischer }, 120426b3d04SJulian Elischer { 121426b3d04SJulian Elischer NGM_PATCH_COOKIE, 122426b3d04SJulian Elischer NGM_PATCH_SETDLT, 123426b3d04SJulian Elischer "setdlt", 124426b3d04SJulian Elischer &ng_parse_uint8_type, 125426b3d04SJulian Elischer NULL 126426b3d04SJulian Elischer }, 127426b3d04SJulian Elischer { 128426b3d04SJulian Elischer NGM_PATCH_COOKIE, 129d359a62dSAndrey V. Elsukov NGM_PATCH_GETCONFIG, 130d359a62dSAndrey V. Elsukov "getconfig", 131d359a62dSAndrey V. Elsukov NULL, 132d359a62dSAndrey V. Elsukov &ng_patch_config_type 133d359a62dSAndrey V. Elsukov }, 134d359a62dSAndrey V. Elsukov { 135d359a62dSAndrey V. Elsukov NGM_PATCH_COOKIE, 136d359a62dSAndrey V. Elsukov NGM_PATCH_SETCONFIG, 137d359a62dSAndrey V. Elsukov "setconfig", 138d359a62dSAndrey V. Elsukov &ng_patch_config_type, 139d359a62dSAndrey V. Elsukov NULL 140d359a62dSAndrey V. Elsukov }, 141d359a62dSAndrey V. Elsukov { 142d359a62dSAndrey V. Elsukov NGM_PATCH_COOKIE, 143d359a62dSAndrey V. Elsukov NGM_PATCH_GET_STATS, 144d359a62dSAndrey V. Elsukov "getstats", 145d359a62dSAndrey V. Elsukov NULL, 146d359a62dSAndrey V. Elsukov &ng_patch_stats_type 147d359a62dSAndrey V. Elsukov }, 148d359a62dSAndrey V. Elsukov { 149d359a62dSAndrey V. Elsukov NGM_PATCH_COOKIE, 150d359a62dSAndrey V. Elsukov NGM_PATCH_CLR_STATS, 151d359a62dSAndrey V. Elsukov "clrstats", 152d359a62dSAndrey V. Elsukov NULL, 153d359a62dSAndrey V. Elsukov NULL 154d359a62dSAndrey V. Elsukov }, 155d359a62dSAndrey V. Elsukov { 156d359a62dSAndrey V. Elsukov NGM_PATCH_COOKIE, 157d359a62dSAndrey V. Elsukov NGM_PATCH_GETCLR_STATS, 158d359a62dSAndrey V. Elsukov "getclrstats", 159d359a62dSAndrey V. Elsukov NULL, 160d359a62dSAndrey V. Elsukov &ng_patch_stats_type 161d359a62dSAndrey V. Elsukov }, 162d359a62dSAndrey V. Elsukov { 0 } 163d359a62dSAndrey V. Elsukov }; 164d359a62dSAndrey V. Elsukov 165d359a62dSAndrey V. Elsukov static struct ng_type typestruct = { 166d359a62dSAndrey V. Elsukov .version = NG_ABI_VERSION, 167d359a62dSAndrey V. Elsukov .name = NG_PATCH_NODE_TYPE, 168d359a62dSAndrey V. Elsukov .constructor = ng_patch_constructor, 169d359a62dSAndrey V. Elsukov .rcvmsg = ng_patch_rcvmsg, 170d359a62dSAndrey V. Elsukov .shutdown = ng_patch_shutdown, 171d359a62dSAndrey V. Elsukov .newhook = ng_patch_newhook, 172d359a62dSAndrey V. Elsukov .rcvdata = ng_patch_rcvdata, 173d359a62dSAndrey V. Elsukov .disconnect = ng_patch_disconnect, 174d359a62dSAndrey V. Elsukov .cmdlist = ng_patch_cmdlist, 175d359a62dSAndrey V. Elsukov }; 176bf909fc9SJulian Elischer 177d359a62dSAndrey V. Elsukov NETGRAPH_INIT(patch, &typestruct); 178d359a62dSAndrey V. Elsukov 179d359a62dSAndrey V. Elsukov static int 180d359a62dSAndrey V. Elsukov ng_patch_constructor(node_p node) 181d359a62dSAndrey V. Elsukov { 182d359a62dSAndrey V. Elsukov priv_p privdata; 183d359a62dSAndrey V. Elsukov 184ffbfc0aaSAndrey V. Elsukov privdata = malloc(sizeof(*privdata), M_NETGRAPH, M_WAITOK | M_ZERO); 185426b3d04SJulian Elischer privdata->dlt = DLT_RAW; 186426b3d04SJulian Elischer 187d359a62dSAndrey V. Elsukov NG_NODE_SET_PRIVATE(node, privdata); 188426b3d04SJulian Elischer 189d359a62dSAndrey V. Elsukov return (0); 190d359a62dSAndrey V. Elsukov } 191d359a62dSAndrey V. Elsukov 192d359a62dSAndrey V. Elsukov static int 193d359a62dSAndrey V. Elsukov ng_patch_newhook(node_p node, hook_p hook, const char *name) 194d359a62dSAndrey V. Elsukov { 195d359a62dSAndrey V. Elsukov const priv_p privp = NG_NODE_PRIVATE(node); 196d359a62dSAndrey V. Elsukov 197d359a62dSAndrey V. Elsukov if (strncmp(name, NG_PATCH_HOOK_IN, strlen(NG_PATCH_HOOK_IN)) == 0) { 198d359a62dSAndrey V. Elsukov privp->in = hook; 199d359a62dSAndrey V. Elsukov } else if (strncmp(name, NG_PATCH_HOOK_OUT, 200d359a62dSAndrey V. Elsukov strlen(NG_PATCH_HOOK_OUT)) == 0) { 201d359a62dSAndrey V. Elsukov privp->out = hook; 202d359a62dSAndrey V. Elsukov } else 203d359a62dSAndrey V. Elsukov return (EINVAL); 204426b3d04SJulian Elischer 205d359a62dSAndrey V. Elsukov return (0); 206d359a62dSAndrey V. Elsukov } 207d359a62dSAndrey V. Elsukov 208d359a62dSAndrey V. Elsukov static int 209d359a62dSAndrey V. Elsukov ng_patch_rcvmsg(node_p node, item_p item, hook_p lasthook) 210d359a62dSAndrey V. Elsukov { 211d359a62dSAndrey V. Elsukov const priv_p privp = NG_NODE_PRIVATE(node); 2126c0a2eb1SAndrey V. Elsukov struct ng_patch_config *conf, *newconf; 213d359a62dSAndrey V. Elsukov struct ng_mesg *msg; 214426b3d04SJulian Elischer struct ng_mesg *resp = NULL; 215426b3d04SJulian Elischer int i, error = 0; 216d359a62dSAndrey V. Elsukov 217d359a62dSAndrey V. Elsukov NGI_GET_MSG(item, msg); 218426b3d04SJulian Elischer 219426b3d04SJulian Elischer if (msg->header.typecookie != NGM_PATCH_COOKIE) 220426b3d04SJulian Elischer ERROUT(EINVAL); 221426b3d04SJulian Elischer 222426b3d04SJulian Elischer switch (msg->header.cmd) 223d359a62dSAndrey V. Elsukov { 224426b3d04SJulian Elischer case NGM_PATCH_GETCONFIG: 225426b3d04SJulian Elischer if (privp->conf == NULL) 226426b3d04SJulian Elischer ERROUT(0); 227426b3d04SJulian Elischer 228426b3d04SJulian Elischer NG_MKRESPONSE(resp, msg, 229426b3d04SJulian Elischer NG_PATCH_CONF_SIZE(privp->conf->count), M_WAITOK); 230426b3d04SJulian Elischer 231426b3d04SJulian Elischer if (resp == NULL) 232426b3d04SJulian Elischer ERROUT(ENOMEM); 233426b3d04SJulian Elischer 234426b3d04SJulian Elischer bcopy(privp->conf, resp->data, 235426b3d04SJulian Elischer NG_PATCH_CONF_SIZE(privp->conf->count)); 236426b3d04SJulian Elischer 237426b3d04SJulian Elischer conf = (struct ng_patch_config *) resp->data; 238426b3d04SJulian Elischer 239426b3d04SJulian Elischer for (i = 0; i < conf->count; i++) { 240426b3d04SJulian Elischer switch (conf->ops[i].length) 241426b3d04SJulian Elischer { 242426b3d04SJulian Elischer case 1: 243426b3d04SJulian Elischer conf->ops[i].val.v8 = conf->ops[i].val.v1; 244426b3d04SJulian Elischer break; 245426b3d04SJulian Elischer case 2: 246426b3d04SJulian Elischer conf->ops[i].val.v8 = conf->ops[i].val.v2; 247426b3d04SJulian Elischer break; 248426b3d04SJulian Elischer case 4: 249426b3d04SJulian Elischer conf->ops[i].val.v8 = conf->ops[i].val.v4; 250426b3d04SJulian Elischer break; 251426b3d04SJulian Elischer case 8: 252d359a62dSAndrey V. Elsukov break; 253d359a62dSAndrey V. Elsukov } 254426b3d04SJulian Elischer } 255d359a62dSAndrey V. Elsukov 256426b3d04SJulian Elischer break; 257426b3d04SJulian Elischer 258426b3d04SJulian Elischer case NGM_PATCH_SETCONFIG: 259d359a62dSAndrey V. Elsukov conf = (struct ng_patch_config *) msg->data; 260426b3d04SJulian Elischer 261426b3d04SJulian Elischer if (msg->header.arglen < sizeof(struct ng_patch_config) || 262426b3d04SJulian Elischer msg->header.arglen < NG_PATCH_CONF_SIZE(conf->count)) 263426b3d04SJulian Elischer ERROUT(EINVAL); 264d359a62dSAndrey V. Elsukov 265d359a62dSAndrey V. Elsukov for (i = 0; i < conf->count; i++) { 266426b3d04SJulian Elischer switch (conf->ops[i].length) 267426b3d04SJulian Elischer { 268d359a62dSAndrey V. Elsukov case 1: 269426b3d04SJulian Elischer conf->ops[i].val.v1 = (uint8_t) conf->ops[i].val.v8; 270426b3d04SJulian Elischer break; 271d359a62dSAndrey V. Elsukov case 2: 272426b3d04SJulian Elischer conf->ops[i].val.v2 = (uint16_t) conf->ops[i].val.v8; 273426b3d04SJulian Elischer break; 274d359a62dSAndrey V. Elsukov case 4: 275426b3d04SJulian Elischer conf->ops[i].val.v4 = (uint32_t) conf->ops[i].val.v8; 276426b3d04SJulian Elischer break; 277d359a62dSAndrey V. Elsukov case 8: 278d359a62dSAndrey V. Elsukov break; 279d359a62dSAndrey V. Elsukov default: 280426b3d04SJulian Elischer ERROUT(EINVAL); 281d359a62dSAndrey V. Elsukov } 282d359a62dSAndrey V. Elsukov } 283d359a62dSAndrey V. Elsukov 284426b3d04SJulian Elischer conf->csum_flags &= NG_PATCH_CSUM_IPV4|NG_PATCH_CSUM_IPV6; 285426b3d04SJulian Elischer conf->relative_offset = !!conf->relative_offset; 286d359a62dSAndrey V. Elsukov 287426b3d04SJulian Elischer newconf = malloc(NG_PATCH_CONF_SIZE(conf->count), M_NETGRAPH, M_WAITOK | M_ZERO); 288426b3d04SJulian Elischer 289426b3d04SJulian Elischer bcopy(conf, newconf, NG_PATCH_CONF_SIZE(conf->count)); 290426b3d04SJulian Elischer 291426b3d04SJulian Elischer if (privp->conf) 292426b3d04SJulian Elischer free(privp->conf, M_NETGRAPH); 293426b3d04SJulian Elischer 294426b3d04SJulian Elischer privp->conf = newconf; 295426b3d04SJulian Elischer 296d359a62dSAndrey V. Elsukov break; 297426b3d04SJulian Elischer 298d359a62dSAndrey V. Elsukov case NGM_PATCH_GET_STATS: 299d359a62dSAndrey V. Elsukov case NGM_PATCH_CLR_STATS: 300426b3d04SJulian Elischer case NGM_PATCH_GETCLR_STATS: 301426b3d04SJulian Elischer if (msg->header.cmd != NGM_PATCH_CLR_STATS) { 302426b3d04SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(struct ng_patch_stats), M_WAITOK); 303426b3d04SJulian Elischer 304426b3d04SJulian Elischer if (resp == NULL) 305426b3d04SJulian Elischer ERROUT(ENOMEM); 306426b3d04SJulian Elischer 307426b3d04SJulian Elischer bcopy(&(privp->stats), resp->data, sizeof(struct ng_patch_stats)); 308d359a62dSAndrey V. Elsukov } 309d359a62dSAndrey V. Elsukov 310426b3d04SJulian Elischer if (msg->header.cmd != NGM_PATCH_GET_STATS) 311426b3d04SJulian Elischer bzero(&(privp->stats), sizeof(struct ng_patch_stats)); 312426b3d04SJulian Elischer 313426b3d04SJulian Elischer break; 314426b3d04SJulian Elischer 315426b3d04SJulian Elischer case NGM_PATCH_GETDLT: 316426b3d04SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(uint8_t), M_WAITOK); 317426b3d04SJulian Elischer 318426b3d04SJulian Elischer if (resp == NULL) 319426b3d04SJulian Elischer ERROUT(ENOMEM); 320426b3d04SJulian Elischer 321426b3d04SJulian Elischer *((uint8_t *) resp->data) = privp->dlt; 322426b3d04SJulian Elischer 323426b3d04SJulian Elischer break; 324426b3d04SJulian Elischer 325426b3d04SJulian Elischer case NGM_PATCH_SETDLT: 326426b3d04SJulian Elischer if (msg->header.arglen != sizeof(uint8_t)) 327426b3d04SJulian Elischer ERROUT(EINVAL); 328426b3d04SJulian Elischer 329426b3d04SJulian Elischer switch (*(uint8_t *) msg->data) 330426b3d04SJulian Elischer { 331426b3d04SJulian Elischer case DLT_EN10MB: 332426b3d04SJulian Elischer case DLT_RAW: 333426b3d04SJulian Elischer privp->dlt = *(uint8_t *) msg->data; 334426b3d04SJulian Elischer break; 335426b3d04SJulian Elischer 336426b3d04SJulian Elischer default: 337426b3d04SJulian Elischer ERROUT(EINVAL); 338426b3d04SJulian Elischer } 339426b3d04SJulian Elischer 340426b3d04SJulian Elischer break; 341426b3d04SJulian Elischer 342426b3d04SJulian Elischer default: 343426b3d04SJulian Elischer ERROUT(EINVAL); 344426b3d04SJulian Elischer } 345426b3d04SJulian Elischer 346426b3d04SJulian Elischer done: 347d359a62dSAndrey V. Elsukov NG_RESPOND_MSG(error, node, item, resp); 348d359a62dSAndrey V. Elsukov NG_FREE_MSG(msg); 349426b3d04SJulian Elischer 350d359a62dSAndrey V. Elsukov return (error); 351d359a62dSAndrey V. Elsukov } 352d359a62dSAndrey V. Elsukov 353d359a62dSAndrey V. Elsukov static void 354426b3d04SJulian Elischer do_patch(priv_p privp, struct mbuf *m, int global_offset) 355d359a62dSAndrey V. Elsukov { 356426b3d04SJulian Elischer int i, offset, patched = 0; 357426b3d04SJulian Elischer union ng_patch_op_val val; 358d359a62dSAndrey V. Elsukov 359426b3d04SJulian Elischer for (i = 0; i < privp->conf->count; i++) { 360426b3d04SJulian Elischer offset = global_offset + privp->conf->ops[i].offset; 361426b3d04SJulian Elischer 362426b3d04SJulian Elischer if (offset + privp->conf->ops[i].length > m->m_pkthdr.len) 363d359a62dSAndrey V. Elsukov continue; 364d359a62dSAndrey V. Elsukov 365d359a62dSAndrey V. Elsukov /* for "=" operation we don't need to copy data from mbuf */ 366426b3d04SJulian Elischer if (privp->conf->ops[i].mode != NG_PATCH_MODE_SET) 367426b3d04SJulian Elischer m_copydata(m, offset, privp->conf->ops[i].length, (caddr_t) &val); 368d359a62dSAndrey V. Elsukov 369426b3d04SJulian Elischer switch (privp->conf->ops[i].length) 370426b3d04SJulian Elischer { 371d359a62dSAndrey V. Elsukov case 1: 372426b3d04SJulian Elischer switch (privp->conf->ops[i].mode) 373426b3d04SJulian Elischer { 374d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SET: 375426b3d04SJulian Elischer val.v1 = privp->conf->ops[i].val.v1; 376d359a62dSAndrey V. Elsukov break; 377d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_ADD: 378426b3d04SJulian Elischer val.v1 += privp->conf->ops[i].val.v1; 379d359a62dSAndrey V. Elsukov break; 380d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SUB: 381426b3d04SJulian Elischer val.v1 -= privp->conf->ops[i].val.v1; 382d359a62dSAndrey V. Elsukov break; 383d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_MUL: 384426b3d04SJulian Elischer val.v1 *= privp->conf->ops[i].val.v1; 385d359a62dSAndrey V. Elsukov break; 386d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_DIV: 387426b3d04SJulian Elischer val.v1 /= privp->conf->ops[i].val.v1; 388d359a62dSAndrey V. Elsukov break; 389d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_NEG: 390426b3d04SJulian Elischer *((int8_t *) &val) = - *((int8_t *) &val); 391d359a62dSAndrey V. Elsukov break; 392d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_AND: 393426b3d04SJulian Elischer val.v1 &= privp->conf->ops[i].val.v1; 394d359a62dSAndrey V. Elsukov break; 395d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_OR: 396426b3d04SJulian Elischer val.v1 |= privp->conf->ops[i].val.v1; 397d359a62dSAndrey V. Elsukov break; 398d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_XOR: 399426b3d04SJulian Elischer val.v1 ^= privp->conf->ops[i].val.v1; 400d359a62dSAndrey V. Elsukov break; 401d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SHL: 402426b3d04SJulian Elischer val.v1 <<= privp->conf->ops[i].val.v1; 403d359a62dSAndrey V. Elsukov break; 404d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SHR: 405426b3d04SJulian Elischer val.v1 >>= privp->conf->ops[i].val.v1; 406d359a62dSAndrey V. Elsukov break; 407d359a62dSAndrey V. Elsukov } 408d359a62dSAndrey V. Elsukov break; 409426b3d04SJulian Elischer 410d359a62dSAndrey V. Elsukov case 2: 411426b3d04SJulian Elischer val.v2 = ntohs(val.v2); 412426b3d04SJulian Elischer 413426b3d04SJulian Elischer switch (privp->conf->ops[i].mode) 414426b3d04SJulian Elischer { 415d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SET: 416426b3d04SJulian Elischer val.v2 = privp->conf->ops[i].val.v2; 417d359a62dSAndrey V. Elsukov break; 418d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_ADD: 419426b3d04SJulian Elischer val.v2 += privp->conf->ops[i].val.v2; 420d359a62dSAndrey V. Elsukov break; 421d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SUB: 422426b3d04SJulian Elischer val.v2 -= privp->conf->ops[i].val.v2; 423d359a62dSAndrey V. Elsukov break; 424d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_MUL: 425426b3d04SJulian Elischer val.v2 *= privp->conf->ops[i].val.v2; 426d359a62dSAndrey V. Elsukov break; 427d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_DIV: 428426b3d04SJulian Elischer val.v2 /= privp->conf->ops[i].val.v2; 429d359a62dSAndrey V. Elsukov break; 430d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_NEG: 431426b3d04SJulian Elischer *((int16_t *) &val) = - *((int16_t *) &val); 432d359a62dSAndrey V. Elsukov break; 433d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_AND: 434426b3d04SJulian Elischer val.v2 &= privp->conf->ops[i].val.v2; 435d359a62dSAndrey V. Elsukov break; 436d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_OR: 437426b3d04SJulian Elischer val.v2 |= privp->conf->ops[i].val.v2; 438d359a62dSAndrey V. Elsukov break; 439d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_XOR: 440426b3d04SJulian Elischer val.v2 ^= privp->conf->ops[i].val.v2; 441d359a62dSAndrey V. Elsukov break; 442d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SHL: 443426b3d04SJulian Elischer val.v2 <<= privp->conf->ops[i].val.v2; 444d359a62dSAndrey V. Elsukov break; 445d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SHR: 446426b3d04SJulian Elischer val.v2 >>= privp->conf->ops[i].val.v2; 447d359a62dSAndrey V. Elsukov break; 448d359a62dSAndrey V. Elsukov } 449d359a62dSAndrey V. Elsukov 450426b3d04SJulian Elischer val.v2 = htons(val.v2); 451426b3d04SJulian Elischer 452426b3d04SJulian Elischer break; 453426b3d04SJulian Elischer 454426b3d04SJulian Elischer case 4: 455426b3d04SJulian Elischer val.v4 = ntohl(val.v4); 456426b3d04SJulian Elischer 457426b3d04SJulian Elischer switch (privp->conf->ops[i].mode) 458426b3d04SJulian Elischer { 459426b3d04SJulian Elischer case NG_PATCH_MODE_SET: 460426b3d04SJulian Elischer val.v4 = privp->conf->ops[i].val.v4; 461426b3d04SJulian Elischer break; 462426b3d04SJulian Elischer case NG_PATCH_MODE_ADD: 463426b3d04SJulian Elischer val.v4 += privp->conf->ops[i].val.v4; 464426b3d04SJulian Elischer break; 465426b3d04SJulian Elischer case NG_PATCH_MODE_SUB: 466426b3d04SJulian Elischer val.v4 -= privp->conf->ops[i].val.v4; 467426b3d04SJulian Elischer break; 468426b3d04SJulian Elischer case NG_PATCH_MODE_MUL: 469426b3d04SJulian Elischer val.v4 *= privp->conf->ops[i].val.v4; 470426b3d04SJulian Elischer break; 471426b3d04SJulian Elischer case NG_PATCH_MODE_DIV: 472426b3d04SJulian Elischer val.v4 /= privp->conf->ops[i].val.v4; 473426b3d04SJulian Elischer break; 474426b3d04SJulian Elischer case NG_PATCH_MODE_NEG: 475426b3d04SJulian Elischer *((int32_t *) &val) = - *((int32_t *) &val); 476426b3d04SJulian Elischer break; 477426b3d04SJulian Elischer case NG_PATCH_MODE_AND: 478426b3d04SJulian Elischer val.v4 &= privp->conf->ops[i].val.v4; 479426b3d04SJulian Elischer break; 480426b3d04SJulian Elischer case NG_PATCH_MODE_OR: 481426b3d04SJulian Elischer val.v4 |= privp->conf->ops[i].val.v4; 482426b3d04SJulian Elischer break; 483426b3d04SJulian Elischer case NG_PATCH_MODE_XOR: 484426b3d04SJulian Elischer val.v4 ^= privp->conf->ops[i].val.v4; 485426b3d04SJulian Elischer break; 486426b3d04SJulian Elischer case NG_PATCH_MODE_SHL: 487426b3d04SJulian Elischer val.v4 <<= privp->conf->ops[i].val.v4; 488426b3d04SJulian Elischer break; 489426b3d04SJulian Elischer case NG_PATCH_MODE_SHR: 490426b3d04SJulian Elischer val.v4 >>= privp->conf->ops[i].val.v4; 491426b3d04SJulian Elischer break; 492426b3d04SJulian Elischer } 493426b3d04SJulian Elischer 494426b3d04SJulian Elischer val.v4 = htonl(val.v4); 495426b3d04SJulian Elischer 496426b3d04SJulian Elischer break; 497426b3d04SJulian Elischer 498426b3d04SJulian Elischer case 8: 499426b3d04SJulian Elischer val.v8 = be64toh(val.v8); 500426b3d04SJulian Elischer 501426b3d04SJulian Elischer switch (privp->conf->ops[i].mode) 502426b3d04SJulian Elischer { 503426b3d04SJulian Elischer case NG_PATCH_MODE_SET: 504426b3d04SJulian Elischer val.v8 = privp->conf->ops[i].val.v8; 505426b3d04SJulian Elischer break; 506426b3d04SJulian Elischer case NG_PATCH_MODE_ADD: 507426b3d04SJulian Elischer val.v8 += privp->conf->ops[i].val.v8; 508426b3d04SJulian Elischer break; 509426b3d04SJulian Elischer case NG_PATCH_MODE_SUB: 510426b3d04SJulian Elischer val.v8 -= privp->conf->ops[i].val.v8; 511426b3d04SJulian Elischer break; 512426b3d04SJulian Elischer case NG_PATCH_MODE_MUL: 513426b3d04SJulian Elischer val.v8 *= privp->conf->ops[i].val.v8; 514426b3d04SJulian Elischer break; 515426b3d04SJulian Elischer case NG_PATCH_MODE_DIV: 516426b3d04SJulian Elischer val.v8 /= privp->conf->ops[i].val.v8; 517426b3d04SJulian Elischer break; 518426b3d04SJulian Elischer case NG_PATCH_MODE_NEG: 519426b3d04SJulian Elischer *((int64_t *) &val) = - *((int64_t *) &val); 520426b3d04SJulian Elischer break; 521426b3d04SJulian Elischer case NG_PATCH_MODE_AND: 522426b3d04SJulian Elischer val.v8 &= privp->conf->ops[i].val.v8; 523426b3d04SJulian Elischer break; 524426b3d04SJulian Elischer case NG_PATCH_MODE_OR: 525426b3d04SJulian Elischer val.v8 |= privp->conf->ops[i].val.v8; 526426b3d04SJulian Elischer break; 527426b3d04SJulian Elischer case NG_PATCH_MODE_XOR: 528426b3d04SJulian Elischer val.v8 ^= privp->conf->ops[i].val.v8; 529426b3d04SJulian Elischer break; 530426b3d04SJulian Elischer case NG_PATCH_MODE_SHL: 531426b3d04SJulian Elischer val.v8 <<= privp->conf->ops[i].val.v8; 532426b3d04SJulian Elischer break; 533426b3d04SJulian Elischer case NG_PATCH_MODE_SHR: 534426b3d04SJulian Elischer val.v8 >>= privp->conf->ops[i].val.v8; 535426b3d04SJulian Elischer break; 536426b3d04SJulian Elischer } 537426b3d04SJulian Elischer 538426b3d04SJulian Elischer val.v8 = htobe64(val.v8); 539426b3d04SJulian Elischer 540426b3d04SJulian Elischer break; 541426b3d04SJulian Elischer } 542426b3d04SJulian Elischer 543426b3d04SJulian Elischer m_copyback(m, offset, privp->conf->ops[i].length, (caddr_t) &val); 544d359a62dSAndrey V. Elsukov patched = 1; 545d359a62dSAndrey V. Elsukov } 546426b3d04SJulian Elischer 547426b3d04SJulian Elischer if (patched) 548d359a62dSAndrey V. Elsukov privp->stats.patched++; 549d359a62dSAndrey V. Elsukov } 550d359a62dSAndrey V. Elsukov 551d359a62dSAndrey V. Elsukov static int 552d359a62dSAndrey V. Elsukov ng_patch_rcvdata(hook_p hook, item_p item) 553d359a62dSAndrey V. Elsukov { 554d359a62dSAndrey V. Elsukov const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 555d359a62dSAndrey V. Elsukov struct mbuf *m; 556426b3d04SJulian Elischer hook_p out; 557426b3d04SJulian Elischer int pullup_len = 0; 558426b3d04SJulian Elischer int error = 0; 559d359a62dSAndrey V. Elsukov 560d359a62dSAndrey V. Elsukov priv->stats.received++; 561426b3d04SJulian Elischer 562d359a62dSAndrey V. Elsukov NGI_GET_M(item, m); 563426b3d04SJulian Elischer 564426b3d04SJulian Elischer #define PULLUP_CHECK(mbuf, length) do { \ 565426b3d04SJulian Elischer pullup_len += length; \ 566426b3d04SJulian Elischer if (((mbuf)->m_pkthdr.len < pullup_len) || \ 567426b3d04SJulian Elischer (pullup_len > MHLEN)) { \ 568426b3d04SJulian Elischer error = EINVAL; \ 569426b3d04SJulian Elischer goto bypass; \ 570426b3d04SJulian Elischer } \ 571426b3d04SJulian Elischer if ((mbuf)->m_len < pullup_len && \ 572426b3d04SJulian Elischer (((mbuf) = m_pullup((mbuf), pullup_len)) == NULL)) { \ 573426b3d04SJulian Elischer error = ENOBUFS; \ 574426b3d04SJulian Elischer goto drop; \ 575426b3d04SJulian Elischer } \ 576426b3d04SJulian Elischer } while (0) 577426b3d04SJulian Elischer 578426b3d04SJulian Elischer if (priv->conf && hook == priv->in && 579426b3d04SJulian Elischer m && (m->m_flags & M_PKTHDR)) { 580d359a62dSAndrey V. Elsukov m = m_unshare(m, M_NOWAIT); 581426b3d04SJulian Elischer 582426b3d04SJulian Elischer if (m == NULL) 583426b3d04SJulian Elischer ERROUT(ENOMEM); 584426b3d04SJulian Elischer 585426b3d04SJulian Elischer if (priv->conf->relative_offset) { 586426b3d04SJulian Elischer struct ether_header *eh; 587426b3d04SJulian Elischer struct ng_patch_vlan_header *vh; 588426b3d04SJulian Elischer uint16_t etype; 589426b3d04SJulian Elischer 590426b3d04SJulian Elischer switch (priv->dlt) 591426b3d04SJulian Elischer { 592426b3d04SJulian Elischer case DLT_EN10MB: 593426b3d04SJulian Elischer PULLUP_CHECK(m, sizeof(struct ether_header)); 594426b3d04SJulian Elischer eh = mtod(m, struct ether_header *); 595426b3d04SJulian Elischer etype = ntohs(eh->ether_type); 596426b3d04SJulian Elischer 597426b3d04SJulian Elischer for (;;) { /* QinQ support */ 598426b3d04SJulian Elischer switch (etype) 599426b3d04SJulian Elischer { 600426b3d04SJulian Elischer case 0x8100: 601426b3d04SJulian Elischer case 0x88A8: 602426b3d04SJulian Elischer case 0x9100: 603426b3d04SJulian Elischer PULLUP_CHECK(m, sizeof(struct ng_patch_vlan_header)); 604426b3d04SJulian Elischer vh = (struct ng_patch_vlan_header *) mtodo(m, 605426b3d04SJulian Elischer pullup_len - sizeof(struct ng_patch_vlan_header)); 606426b3d04SJulian Elischer etype = ntohs(vh->etype); 607426b3d04SJulian Elischer break; 608426b3d04SJulian Elischer 609426b3d04SJulian Elischer default: 610426b3d04SJulian Elischer goto loopend; 611d359a62dSAndrey V. Elsukov } 612426b3d04SJulian Elischer } 613426b3d04SJulian Elischer loopend: 614426b3d04SJulian Elischer break; 615426b3d04SJulian Elischer 616426b3d04SJulian Elischer case DLT_RAW: 617426b3d04SJulian Elischer break; 618426b3d04SJulian Elischer 619426b3d04SJulian Elischer default: 620426b3d04SJulian Elischer ERROUT(EINVAL); 621426b3d04SJulian Elischer } 622d359a62dSAndrey V. Elsukov } 623d359a62dSAndrey V. Elsukov 624426b3d04SJulian Elischer do_patch(priv, m, pullup_len); 625426b3d04SJulian Elischer 626426b3d04SJulian Elischer m->m_pkthdr.csum_flags |= priv->conf->csum_flags; 627426b3d04SJulian Elischer } 628426b3d04SJulian Elischer 629426b3d04SJulian Elischer #undef PULLUP_CHECK 630426b3d04SJulian Elischer 631426b3d04SJulian Elischer bypass: 632426b3d04SJulian Elischer out = NULL; 633426b3d04SJulian Elischer 634d359a62dSAndrey V. Elsukov if (hook == priv->in) { 635d359a62dSAndrey V. Elsukov /* return frames on 'in' hook if 'out' not connected */ 636426b3d04SJulian Elischer out = priv->out ? priv->out : priv->in; 637426b3d04SJulian Elischer } else if (hook == priv->out && priv->in) { 638426b3d04SJulian Elischer /* pass frames on 'out' hook if 'in' connected */ 639426b3d04SJulian Elischer out = priv->in; 640d359a62dSAndrey V. Elsukov } 641d359a62dSAndrey V. Elsukov 642426b3d04SJulian Elischer if (out == NULL) 643426b3d04SJulian Elischer ERROUT(0); 644426b3d04SJulian Elischer 645426b3d04SJulian Elischer NG_FWD_NEW_DATA(error, item, out, m); 646426b3d04SJulian Elischer 647426b3d04SJulian Elischer return (error); 648426b3d04SJulian Elischer 649426b3d04SJulian Elischer done: 650426b3d04SJulian Elischer drop: 651d359a62dSAndrey V. Elsukov NG_FREE_ITEM(item); 652d359a62dSAndrey V. Elsukov NG_FREE_M(m); 653426b3d04SJulian Elischer 654426b3d04SJulian Elischer priv->stats.dropped++; 655426b3d04SJulian 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); 666426b3d04SJulian Elischer 667426b3d04SJulian Elischer if (privdata->conf != NULL) 668426b3d04SJulian Elischer free(privdata->conf, M_NETGRAPH); 669426b3d04SJulian Elischer 670d359a62dSAndrey V. Elsukov free(privdata, M_NETGRAPH); 671426b3d04SJulian 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)); 681426b3d04SJulian Elischer 682d359a62dSAndrey V. Elsukov if (hook == priv->in) { 683d359a62dSAndrey V. Elsukov priv->in = NULL; 684d359a62dSAndrey V. Elsukov } 685426b3d04SJulian Elischer 686d359a62dSAndrey V. Elsukov if (hook == priv->out) { 687d359a62dSAndrey V. Elsukov priv->out = NULL; 688d359a62dSAndrey V. Elsukov } 689426b3d04SJulian 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)); 693426b3d04SJulian Elischer 694d359a62dSAndrey V. Elsukov return (0); 695d359a62dSAndrey V. Elsukov } 696