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$ 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 72249132Smavstatic 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. */ 306184205Sdes hip = malloc(sizeof(*hip), M_NETGRAPH_TAG, M_WAITOK | M_ZERO); 307159979Sglebius /* M_WAITOK can't return NULL. */ 308159979Sglebius NG_HOOK_SET_PRIVATE(hook, hip); 309159979Sglebius 310159979Sglebius /* 311159979Sglebius * After M_ZERO both in and out hook pointers are set to NULL, 312159979Sglebius * as well as all members and pointers to in and out API 313159979Sglebius * structures, so we need to set explicitly only thisHook field 314159979Sglebius * in that structures (after allocating them, of course). 315159979Sglebius */ 316159979Sglebius 317159979Sglebius /* Attach the default IN data. */ 318159979Sglebius if ((error = ng_tag_setdata_in(hook, &ng_tag_default_in)) != 0) { 319184205Sdes free(hip, M_NETGRAPH_TAG); 320159979Sglebius return (error); 321159979Sglebius } 322159979Sglebius 323159979Sglebius /* Attach the default OUT data. */ 324159979Sglebius if ((error = ng_tag_setdata_out(hook, &ng_tag_default_out)) != 0) { 325184205Sdes free(hip, M_NETGRAPH_TAG); 326159979Sglebius return (error); 327159979Sglebius } 328159979Sglebius 329159979Sglebius /* 330159979Sglebius * Set hook name. This is done only once at hook creation time 331159979Sglebius * since hook name can't change, rather than to do it on every 332159979Sglebius * response to messages requesting API structures with data who 333159979Sglebius * we are etc. 334159979Sglebius */ 335159979Sglebius strncpy(hip->in->thisHook, name, sizeof(hip->in->thisHook) - 1); 336159979Sglebius hip->in->thisHook[sizeof(hip->in->thisHook) - 1] = '\0'; 337159979Sglebius strncpy(hip->out->thisHook, name, sizeof(hip->out->thisHook) - 1); 338159979Sglebius hip->out->thisHook[sizeof(hip->out->thisHook) - 1] = '\0'; 339159979Sglebius return (0); 340159979Sglebius} 341159979Sglebius 342159979Sglebius/* 343159979Sglebius * Receive a control message. 344159979Sglebius */ 345159979Sglebiusstatic int 346159979Sglebiusng_tag_rcvmsg(node_p node, item_p item, hook_p lasthook) 347159979Sglebius{ 348159979Sglebius struct ng_mesg *msg; 349159979Sglebius struct ng_mesg *resp = NULL; 350159979Sglebius int error = 0; 351159979Sglebius 352159979Sglebius NGI_GET_MSG(item, msg); 353159979Sglebius switch (msg->header.typecookie) { 354159979Sglebius case NGM_TAG_COOKIE: 355159979Sglebius switch (msg->header.cmd) { 356159979Sglebius case NGM_TAG_SET_HOOKIN: 357159979Sglebius { 358159979Sglebius struct ng_tag_hookin *const 359159979Sglebius hp = (struct ng_tag_hookin *)msg->data; 360159979Sglebius hook_p hook; 361159979Sglebius 362159979Sglebius /* Sanity check. */ 363159979Sglebius if (msg->header.arglen < sizeof(*hp) 364159979Sglebius || msg->header.arglen != 365159979Sglebius NG_TAG_HOOKIN_SIZE(hp->tag_len)) 366159979Sglebius ERROUT(EINVAL); 367159979Sglebius 368159979Sglebius /* Find hook. */ 369159979Sglebius if ((hook = ng_findhook(node, hp->thisHook)) == NULL) 370159979Sglebius ERROUT(ENOENT); 371159979Sglebius 372159979Sglebius /* Set new tag values. */ 373159979Sglebius if ((error = ng_tag_setdata_in(hook, hp)) != 0) 374159979Sglebius ERROUT(error); 375159979Sglebius break; 376159979Sglebius } 377159979Sglebius 378159979Sglebius case NGM_TAG_SET_HOOKOUT: 379159979Sglebius { 380159979Sglebius struct ng_tag_hookout *const 381159979Sglebius hp = (struct ng_tag_hookout *)msg->data; 382159979Sglebius hook_p hook; 383159979Sglebius 384159979Sglebius /* Sanity check. */ 385159979Sglebius if (msg->header.arglen < sizeof(*hp) 386159979Sglebius || msg->header.arglen != 387159979Sglebius NG_TAG_HOOKOUT_SIZE(hp->tag_len)) 388159979Sglebius ERROUT(EINVAL); 389159979Sglebius 390159979Sglebius /* Find hook. */ 391159979Sglebius if ((hook = ng_findhook(node, hp->thisHook)) == NULL) 392159979Sglebius ERROUT(ENOENT); 393159979Sglebius 394159979Sglebius /* Set new tag values. */ 395159979Sglebius if ((error = ng_tag_setdata_out(hook, hp)) != 0) 396159979Sglebius ERROUT(error); 397159979Sglebius break; 398159979Sglebius } 399159979Sglebius 400159979Sglebius case NGM_TAG_GET_HOOKIN: 401159979Sglebius { 402159979Sglebius struct ng_tag_hookin *hp; 403159979Sglebius hook_p hook; 404159979Sglebius 405159979Sglebius /* Sanity check. */ 406159979Sglebius if (msg->header.arglen == 0) 407159979Sglebius ERROUT(EINVAL); 408159979Sglebius msg->data[msg->header.arglen - 1] = '\0'; 409159979Sglebius 410159979Sglebius /* Find hook. */ 411159979Sglebius if ((hook = ng_findhook(node, msg->data)) == NULL) 412159979Sglebius ERROUT(ENOENT); 413159979Sglebius 414159979Sglebius /* Build response. */ 415159979Sglebius hp = ((hinfo_p)NG_HOOK_PRIVATE(hook))->in; 416159979Sglebius NG_MKRESPONSE(resp, msg, 417159979Sglebius NG_TAG_HOOKIN_SIZE(hp->tag_len), M_WAITOK); 418159979Sglebius /* M_WAITOK can't return NULL. */ 419159979Sglebius bcopy(hp, resp->data, 420159979Sglebius NG_TAG_HOOKIN_SIZE(hp->tag_len)); 421159979Sglebius break; 422159979Sglebius } 423159979Sglebius 424159979Sglebius case NGM_TAG_GET_HOOKOUT: 425159979Sglebius { 426159979Sglebius struct ng_tag_hookout *hp; 427159979Sglebius hook_p hook; 428159979Sglebius 429159979Sglebius /* Sanity check. */ 430159979Sglebius if (msg->header.arglen == 0) 431159979Sglebius ERROUT(EINVAL); 432159979Sglebius msg->data[msg->header.arglen - 1] = '\0'; 433159979Sglebius 434159979Sglebius /* Find hook. */ 435159979Sglebius if ((hook = ng_findhook(node, msg->data)) == NULL) 436159979Sglebius ERROUT(ENOENT); 437159979Sglebius 438159979Sglebius /* Build response. */ 439159979Sglebius hp = ((hinfo_p)NG_HOOK_PRIVATE(hook))->out; 440159979Sglebius NG_MKRESPONSE(resp, msg, 441159979Sglebius NG_TAG_HOOKOUT_SIZE(hp->tag_len), M_WAITOK); 442159979Sglebius /* M_WAITOK can't return NULL. */ 443159979Sglebius bcopy(hp, resp->data, 444159979Sglebius NG_TAG_HOOKOUT_SIZE(hp->tag_len)); 445159979Sglebius break; 446159979Sglebius } 447159979Sglebius 448159979Sglebius#ifdef NG_TAG_DEBUG 449159979Sglebius case NGM_TAG_GET_STATS: 450159979Sglebius case NGM_TAG_CLR_STATS: 451159979Sglebius case NGM_TAG_GETCLR_STATS: 452159979Sglebius { 453159979Sglebius struct ng_tag_hookstat *stats; 454159979Sglebius hook_p hook; 455159979Sglebius 456159979Sglebius /* Sanity check. */ 457159979Sglebius if (msg->header.arglen == 0) 458159979Sglebius ERROUT(EINVAL); 459159979Sglebius msg->data[msg->header.arglen - 1] = '\0'; 460159979Sglebius 461159979Sglebius /* Find hook. */ 462159979Sglebius if ((hook = ng_findhook(node, msg->data)) == NULL) 463159979Sglebius ERROUT(ENOENT); 464159979Sglebius stats = &((hinfo_p)NG_HOOK_PRIVATE(hook))->stats; 465159979Sglebius 466159979Sglebius /* Build response (if desired). */ 467159979Sglebius if (msg->header.cmd != NGM_TAG_CLR_STATS) { 468159979Sglebius NG_MKRESPONSE(resp, 469159979Sglebius msg, sizeof(*stats), M_WAITOK); 470159979Sglebius /* M_WAITOK can't return NULL. */ 471159979Sglebius bcopy(stats, resp->data, sizeof(*stats)); 472159979Sglebius } 473159979Sglebius 474159979Sglebius /* Clear stats (if desired). */ 475159979Sglebius if (msg->header.cmd != NGM_TAG_GET_STATS) 476159979Sglebius bzero(stats, sizeof(*stats)); 477159979Sglebius break; 478159979Sglebius } 479159979Sglebius#endif /* NG_TAG_DEBUG */ 480159979Sglebius 481159979Sglebius default: 482159979Sglebius error = EINVAL; 483159979Sglebius break; 484159979Sglebius } 485159979Sglebius break; 486159979Sglebius default: 487159979Sglebius error = EINVAL; 488159979Sglebius break; 489159979Sglebius } 490159979Sglebiusdone: 491159979Sglebius NG_RESPOND_MSG(error, node, item, resp); 492159979Sglebius NG_FREE_MSG(msg); 493159979Sglebius return (error); 494159979Sglebius} 495159979Sglebius 496159979Sglebius/* 497159979Sglebius * Receive data on a hook. 498159979Sglebius * 499159979Sglebius * Apply the filter, and then drop or forward packet as appropriate. 500159979Sglebius */ 501159979Sglebiusstatic int 502159979Sglebiusng_tag_rcvdata(hook_p hook, item_p item) 503159979Sglebius{ 504159979Sglebius struct mbuf *m; 505159979Sglebius struct m_tag *tag = NULL; 506159979Sglebius const hinfo_p hip = NG_HOOK_PRIVATE(hook); 507159979Sglebius uint16_t type, tag_len; 508159979Sglebius uint32_t cookie; 509159979Sglebius hinfo_p dhip; 510159979Sglebius hook_p dest; 511159979Sglebius int totlen; 512159979Sglebius int found = 0, error = 0; 513159979Sglebius 514159979Sglebius m = NGI_M(item); /* 'item' still owns it.. we are peeking */ 515159979Sglebius totlen = m->m_pkthdr.len; 516159979Sglebius 517159979Sglebius#ifdef NG_TAG_DEBUG 518159979Sglebius hip->stats.recvFrames++; 519159979Sglebius hip->stats.recvOctets += totlen; 520159979Sglebius#endif 521159979Sglebius 522159979Sglebius /* Looking up incoming tag. */ 523159979Sglebius cookie = hip->in_tag_cookie; 524159979Sglebius type = hip->in_tag_id; 525159979Sglebius tag_len = hip->in_tag_len; 526159979Sglebius 527159979Sglebius /* 528159979Sglebius * We treat case of all zeroes specially (that is, cookie and 529159979Sglebius * type are equal to zero), as we assume that such tag 530159979Sglebius * can never occur in the wild. So we don't waste time trying 531159979Sglebius * to find such tag (for example, these are zeroes after hook 532159979Sglebius * creation in default structures). 533159979Sglebius */ 534159979Sglebius if ((cookie != 0) || (type != 0)) { 535159979Sglebius tag = m_tag_locate(m, cookie, type, NULL); 536159979Sglebius while (tag != NULL) { 537159979Sglebius if (memcmp((void *)(tag + 1), 538159979Sglebius hip->in_tag_data, tag_len) == 0) { 539159979Sglebius found = 1; 540159979Sglebius break; 541159979Sglebius } 542159979Sglebius tag = m_tag_locate(m, cookie, type, tag); 543159979Sglebius } 544159979Sglebius } 545159979Sglebius 546159979Sglebius /* See if we got a match and find destination hook. */ 547159979Sglebius if (found) { 548159979Sglebius#ifdef NG_TAG_DEBUG 549159979Sglebius hip->stats.recvMatchFrames++; 550159979Sglebius hip->stats.recvMatchOctets += totlen; 551159979Sglebius#endif 552159979Sglebius if (hip->strip) 553159979Sglebius m_tag_delete(m, tag); 554159979Sglebius dest = hip->hi_match; 555159979Sglebius } else 556159979Sglebius dest = hip->hi_nonmatch; 557159979Sglebius if (dest == NULL) { 558159979Sglebius NG_FREE_ITEM(item); 559159979Sglebius return (0); 560159979Sglebius } 561159979Sglebius 562159979Sglebius /* Deliver frame out destination hook. */ 563159979Sglebius dhip = NG_HOOK_PRIVATE(dest); 564159979Sglebius 565159979Sglebius#ifdef NG_TAG_DEBUG 566159979Sglebius dhip->stats.xmitOctets += totlen; 567159979Sglebius dhip->stats.xmitFrames++; 568159979Sglebius#endif 569159979Sglebius 570159979Sglebius cookie = dhip->out_tag_cookie; 571159979Sglebius type = dhip->out_tag_id; 572159979Sglebius tag_len = dhip->out_tag_len; 573159979Sglebius 574159979Sglebius if ((cookie != 0) || (type != 0)) { 575159979Sglebius tag = m_tag_alloc(cookie, type, tag_len, M_NOWAIT); 576159979Sglebius /* XXX may be free the mbuf if tag allocation failed? */ 577159979Sglebius if (tag != NULL) { 578159979Sglebius if (tag_len != 0) { 579159979Sglebius /* copy tag data to its place */ 580159979Sglebius memcpy((void *)(tag + 1), 581159979Sglebius dhip->out_tag_data, tag_len); 582159979Sglebius } 583159979Sglebius m_tag_prepend(m, tag); 584159979Sglebius } 585159979Sglebius } 586159979Sglebius 587159979Sglebius NG_FWD_ITEM_HOOK(error, item, dest); 588159979Sglebius return (error); 589159979Sglebius} 590159979Sglebius 591159979Sglebius/* 592159979Sglebius * Shutdown processing. 593159979Sglebius */ 594159979Sglebiusstatic int 595159979Sglebiusng_tag_shutdown(node_p node) 596159979Sglebius{ 597159979Sglebius NG_NODE_UNREF(node); 598159979Sglebius return (0); 599159979Sglebius} 600159979Sglebius 601159979Sglebius/* 602159979Sglebius * Hook disconnection. 603159979Sglebius * 604159979Sglebius * We must check all hooks, since they may reference this one. 605159979Sglebius */ 606159979Sglebiusstatic int 607159979Sglebiusng_tag_disconnect(hook_p hook) 608159979Sglebius{ 609159979Sglebius const hinfo_p hip = NG_HOOK_PRIVATE(hook); 610159979Sglebius node_p node = NG_HOOK_NODE(hook); 611159979Sglebius hook_p hook2; 612159979Sglebius 613159979Sglebius KASSERT(hip != NULL, ("%s: null info", __func__)); 614159979Sglebius 615159979Sglebius LIST_FOREACH(hook2, &node->nd_hooks, hk_hooks) { 616159979Sglebius hinfo_p priv = NG_HOOK_PRIVATE(hook2); 617159979Sglebius 618159979Sglebius if (priv->hi_match == hook) 619159979Sglebius priv->hi_match = NULL; 620159979Sglebius if (priv->hi_nonmatch == hook) 621159979Sglebius priv->hi_nonmatch = NULL; 622159979Sglebius } 623159979Sglebius 624184205Sdes free(hip->in, M_NETGRAPH_TAG); 625184205Sdes free(hip->out, M_NETGRAPH_TAG); 626184205Sdes free(hip, M_NETGRAPH_TAG); 627159979Sglebius NG_HOOK_SET_PRIVATE(hook, NULL); /* for good measure */ 628159979Sglebius if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) && 629159979Sglebius (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) { 630159979Sglebius ng_rmnode_self(NG_HOOK_NODE(hook)); 631159979Sglebius } 632159979Sglebius return (0); 633159979Sglebius} 634159979Sglebius 635159979Sglebius/************************************************************************ 636159979Sglebius HELPER STUFF 637159979Sglebius ************************************************************************/ 638159979Sglebius 639159979Sglebius/* 640159979Sglebius * Set the IN tag values associated with a hook. 641159979Sglebius */ 642159979Sglebiusstatic int 643159979Sglebiusng_tag_setdata_in(hook_p hook, const struct ng_tag_hookin *hp0) 644159979Sglebius{ 645159979Sglebius const hinfo_p hip = NG_HOOK_PRIVATE(hook); 646159979Sglebius struct ng_tag_hookin *hp; 647159979Sglebius int size; 648159979Sglebius 649159979Sglebius /* Make a copy of the tag values and data. */ 650159979Sglebius size = NG_TAG_HOOKIN_SIZE(hp0->tag_len); 651184205Sdes hp = malloc(size, M_NETGRAPH_TAG, M_WAITOK); 652159979Sglebius /* M_WAITOK can't return NULL. */ 653159979Sglebius bcopy(hp0, hp, size); 654159979Sglebius 655159979Sglebius /* Free previous tag, if any, and assign new one. */ 656159979Sglebius if (hip->in != NULL) 657184205Sdes free(hip->in, M_NETGRAPH_TAG); 658159979Sglebius hip->in = hp; 659159979Sglebius 660159979Sglebius /* 661159979Sglebius * Resolve hook names to pointers. 662159979Sglebius * 663159979Sglebius * As ng_findhook() is expensive operation to do it on every packet 664159979Sglebius * after tag matching check, we do it here and use resolved pointers 665159979Sglebius * where appropriate. 666159979Sglebius * 667159979Sglebius * XXX The drawback is that user can configure a hook to use 668159979Sglebius * ifMatch/ifNotMatch hooks that do not yet exist and will be added 669159979Sglebius * by user later, so that resolved pointers will be NULL even 670159979Sglebius * if the hook already exists, causing node to drop packets and 671159979Sglebius * user to report bugs. We could do check for this situation on 672159979Sglebius * every hook creation with pointers correction, but that involves 673159979Sglebius * re-resolving for all pointers in all hooks, up to O(n^2) operations, 674159979Sglebius * so we better document this in man page for user not to do 675159979Sglebius * configuration before creating all hooks. 676159979Sglebius */ 677159979Sglebius hip->hi_match = ng_findhook(NG_HOOK_NODE(hook), hip->in->ifMatch); 678159979Sglebius hip->hi_nonmatch = ng_findhook(NG_HOOK_NODE(hook), hip->in->ifNotMatch); 679159979Sglebius 680159979Sglebius /* Fill internal values from API structures. */ 681159979Sglebius hip->in_tag_cookie = hip->in->tag_cookie; 682159979Sglebius hip->in_tag_id = hip->in->tag_id; 683159979Sglebius hip->in_tag_len = hip->in->tag_len; 684159979Sglebius hip->strip = hip->in->strip; 685159979Sglebius hip->in_tag_data = (void*)(hip->in->tag_data); 686159979Sglebius return (0); 687159979Sglebius} 688159979Sglebius 689159979Sglebius/* 690159979Sglebius * Set the OUT tag values associated with a hook. 691159979Sglebius */ 692159979Sglebiusstatic int 693159979Sglebiusng_tag_setdata_out(hook_p hook, const struct ng_tag_hookout *hp0) 694159979Sglebius{ 695159979Sglebius const hinfo_p hip = NG_HOOK_PRIVATE(hook); 696159979Sglebius struct ng_tag_hookout *hp; 697159979Sglebius int size; 698159979Sglebius 699159979Sglebius /* Make a copy of the tag values and data. */ 700159979Sglebius size = NG_TAG_HOOKOUT_SIZE(hp0->tag_len); 701184205Sdes hp = malloc(size, M_NETGRAPH_TAG, M_WAITOK); 702159979Sglebius /* M_WAITOK can't return NULL. */ 703159979Sglebius bcopy(hp0, hp, size); 704159979Sglebius 705159979Sglebius /* Free previous tag, if any, and assign new one. */ 706159979Sglebius if (hip->out != NULL) 707184205Sdes free(hip->out, M_NETGRAPH_TAG); 708159979Sglebius hip->out = hp; 709159979Sglebius 710159979Sglebius /* Fill internal values from API structures. */ 711159979Sglebius hip->out_tag_cookie = hip->out->tag_cookie; 712159979Sglebius hip->out_tag_id = hip->out->tag_id; 713159979Sglebius hip->out_tag_len = hip->out->tag_len; 714159979Sglebius hip->out_tag_data = (void*)(hip->out->tag_data); 715159979Sglebius return (0); 716159979Sglebius} 717159979Sglebius 718