1159979Sglebius/*- 2159979Sglebius * Copyright (c) 2006 Vadim Goncharov <vadimnuclight@tpu.ru> 3159979Sglebius * All rights reserved. 4159979Sglebius * 5159979Sglebius * Redistribution and use in source and binary forms, with or without 6159979Sglebius * modification, are permitted provided that the following conditions 7159979Sglebius * are met: 8159979Sglebius * 1. Redistributions of source code must retain the above copyright 9159979Sglebius * notice unmodified, this list of conditions, and the following 10159979Sglebius * disclaimer. 11159979Sglebius * 2. Redistributions in binary form must reproduce the above copyright 12159979Sglebius * notice, this list of conditions and the following disclaimer in the 13159979Sglebius * documentation and/or other materials provided with the distribution. 14159979Sglebius * 15159979Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16159979Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17159979Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18159979Sglebius * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19159979Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20159979Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21159979Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22159979Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23159979Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24159979Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25159979Sglebius * SUCH DAMAGE. 26159979Sglebius * 27159979Sglebius * Portions Copyright (c) 1999 Whistle Communications, Inc. 28159979Sglebius * (ng_bpf by Archie Cobbs <archie@freebsd.org>) 29159979Sglebius * 30159979Sglebius * $FreeBSD: releng/10.2/sys/netgraph/ng_tag.c 230272 2012-01-17 18:10:25Z glebius $ 31159979Sglebius */ 32159979Sglebius 33159979Sglebius/* 34159979Sglebius * TAG NETGRAPH NODE TYPE 35159979Sglebius * 36159979Sglebius * This node type accepts an arbitrary number of hooks. Each hook can be 37159979Sglebius * configured for an mbuf_tags(9) definition and two hook names: a hook 38159979Sglebius * for matched packets, and a hook for packets, that didn't match. Incoming 39159979Sglebius * packets are examined for configured tag, matched packets are delivered 40159979Sglebius * out via first hook, and not matched out via second. If corresponding hook 41159979Sglebius * is not configured, packets are dropped. 42159979Sglebius * 43159979Sglebius * A hook can also have an outgoing tag definition configured, so that 44159979Sglebius * all packets leaving the hook will be unconditionally appended with newly 45159979Sglebius * allocated tag. 46159979Sglebius * 47159979Sglebius * Both hooks can be set to null tag definitions (that is, with zeroed 48159979Sglebius * fields), so that packet tags are unmodified on output or all packets 49159979Sglebius * are unconditionally forwarded to non-matching hook on input. There is 50159979Sglebius * also a possibility to replace tags by specifying strip flag on input 51159979Sglebius * and replacing tag on corresponding output tag (or simply remove tag if 52159979Sglebius * no tag specified on output). 53159979Sglebius * 54159979Sglebius * If compiled with NG_TAG_DEBUG, each hook also keeps statistics about 55159979Sglebius * how many packets have matched, etc. 56159979Sglebius */ 57159979Sglebius 58159979Sglebius#include <sys/param.h> 59159979Sglebius#include <sys/systm.h> 60159979Sglebius#include <sys/errno.h> 61159979Sglebius#include <sys/kernel.h> 62159979Sglebius#include <sys/malloc.h> 63159979Sglebius#include <sys/mbuf.h> 64159979Sglebius#include <sys/stddef.h> 65159979Sglebius 66159979Sglebius#include <netgraph/ng_message.h> 67159979Sglebius#include <netgraph/netgraph.h> 68159979Sglebius#include <netgraph/ng_parse.h> 69159979Sglebius#include <netgraph/ng_tag.h> 70159979Sglebius 71159979Sglebius#ifdef NG_SEPARATE_MALLOC 72227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_TAG, "netgraph_tag", "netgraph tag node"); 73159979Sglebius#else 74159979Sglebius#define M_NETGRAPH_TAG M_NETGRAPH 75159979Sglebius#endif 76159979Sglebius 77159979Sglebius#define ERROUT(x) do { error = (x); goto done; } while (0) 78159979Sglebius 79159979Sglebius/* 80159979Sglebius * Per hook private info. 81159979Sglebius * 82159979Sglebius * We've separated API and ABI here, to make easier changes in this node, 83159979Sglebius * if needed. If you want to change representation, please do not break API. 84159979Sglebius * We still keep API structures in memory to simplify access to them for 85159979Sglebius * GET* messages, but most of data is accessed in internal representation 86159979Sglebius * only. The reason for this is to speed things up - if data will be 87159979Sglebius * accessed from API structures, there would be double pointer dereferencing 88159979Sglebius * in the code, which almost necessarily leads to CPU cache misses and 89159979Sglebius * reloads. 90159979Sglebius * 91159979Sglebius * We also do another optimization by using resolved pointers to 92159979Sglebius * destination hooks instead of expensive ng_findhook(). 93159979Sglebius */ 94159979Sglebiusstruct ng_tag_hookinfo { 95159979Sglebius hook_p hi_match; /* matching hook pointer */ 96159979Sglebius hook_p hi_nonmatch; /* non-matching hook pointer */ 97159979Sglebius uint32_t in_tag_cookie; 98159979Sglebius uint32_t out_tag_cookie; 99159979Sglebius uint16_t in_tag_id; 100159979Sglebius uint16_t in_tag_len; 101159979Sglebius uint16_t out_tag_id; 102159979Sglebius uint16_t out_tag_len; 103159979Sglebius uint8_t strip; 104159979Sglebius void *in_tag_data; 105159979Sglebius void *out_tag_data; 106159979Sglebius struct ng_tag_hookin *in; 107159979Sglebius struct ng_tag_hookout *out; 108159979Sglebius#ifdef NG_TAG_DEBUG 109159979Sglebius struct ng_tag_hookstat stats; 110159979Sglebius#endif 111159979Sglebius}; 112159979Sglebiustypedef struct ng_tag_hookinfo *hinfo_p; 113159979Sglebius 114159979Sglebius/* Netgraph methods. */ 115159979Sglebiusstatic ng_constructor_t ng_tag_constructor; 116159979Sglebiusstatic ng_rcvmsg_t ng_tag_rcvmsg; 117159979Sglebiusstatic ng_shutdown_t ng_tag_shutdown; 118159979Sglebiusstatic ng_newhook_t ng_tag_newhook; 119159979Sglebiusstatic ng_rcvdata_t ng_tag_rcvdata; 120159979Sglebiusstatic ng_disconnect_t ng_tag_disconnect; 121159979Sglebius 122159979Sglebius/* Internal helper functions. */ 123159979Sglebiusstatic int ng_tag_setdata_in(hook_p hook, const struct ng_tag_hookin *hp); 124159979Sglebiusstatic int ng_tag_setdata_out(hook_p hook, const struct ng_tag_hookout *hp); 125159979Sglebius 126159979Sglebius/* Parse types for the field 'tag_data' in structs ng_tag_hookin and out. */ 127159979Sglebiusstatic int 128159979Sglebiusng_tag_hookinary_getLength(const struct ng_parse_type *type, 129159979Sglebius const u_char *start, const u_char *buf) 130159979Sglebius{ 131159979Sglebius const struct ng_tag_hookin *hp; 132159979Sglebius 133159979Sglebius hp = (const struct ng_tag_hookin *) 134159979Sglebius (buf - offsetof(struct ng_tag_hookin, tag_data)); 135159979Sglebius return (hp->tag_len); 136159979Sglebius} 137159979Sglebius 138159979Sglebiusstatic int 139159979Sglebiusng_tag_hookoutary_getLength(const struct ng_parse_type *type, 140159979Sglebius const u_char *start, const u_char *buf) 141159979Sglebius{ 142159979Sglebius const struct ng_tag_hookout *hp; 143159979Sglebius 144159979Sglebius hp = (const struct ng_tag_hookout *) 145159979Sglebius (buf - offsetof(struct ng_tag_hookout, tag_data)); 146159979Sglebius return (hp->tag_len); 147159979Sglebius} 148159979Sglebius 149159979Sglebiusstatic const struct ng_parse_type ng_tag_hookinary_type = { 150159979Sglebius &ng_parse_bytearray_type, 151159979Sglebius &ng_tag_hookinary_getLength 152159979Sglebius}; 153159979Sglebius 154159979Sglebiusstatic const struct ng_parse_type ng_tag_hookoutary_type = { 155159979Sglebius &ng_parse_bytearray_type, 156159979Sglebius &ng_tag_hookoutary_getLength 157159979Sglebius}; 158159979Sglebius 159159979Sglebius/* Parse type for struct ng_tag_hookin. */ 160159979Sglebiusstatic const struct ng_parse_struct_field ng_tag_hookin_type_fields[] 161159979Sglebius = NG_TAG_HOOKIN_TYPE_INFO(&ng_tag_hookinary_type); 162159979Sglebiusstatic const struct ng_parse_type ng_tag_hookin_type = { 163159979Sglebius &ng_parse_struct_type, 164159979Sglebius &ng_tag_hookin_type_fields 165159979Sglebius}; 166159979Sglebius 167159979Sglebius/* Parse type for struct ng_tag_hookout. */ 168159979Sglebiusstatic const struct ng_parse_struct_field ng_tag_hookout_type_fields[] 169159979Sglebius = NG_TAG_HOOKOUT_TYPE_INFO(&ng_tag_hookoutary_type); 170159979Sglebiusstatic const struct ng_parse_type ng_tag_hookout_type = { 171159979Sglebius &ng_parse_struct_type, 172159979Sglebius &ng_tag_hookout_type_fields 173159979Sglebius}; 174159979Sglebius 175159979Sglebius#ifdef NG_TAG_DEBUG 176159979Sglebius/* Parse type for struct ng_tag_hookstat. */ 177159979Sglebiusstatic const struct ng_parse_struct_field ng_tag_hookstat_type_fields[] 178159979Sglebius = NG_TAG_HOOKSTAT_TYPE_INFO; 179159979Sglebiusstatic const struct ng_parse_type ng_tag_hookstat_type = { 180159979Sglebius &ng_parse_struct_type, 181159979Sglebius &ng_tag_hookstat_type_fields 182159979Sglebius}; 183159979Sglebius#endif 184159979Sglebius 185159979Sglebius/* List of commands and how to convert arguments to/from ASCII. */ 186159979Sglebiusstatic const struct ng_cmdlist ng_tag_cmdlist[] = { 187159979Sglebius { 188159979Sglebius NGM_TAG_COOKIE, 189159979Sglebius NGM_TAG_SET_HOOKIN, 190159979Sglebius "sethookin", 191159979Sglebius &ng_tag_hookin_type, 192159979Sglebius NULL 193159979Sglebius }, 194159979Sglebius { 195159979Sglebius NGM_TAG_COOKIE, 196159979Sglebius NGM_TAG_GET_HOOKIN, 197159979Sglebius "gethookin", 198159979Sglebius &ng_parse_hookbuf_type, 199159979Sglebius &ng_tag_hookin_type 200159979Sglebius }, 201159979Sglebius { 202159979Sglebius NGM_TAG_COOKIE, 203159979Sglebius NGM_TAG_SET_HOOKOUT, 204159979Sglebius "sethookout", 205159979Sglebius &ng_tag_hookout_type, 206159979Sglebius NULL 207159979Sglebius }, 208159979Sglebius { 209159979Sglebius NGM_TAG_COOKIE, 210159979Sglebius NGM_TAG_GET_HOOKOUT, 211159979Sglebius "gethookout", 212159979Sglebius &ng_parse_hookbuf_type, 213159979Sglebius &ng_tag_hookout_type 214159979Sglebius }, 215159979Sglebius#ifdef NG_TAG_DEBUG 216159979Sglebius { 217159979Sglebius NGM_TAG_COOKIE, 218159979Sglebius NGM_TAG_GET_STATS, 219159979Sglebius "getstats", 220159979Sglebius &ng_parse_hookbuf_type, 221159979Sglebius &ng_tag_hookstat_type 222159979Sglebius }, 223159979Sglebius { 224159979Sglebius NGM_TAG_COOKIE, 225159979Sglebius NGM_TAG_CLR_STATS, 226159979Sglebius "clrstats", 227159979Sglebius &ng_parse_hookbuf_type, 228159979Sglebius NULL 229159979Sglebius }, 230159979Sglebius { 231159979Sglebius NGM_TAG_COOKIE, 232159979Sglebius NGM_TAG_GETCLR_STATS, 233159979Sglebius "getclrstats", 234159979Sglebius &ng_parse_hookbuf_type, 235159979Sglebius &ng_tag_hookstat_type 236159979Sglebius }, 237159979Sglebius#endif 238159979Sglebius { 0 } 239159979Sglebius}; 240159979Sglebius 241159979Sglebius/* Netgraph type descriptor. */ 242159979Sglebiusstatic struct ng_type typestruct = { 243159979Sglebius .version = NG_ABI_VERSION, 244159979Sglebius .name = NG_TAG_NODE_TYPE, 245159979Sglebius .constructor = ng_tag_constructor, 246159979Sglebius .rcvmsg = ng_tag_rcvmsg, 247159979Sglebius .shutdown = ng_tag_shutdown, 248159979Sglebius .newhook = ng_tag_newhook, 249159979Sglebius .rcvdata = ng_tag_rcvdata, 250159979Sglebius .disconnect = ng_tag_disconnect, 251159979Sglebius .cmdlist = ng_tag_cmdlist, 252159979Sglebius}; 253159979SglebiusNETGRAPH_INIT(tag, &typestruct); 254159979Sglebius 255159979Sglebius/* 256159979Sglebius * This are default API structures (initialized to zeroes) which are 257159979Sglebius * returned in response to GET* messages when no configuration was made. 258159979Sglebius * One could ask why to have this structures at all when we have 259159979Sglebius * ng_tag_hookinfo initialized to zero and don't need in and out structures 260159979Sglebius * at all to operate. Unfortunatelly, we have to return thisHook field 261159979Sglebius * in response to messages so the fastest and simpliest way is to have 262159979Sglebius * this default structures and initialize thisHook once at hook creation 263159979Sglebius * rather than to do it on every response. 264159979Sglebius */ 265159979Sglebius 266159979Sglebius/* Default tag values for a hook that matches nothing. */ 267159979Sglebiusstatic const struct ng_tag_hookin ng_tag_default_in = { 268159979Sglebius { '\0' }, /* to be filled in at hook creation time */ 269159979Sglebius { '\0' }, 270159979Sglebius { '\0' }, 271159979Sglebius 0, 272159979Sglebius 0, 273159979Sglebius 0, 274159979Sglebius 0 275159979Sglebius}; 276159979Sglebius 277159979Sglebius/* Default tag values for a hook that adds nothing */ 278159979Sglebiusstatic const struct ng_tag_hookout ng_tag_default_out = { 279159979Sglebius { '\0' }, /* to be filled in at hook creation time */ 280159979Sglebius 0, 281159979Sglebius 0, 282159979Sglebius 0 283159979Sglebius}; 284159979Sglebius 285159979Sglebius/* 286159979Sglebius * Node constructor. 287159979Sglebius * 288159979Sglebius * We don't keep any per-node private data - we do it on per-hook basis. 289159979Sglebius */ 290159979Sglebiusstatic int 291159979Sglebiusng_tag_constructor(node_p node) 292159979Sglebius{ 293159979Sglebius return (0); 294159979Sglebius} 295159979Sglebius 296159979Sglebius/* 297159979Sglebius * Add a hook. 298159979Sglebius */ 299159979Sglebiusstatic int 300159979Sglebiusng_tag_newhook(node_p node, hook_p hook, const char *name) 301159979Sglebius{ 302159979Sglebius hinfo_p hip; 303159979Sglebius int error; 304159979Sglebius 305159979Sglebius /* Create hook private structure. */ 306230272Sglebius hip = malloc(sizeof(*hip), M_NETGRAPH_TAG, M_NOWAIT | M_ZERO); 307230272Sglebius if (hip == NULL) 308230272Sglebius return (ENOMEM); 309159979Sglebius NG_HOOK_SET_PRIVATE(hook, hip); 310159979Sglebius 311159979Sglebius /* 312159979Sglebius * After M_ZERO both in and out hook pointers are set to NULL, 313159979Sglebius * as well as all members and pointers to in and out API 314159979Sglebius * structures, so we need to set explicitly only thisHook field 315159979Sglebius * in that structures (after allocating them, of course). 316159979Sglebius */ 317159979Sglebius 318159979Sglebius /* Attach the default IN data. */ 319159979Sglebius if ((error = ng_tag_setdata_in(hook, &ng_tag_default_in)) != 0) { 320184205Sdes free(hip, M_NETGRAPH_TAG); 321159979Sglebius return (error); 322159979Sglebius } 323159979Sglebius 324159979Sglebius /* Attach the default OUT data. */ 325159979Sglebius if ((error = ng_tag_setdata_out(hook, &ng_tag_default_out)) != 0) { 326184205Sdes free(hip, M_NETGRAPH_TAG); 327159979Sglebius return (error); 328159979Sglebius } 329159979Sglebius 330159979Sglebius /* 331159979Sglebius * Set hook name. This is done only once at hook creation time 332159979Sglebius * since hook name can't change, rather than to do it on every 333159979Sglebius * response to messages requesting API structures with data who 334159979Sglebius * we are etc. 335159979Sglebius */ 336159979Sglebius strncpy(hip->in->thisHook, name, sizeof(hip->in->thisHook) - 1); 337159979Sglebius hip->in->thisHook[sizeof(hip->in->thisHook) - 1] = '\0'; 338159979Sglebius strncpy(hip->out->thisHook, name, sizeof(hip->out->thisHook) - 1); 339159979Sglebius hip->out->thisHook[sizeof(hip->out->thisHook) - 1] = '\0'; 340159979Sglebius return (0); 341159979Sglebius} 342159979Sglebius 343159979Sglebius/* 344159979Sglebius * Receive a control message. 345159979Sglebius */ 346159979Sglebiusstatic int 347159979Sglebiusng_tag_rcvmsg(node_p node, item_p item, hook_p lasthook) 348159979Sglebius{ 349159979Sglebius struct ng_mesg *msg; 350159979Sglebius struct ng_mesg *resp = NULL; 351159979Sglebius int error = 0; 352159979Sglebius 353159979Sglebius NGI_GET_MSG(item, msg); 354159979Sglebius switch (msg->header.typecookie) { 355159979Sglebius case NGM_TAG_COOKIE: 356159979Sglebius switch (msg->header.cmd) { 357159979Sglebius case NGM_TAG_SET_HOOKIN: 358159979Sglebius { 359159979Sglebius struct ng_tag_hookin *const 360159979Sglebius hp = (struct ng_tag_hookin *)msg->data; 361159979Sglebius hook_p hook; 362159979Sglebius 363159979Sglebius /* Sanity check. */ 364159979Sglebius if (msg->header.arglen < sizeof(*hp) 365159979Sglebius || msg->header.arglen != 366159979Sglebius NG_TAG_HOOKIN_SIZE(hp->tag_len)) 367159979Sglebius ERROUT(EINVAL); 368159979Sglebius 369159979Sglebius /* Find hook. */ 370159979Sglebius if ((hook = ng_findhook(node, hp->thisHook)) == NULL) 371159979Sglebius ERROUT(ENOENT); 372159979Sglebius 373159979Sglebius /* Set new tag values. */ 374159979Sglebius if ((error = ng_tag_setdata_in(hook, hp)) != 0) 375159979Sglebius ERROUT(error); 376159979Sglebius break; 377159979Sglebius } 378159979Sglebius 379159979Sglebius case NGM_TAG_SET_HOOKOUT: 380159979Sglebius { 381159979Sglebius struct ng_tag_hookout *const 382159979Sglebius hp = (struct ng_tag_hookout *)msg->data; 383159979Sglebius hook_p hook; 384159979Sglebius 385159979Sglebius /* Sanity check. */ 386159979Sglebius if (msg->header.arglen < sizeof(*hp) 387159979Sglebius || msg->header.arglen != 388159979Sglebius NG_TAG_HOOKOUT_SIZE(hp->tag_len)) 389159979Sglebius ERROUT(EINVAL); 390159979Sglebius 391159979Sglebius /* Find hook. */ 392159979Sglebius if ((hook = ng_findhook(node, hp->thisHook)) == NULL) 393159979Sglebius ERROUT(ENOENT); 394159979Sglebius 395159979Sglebius /* Set new tag values. */ 396159979Sglebius if ((error = ng_tag_setdata_out(hook, hp)) != 0) 397159979Sglebius ERROUT(error); 398159979Sglebius break; 399159979Sglebius } 400159979Sglebius 401159979Sglebius case NGM_TAG_GET_HOOKIN: 402159979Sglebius { 403159979Sglebius struct ng_tag_hookin *hp; 404159979Sglebius hook_p hook; 405159979Sglebius 406159979Sglebius /* Sanity check. */ 407159979Sglebius if (msg->header.arglen == 0) 408159979Sglebius ERROUT(EINVAL); 409159979Sglebius msg->data[msg->header.arglen - 1] = '\0'; 410159979Sglebius 411159979Sglebius /* Find hook. */ 412159979Sglebius if ((hook = ng_findhook(node, msg->data)) == NULL) 413159979Sglebius ERROUT(ENOENT); 414159979Sglebius 415159979Sglebius /* Build response. */ 416159979Sglebius hp = ((hinfo_p)NG_HOOK_PRIVATE(hook))->in; 417159979Sglebius NG_MKRESPONSE(resp, msg, 418159979Sglebius NG_TAG_HOOKIN_SIZE(hp->tag_len), M_WAITOK); 419159979Sglebius /* M_WAITOK can't return NULL. */ 420159979Sglebius bcopy(hp, resp->data, 421159979Sglebius NG_TAG_HOOKIN_SIZE(hp->tag_len)); 422159979Sglebius break; 423159979Sglebius } 424159979Sglebius 425159979Sglebius case NGM_TAG_GET_HOOKOUT: 426159979Sglebius { 427159979Sglebius struct ng_tag_hookout *hp; 428159979Sglebius hook_p hook; 429159979Sglebius 430159979Sglebius /* Sanity check. */ 431159979Sglebius if (msg->header.arglen == 0) 432159979Sglebius ERROUT(EINVAL); 433159979Sglebius msg->data[msg->header.arglen - 1] = '\0'; 434159979Sglebius 435159979Sglebius /* Find hook. */ 436159979Sglebius if ((hook = ng_findhook(node, msg->data)) == NULL) 437159979Sglebius ERROUT(ENOENT); 438159979Sglebius 439159979Sglebius /* Build response. */ 440159979Sglebius hp = ((hinfo_p)NG_HOOK_PRIVATE(hook))->out; 441159979Sglebius NG_MKRESPONSE(resp, msg, 442159979Sglebius NG_TAG_HOOKOUT_SIZE(hp->tag_len), M_WAITOK); 443159979Sglebius /* M_WAITOK can't return NULL. */ 444159979Sglebius bcopy(hp, resp->data, 445159979Sglebius NG_TAG_HOOKOUT_SIZE(hp->tag_len)); 446159979Sglebius break; 447159979Sglebius } 448159979Sglebius 449159979Sglebius#ifdef NG_TAG_DEBUG 450159979Sglebius case NGM_TAG_GET_STATS: 451159979Sglebius case NGM_TAG_CLR_STATS: 452159979Sglebius case NGM_TAG_GETCLR_STATS: 453159979Sglebius { 454159979Sglebius struct ng_tag_hookstat *stats; 455159979Sglebius hook_p hook; 456159979Sglebius 457159979Sglebius /* Sanity check. */ 458159979Sglebius if (msg->header.arglen == 0) 459159979Sglebius ERROUT(EINVAL); 460159979Sglebius msg->data[msg->header.arglen - 1] = '\0'; 461159979Sglebius 462159979Sglebius /* Find hook. */ 463159979Sglebius if ((hook = ng_findhook(node, msg->data)) == NULL) 464159979Sglebius ERROUT(ENOENT); 465159979Sglebius stats = &((hinfo_p)NG_HOOK_PRIVATE(hook))->stats; 466159979Sglebius 467159979Sglebius /* Build response (if desired). */ 468159979Sglebius if (msg->header.cmd != NGM_TAG_CLR_STATS) { 469159979Sglebius NG_MKRESPONSE(resp, 470159979Sglebius msg, sizeof(*stats), M_WAITOK); 471159979Sglebius /* M_WAITOK can't return NULL. */ 472159979Sglebius bcopy(stats, resp->data, sizeof(*stats)); 473159979Sglebius } 474159979Sglebius 475159979Sglebius /* Clear stats (if desired). */ 476159979Sglebius if (msg->header.cmd != NGM_TAG_GET_STATS) 477159979Sglebius bzero(stats, sizeof(*stats)); 478159979Sglebius break; 479159979Sglebius } 480159979Sglebius#endif /* NG_TAG_DEBUG */ 481159979Sglebius 482159979Sglebius default: 483159979Sglebius error = EINVAL; 484159979Sglebius break; 485159979Sglebius } 486159979Sglebius break; 487159979Sglebius default: 488159979Sglebius error = EINVAL; 489159979Sglebius break; 490159979Sglebius } 491159979Sglebiusdone: 492159979Sglebius NG_RESPOND_MSG(error, node, item, resp); 493159979Sglebius NG_FREE_MSG(msg); 494159979Sglebius return (error); 495159979Sglebius} 496159979Sglebius 497159979Sglebius/* 498159979Sglebius * Receive data on a hook. 499159979Sglebius * 500159979Sglebius * Apply the filter, and then drop or forward packet as appropriate. 501159979Sglebius */ 502159979Sglebiusstatic int 503159979Sglebiusng_tag_rcvdata(hook_p hook, item_p item) 504159979Sglebius{ 505159979Sglebius struct mbuf *m; 506159979Sglebius struct m_tag *tag = NULL; 507159979Sglebius const hinfo_p hip = NG_HOOK_PRIVATE(hook); 508159979Sglebius uint16_t type, tag_len; 509159979Sglebius uint32_t cookie; 510159979Sglebius hinfo_p dhip; 511159979Sglebius hook_p dest; 512159979Sglebius int totlen; 513159979Sglebius int found = 0, error = 0; 514159979Sglebius 515159979Sglebius m = NGI_M(item); /* 'item' still owns it.. we are peeking */ 516159979Sglebius totlen = m->m_pkthdr.len; 517159979Sglebius 518159979Sglebius#ifdef NG_TAG_DEBUG 519159979Sglebius hip->stats.recvFrames++; 520159979Sglebius hip->stats.recvOctets += totlen; 521159979Sglebius#endif 522159979Sglebius 523159979Sglebius /* Looking up incoming tag. */ 524159979Sglebius cookie = hip->in_tag_cookie; 525159979Sglebius type = hip->in_tag_id; 526159979Sglebius tag_len = hip->in_tag_len; 527159979Sglebius 528159979Sglebius /* 529159979Sglebius * We treat case of all zeroes specially (that is, cookie and 530159979Sglebius * type are equal to zero), as we assume that such tag 531159979Sglebius * can never occur in the wild. So we don't waste time trying 532159979Sglebius * to find such tag (for example, these are zeroes after hook 533159979Sglebius * creation in default structures). 534159979Sglebius */ 535159979Sglebius if ((cookie != 0) || (type != 0)) { 536159979Sglebius tag = m_tag_locate(m, cookie, type, NULL); 537159979Sglebius while (tag != NULL) { 538159979Sglebius if (memcmp((void *)(tag + 1), 539159979Sglebius hip->in_tag_data, tag_len) == 0) { 540159979Sglebius found = 1; 541159979Sglebius break; 542159979Sglebius } 543159979Sglebius tag = m_tag_locate(m, cookie, type, tag); 544159979Sglebius } 545159979Sglebius } 546159979Sglebius 547159979Sglebius /* See if we got a match and find destination hook. */ 548159979Sglebius if (found) { 549159979Sglebius#ifdef NG_TAG_DEBUG 550159979Sglebius hip->stats.recvMatchFrames++; 551159979Sglebius hip->stats.recvMatchOctets += totlen; 552159979Sglebius#endif 553159979Sglebius if (hip->strip) 554159979Sglebius m_tag_delete(m, tag); 555159979Sglebius dest = hip->hi_match; 556159979Sglebius } else 557159979Sglebius dest = hip->hi_nonmatch; 558159979Sglebius if (dest == NULL) { 559159979Sglebius NG_FREE_ITEM(item); 560159979Sglebius return (0); 561159979Sglebius } 562159979Sglebius 563159979Sglebius /* Deliver frame out destination hook. */ 564159979Sglebius dhip = NG_HOOK_PRIVATE(dest); 565159979Sglebius 566159979Sglebius#ifdef NG_TAG_DEBUG 567159979Sglebius dhip->stats.xmitOctets += totlen; 568159979Sglebius dhip->stats.xmitFrames++; 569159979Sglebius#endif 570159979Sglebius 571159979Sglebius cookie = dhip->out_tag_cookie; 572159979Sglebius type = dhip->out_tag_id; 573159979Sglebius tag_len = dhip->out_tag_len; 574159979Sglebius 575159979Sglebius if ((cookie != 0) || (type != 0)) { 576159979Sglebius tag = m_tag_alloc(cookie, type, tag_len, M_NOWAIT); 577159979Sglebius /* XXX may be free the mbuf if tag allocation failed? */ 578159979Sglebius if (tag != NULL) { 579159979Sglebius if (tag_len != 0) { 580159979Sglebius /* copy tag data to its place */ 581159979Sglebius memcpy((void *)(tag + 1), 582159979Sglebius dhip->out_tag_data, tag_len); 583159979Sglebius } 584159979Sglebius m_tag_prepend(m, tag); 585159979Sglebius } 586159979Sglebius } 587159979Sglebius 588159979Sglebius NG_FWD_ITEM_HOOK(error, item, dest); 589159979Sglebius return (error); 590159979Sglebius} 591159979Sglebius 592159979Sglebius/* 593159979Sglebius * Shutdown processing. 594159979Sglebius */ 595159979Sglebiusstatic int 596159979Sglebiusng_tag_shutdown(node_p node) 597159979Sglebius{ 598159979Sglebius NG_NODE_UNREF(node); 599159979Sglebius return (0); 600159979Sglebius} 601159979Sglebius 602159979Sglebius/* 603159979Sglebius * Hook disconnection. 604159979Sglebius * 605159979Sglebius * We must check all hooks, since they may reference this one. 606159979Sglebius */ 607159979Sglebiusstatic int 608159979Sglebiusng_tag_disconnect(hook_p hook) 609159979Sglebius{ 610159979Sglebius const hinfo_p hip = NG_HOOK_PRIVATE(hook); 611159979Sglebius node_p node = NG_HOOK_NODE(hook); 612159979Sglebius hook_p hook2; 613159979Sglebius 614159979Sglebius KASSERT(hip != NULL, ("%s: null info", __func__)); 615159979Sglebius 616159979Sglebius LIST_FOREACH(hook2, &node->nd_hooks, hk_hooks) { 617159979Sglebius hinfo_p priv = NG_HOOK_PRIVATE(hook2); 618159979Sglebius 619159979Sglebius if (priv->hi_match == hook) 620159979Sglebius priv->hi_match = NULL; 621159979Sglebius if (priv->hi_nonmatch == hook) 622159979Sglebius priv->hi_nonmatch = NULL; 623159979Sglebius } 624159979Sglebius 625184205Sdes free(hip->in, M_NETGRAPH_TAG); 626184205Sdes free(hip->out, M_NETGRAPH_TAG); 627184205Sdes free(hip, M_NETGRAPH_TAG); 628159979Sglebius NG_HOOK_SET_PRIVATE(hook, NULL); /* for good measure */ 629159979Sglebius if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) && 630159979Sglebius (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) { 631159979Sglebius ng_rmnode_self(NG_HOOK_NODE(hook)); 632159979Sglebius } 633159979Sglebius return (0); 634159979Sglebius} 635159979Sglebius 636159979Sglebius/************************************************************************ 637159979Sglebius HELPER STUFF 638159979Sglebius ************************************************************************/ 639159979Sglebius 640159979Sglebius/* 641159979Sglebius * Set the IN tag values associated with a hook. 642159979Sglebius */ 643159979Sglebiusstatic int 644159979Sglebiusng_tag_setdata_in(hook_p hook, const struct ng_tag_hookin *hp0) 645159979Sglebius{ 646159979Sglebius const hinfo_p hip = NG_HOOK_PRIVATE(hook); 647159979Sglebius struct ng_tag_hookin *hp; 648159979Sglebius int size; 649159979Sglebius 650159979Sglebius /* Make a copy of the tag values and data. */ 651159979Sglebius size = NG_TAG_HOOKIN_SIZE(hp0->tag_len); 652184205Sdes hp = malloc(size, M_NETGRAPH_TAG, M_WAITOK); 653159979Sglebius /* M_WAITOK can't return NULL. */ 654159979Sglebius bcopy(hp0, hp, size); 655159979Sglebius 656159979Sglebius /* Free previous tag, if any, and assign new one. */ 657159979Sglebius if (hip->in != NULL) 658184205Sdes free(hip->in, M_NETGRAPH_TAG); 659159979Sglebius hip->in = hp; 660159979Sglebius 661159979Sglebius /* 662159979Sglebius * Resolve hook names to pointers. 663159979Sglebius * 664159979Sglebius * As ng_findhook() is expensive operation to do it on every packet 665159979Sglebius * after tag matching check, we do it here and use resolved pointers 666159979Sglebius * where appropriate. 667159979Sglebius * 668159979Sglebius * XXX The drawback is that user can configure a hook to use 669159979Sglebius * ifMatch/ifNotMatch hooks that do not yet exist and will be added 670159979Sglebius * by user later, so that resolved pointers will be NULL even 671159979Sglebius * if the hook already exists, causing node to drop packets and 672159979Sglebius * user to report bugs. We could do check for this situation on 673159979Sglebius * every hook creation with pointers correction, but that involves 674159979Sglebius * re-resolving for all pointers in all hooks, up to O(n^2) operations, 675159979Sglebius * so we better document this in man page for user not to do 676159979Sglebius * configuration before creating all hooks. 677159979Sglebius */ 678159979Sglebius hip->hi_match = ng_findhook(NG_HOOK_NODE(hook), hip->in->ifMatch); 679159979Sglebius hip->hi_nonmatch = ng_findhook(NG_HOOK_NODE(hook), hip->in->ifNotMatch); 680159979Sglebius 681159979Sglebius /* Fill internal values from API structures. */ 682159979Sglebius hip->in_tag_cookie = hip->in->tag_cookie; 683159979Sglebius hip->in_tag_id = hip->in->tag_id; 684159979Sglebius hip->in_tag_len = hip->in->tag_len; 685159979Sglebius hip->strip = hip->in->strip; 686159979Sglebius hip->in_tag_data = (void*)(hip->in->tag_data); 687159979Sglebius return (0); 688159979Sglebius} 689159979Sglebius 690159979Sglebius/* 691159979Sglebius * Set the OUT tag values associated with a hook. 692159979Sglebius */ 693159979Sglebiusstatic int 694159979Sglebiusng_tag_setdata_out(hook_p hook, const struct ng_tag_hookout *hp0) 695159979Sglebius{ 696159979Sglebius const hinfo_p hip = NG_HOOK_PRIVATE(hook); 697159979Sglebius struct ng_tag_hookout *hp; 698159979Sglebius int size; 699159979Sglebius 700159979Sglebius /* Make a copy of the tag values and data. */ 701159979Sglebius size = NG_TAG_HOOKOUT_SIZE(hp0->tag_len); 702184205Sdes hp = malloc(size, M_NETGRAPH_TAG, M_WAITOK); 703159979Sglebius /* M_WAITOK can't return NULL. */ 704159979Sglebius bcopy(hp0, hp, size); 705159979Sglebius 706159979Sglebius /* Free previous tag, if any, and assign new one. */ 707159979Sglebius if (hip->out != NULL) 708184205Sdes free(hip->out, M_NETGRAPH_TAG); 709159979Sglebius hip->out = hp; 710159979Sglebius 711159979Sglebius /* Fill internal values from API structures. */ 712159979Sglebius hip->out_tag_cookie = hip->out->tag_cookie; 713159979Sglebius hip->out_tag_id = hip->out->tag_id; 714159979Sglebius hip->out_tag_len = hip->out->tag_len; 715159979Sglebius hip->out_tag_data = (void*)(hip->out->tag_data); 716159979Sglebius return (0); 717159979Sglebius} 718159979Sglebius 719