152419Sjulian 252419Sjulian/* 352419Sjulian * ng_tee.c 4139823Simp */ 5139823Simp 6139823Simp/*- 752419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc. 852419Sjulian * All rights reserved. 952419Sjulian * 1052419Sjulian * Subject to the following obligations and disclaimer of warranty, use and 1152419Sjulian * redistribution of this software, in source or object code forms, with or 1252419Sjulian * without modifications are expressly permitted by Whistle Communications; 1352419Sjulian * provided, however, that: 1452419Sjulian * 1. Any and all reproductions of the source or object code must include the 1552419Sjulian * copyright notice above and the following disclaimer of warranties; and 1652419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle 1752419Sjulian * Communications, Inc. trademarks, including the mark "WHISTLE 1852419Sjulian * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 1952419Sjulian * such appears in the above copyright notice or in the software. 2052419Sjulian * 2152419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 2252419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 2352419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 2452419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 2552419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 2652419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 2752419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 2852419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 2952419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 3052419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 3152419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 3252419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 3352419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 3452419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3552419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3652419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 3752419Sjulian * OF SUCH DAMAGE. 3852419Sjulian * 3967506Sjulian * Author: Julian Elischer <julian@freebsd.org> 4052419Sjulian * 4152419Sjulian * $FreeBSD$ 4252752Sjulian * $Whistle: ng_tee.c,v 1.18 1999/11/01 09:24:52 julian Exp $ 4352419Sjulian */ 4452419Sjulian 4552419Sjulian/* 4652419Sjulian * This node is like the tee(1) command and is useful for ``snooping.'' 4752419Sjulian * It has 4 hooks: left, right, left2right, and right2left. Data 4852419Sjulian * entering from the right is passed to the left and duplicated on 4952419Sjulian * right2left, and data entering from the left is passed to the right 5052419Sjulian * and duplicated on left2right. Data entering from left2right is 51116839Sjulian * sent to left, and data from right2left to right. 5252419Sjulian */ 5352419Sjulian 5452419Sjulian#include <sys/param.h> 5552419Sjulian#include <sys/systm.h> 5652419Sjulian#include <sys/errno.h> 5752419Sjulian#include <sys/kernel.h> 5852419Sjulian#include <sys/malloc.h> 5952419Sjulian#include <sys/mbuf.h> 6052419Sjulian#include <netgraph/ng_message.h> 6152419Sjulian#include <netgraph/netgraph.h> 6256658Sarchie#include <netgraph/ng_parse.h> 6352419Sjulian#include <netgraph/ng_tee.h> 6452419Sjulian 6552419Sjulian/* Per hook info */ 6652419Sjulianstruct hookinfo { 6752817Sarchie hook_p hook; 68175954Smav struct hookinfo *dest, *dup; 6952817Sarchie struct ng_tee_hookstat stats; 7052419Sjulian}; 71175954Smavtypedef struct hookinfo *hi_p; 7252419Sjulian 7352419Sjulian/* Per node info */ 7452419Sjulianstruct privdata { 7552817Sarchie struct hookinfo left; 7652817Sarchie struct hookinfo right; 7752817Sarchie struct hookinfo left2right; 7852817Sarchie struct hookinfo right2left; 7952419Sjulian}; 8052419Sjuliantypedef struct privdata *sc_p; 8152419Sjulian 8252419Sjulian/* Netgraph methods */ 83175954Smavstatic ng_constructor_t ng_tee_constructor; 84175954Smavstatic ng_rcvmsg_t ng_tee_rcvmsg; 85175954Smavstatic ng_close_t ng_tee_close; 86175954Smavstatic ng_shutdown_t ng_tee_shutdown; 87175954Smavstatic ng_newhook_t ng_tee_newhook; 88175954Smavstatic ng_rcvdata_t ng_tee_rcvdata; 89175954Smavstatic ng_disconnect_t ng_tee_disconnect; 9052419Sjulian 9156658Sarchie/* Parse type for struct ng_tee_hookstat */ 9297685Sarchiestatic const struct ng_parse_struct_field ng_tee_hookstat_type_fields[] 9397685Sarchie = NG_TEE_HOOKSTAT_INFO; 9456658Sarchiestatic const struct ng_parse_type ng_tee_hookstat_type = { 9556658Sarchie &ng_parse_struct_type, 9697685Sarchie &ng_tee_hookstat_type_fields 9756658Sarchie}; 9856658Sarchie 9956658Sarchie/* Parse type for struct ng_tee_stats */ 10097685Sarchiestatic const struct ng_parse_struct_field ng_tee_stats_type_fields[] 10197685Sarchie = NG_TEE_STATS_INFO(&ng_tee_hookstat_type); 10256658Sarchiestatic const struct ng_parse_type ng_tee_stats_type = { 10356658Sarchie &ng_parse_struct_type, 10497685Sarchie &ng_tee_stats_type_fields 10556658Sarchie}; 10656658Sarchie 10756658Sarchie/* List of commands and how to convert arguments to/from ASCII */ 10856658Sarchiestatic const struct ng_cmdlist ng_tee_cmds[] = { 10956658Sarchie { 11056658Sarchie NGM_TEE_COOKIE, 11156658Sarchie NGM_TEE_GET_STATS, 11256658Sarchie "getstats", 11356658Sarchie NULL, 11456658Sarchie &ng_tee_stats_type 11556658Sarchie }, 11656658Sarchie { 11756658Sarchie NGM_TEE_COOKIE, 11856658Sarchie NGM_TEE_CLR_STATS, 11956658Sarchie "clrstats", 12056658Sarchie NULL, 12156658Sarchie NULL 12256658Sarchie }, 12364513Sarchie { 12464513Sarchie NGM_TEE_COOKIE, 12564513Sarchie NGM_TEE_GETCLR_STATS, 12664513Sarchie "getclrstats", 12764513Sarchie NULL, 12864513Sarchie &ng_tee_stats_type 12964513Sarchie }, 13056658Sarchie { 0 } 13156658Sarchie}; 13256658Sarchie 13352419Sjulian/* Netgraph type descriptor */ 13456658Sarchiestatic struct ng_type ng_tee_typestruct = { 135129823Sjulian .version = NG_ABI_VERSION, 136129823Sjulian .name = NG_TEE_NODE_TYPE, 137175954Smav .constructor = ng_tee_constructor, 138175954Smav .rcvmsg = ng_tee_rcvmsg, 139175954Smav .close = ng_tee_close, 140175954Smav .shutdown = ng_tee_shutdown, 141175954Smav .newhook = ng_tee_newhook, 142175954Smav .rcvdata = ng_tee_rcvdata, 143175954Smav .disconnect = ng_tee_disconnect, 144129823Sjulian .cmdlist = ng_tee_cmds, 14552419Sjulian}; 14656658SarchieNETGRAPH_INIT(tee, &ng_tee_typestruct); 14752419Sjulian 14852419Sjulian/* 14952419Sjulian * Node constructor 15052419Sjulian */ 15152419Sjulianstatic int 152175954Smavng_tee_constructor(node_p node) 15352419Sjulian{ 15452419Sjulian sc_p privdata; 15552419Sjulian 156220768Sglebius privdata = malloc(sizeof(*privdata), M_NETGRAPH, M_WAITOK | M_ZERO); 15752419Sjulian 15870784Sjulian NG_NODE_SET_PRIVATE(node, privdata); 15952419Sjulian return (0); 16052419Sjulian} 16152419Sjulian 16252419Sjulian/* 16352419Sjulian * Add a hook 16452419Sjulian */ 16552419Sjulianstatic int 166175954Smavng_tee_newhook(node_p node, hook_p hook, const char *name) 16752419Sjulian{ 168175954Smav sc_p privdata = NG_NODE_PRIVATE(node); 169175954Smav hi_p hinfo; 17052419Sjulian 171175954Smav /* Precalculate internal pathes. */ 17252419Sjulian if (strcmp(name, NG_TEE_HOOK_RIGHT) == 0) { 173175954Smav hinfo = &privdata->right; 174175954Smav if (privdata->left.dest) 175175954Smav privdata->left.dup = privdata->left.dest; 176175954Smav privdata->left.dest = hinfo; 177175954Smav privdata->right2left.dest = hinfo; 17852419Sjulian } else if (strcmp(name, NG_TEE_HOOK_LEFT) == 0) { 179175954Smav hinfo = &privdata->left; 180175954Smav if (privdata->right.dest) 181175954Smav privdata->right.dup = privdata->right.dest; 182175954Smav privdata->right.dest = hinfo; 183175954Smav privdata->left2right.dest = hinfo; 18452419Sjulian } else if (strcmp(name, NG_TEE_HOOK_RIGHT2LEFT) == 0) { 185175954Smav hinfo = &privdata->right2left; 186175954Smav if (privdata->right.dest) 187175954Smav privdata->right.dup = hinfo; 188175954Smav else 189175954Smav privdata->right.dest = hinfo; 19052419Sjulian } else if (strcmp(name, NG_TEE_HOOK_LEFT2RIGHT) == 0) { 191175954Smav hinfo = &privdata->left2right; 192175954Smav if (privdata->left.dest) 193175954Smav privdata->left.dup = hinfo; 194175954Smav else 195175954Smav privdata->left.dest = hinfo; 19652419Sjulian } else 19752419Sjulian return (EINVAL); 198175954Smav hinfo->hook = hook; 199175954Smav bzero(&hinfo->stats, sizeof(hinfo->stats)); 200175954Smav NG_HOOK_SET_PRIVATE(hook, hinfo); 20152419Sjulian return (0); 20252419Sjulian} 20352419Sjulian 20452419Sjulian/* 20552817Sarchie * Receive a control message 20652419Sjulian */ 20752419Sjulianstatic int 208175954Smavng_tee_rcvmsg(node_p node, item_p item, hook_p lasthook) 20952419Sjulian{ 21070784Sjulian const sc_p sc = NG_NODE_PRIVATE(node); 21152817Sarchie struct ng_mesg *resp = NULL; 21252817Sarchie int error = 0; 21370700Sjulian struct ng_mesg *msg; 21452817Sarchie 21570700Sjulian NGI_GET_MSG(item, msg); 21652817Sarchie switch (msg->header.typecookie) { 21752817Sarchie case NGM_TEE_COOKIE: 21852817Sarchie switch (msg->header.cmd) { 21952817Sarchie case NGM_TEE_GET_STATS: 22064513Sarchie case NGM_TEE_CLR_STATS: 22164513Sarchie case NGM_TEE_GETCLR_STATS: 22264513Sarchie { 22352817Sarchie struct ng_tee_stats *stats; 22452817Sarchie 22564513Sarchie if (msg->header.cmd != NGM_TEE_CLR_STATS) { 22664513Sarchie NG_MKRESPONSE(resp, msg, 22764513Sarchie sizeof(*stats), M_NOWAIT); 22864513Sarchie if (resp == NULL) { 22964513Sarchie error = ENOMEM; 23064513Sarchie goto done; 23164513Sarchie } 23264513Sarchie stats = (struct ng_tee_stats *)resp->data; 23364513Sarchie bcopy(&sc->right.stats, &stats->right, 23464513Sarchie sizeof(stats->right)); 23564513Sarchie bcopy(&sc->left.stats, &stats->left, 23664513Sarchie sizeof(stats->left)); 23764513Sarchie bcopy(&sc->right2left.stats, &stats->right2left, 23864513Sarchie sizeof(stats->right2left)); 23964513Sarchie bcopy(&sc->left2right.stats, &stats->left2right, 24064513Sarchie sizeof(stats->left2right)); 24164513Sarchie } 24264513Sarchie if (msg->header.cmd != NGM_TEE_GET_STATS) { 24364513Sarchie bzero(&sc->right.stats, 24464513Sarchie sizeof(sc->right.stats)); 24564513Sarchie bzero(&sc->left.stats, 24664513Sarchie sizeof(sc->left.stats)); 24764513Sarchie bzero(&sc->right2left.stats, 24864513Sarchie sizeof(sc->right2left.stats)); 24964513Sarchie bzero(&sc->left2right.stats, 25064513Sarchie sizeof(sc->left2right.stats)); 25152817Sarchie } 25264513Sarchie break; 25352817Sarchie } 25452817Sarchie default: 25552817Sarchie error = EINVAL; 25652817Sarchie break; 25752817Sarchie } 25852817Sarchie break; 25970700Sjulian case NGM_FLOW_COOKIE: 260175954Smav if (lasthook == sc->left.hook || lasthook == sc->right.hook) { 261175954Smav hi_p const hinfo = NG_HOOK_PRIVATE(lasthook); 262175954Smav if (hinfo && hinfo->dest) { 263175954Smav NGI_MSG(item) = msg; 264175954Smav NG_FWD_ITEM_HOOK(error, item, hinfo->dest->hook); 265175954Smav return (error); 26670700Sjulian } 26770700Sjulian } 26870700Sjulian break; 26952817Sarchie default: 27052817Sarchie error = EINVAL; 27152817Sarchie break; 27252817Sarchie } 27352817Sarchiedone: 27470700Sjulian NG_RESPOND_MSG(error, node, item, resp); 27570700Sjulian NG_FREE_MSG(msg); 27652817Sarchie return (error); 27752419Sjulian} 27852419Sjulian 27952419Sjulian/* 28052419Sjulian * Receive data on a hook 28152419Sjulian * 28252419Sjulian * If data comes in the right link send a copy out right2left, and then 28352419Sjulian * send the original onwards out through the left link. 28452419Sjulian * Do the opposite for data coming in from the left link. 28552419Sjulian * Data coming in right2left or left2right is forwarded 28652419Sjulian * on through the appropriate destination hook as if it had come 28752419Sjulian * from the other side. 28852419Sjulian */ 28952419Sjulianstatic int 290175954Smavng_tee_rcvdata(hook_p hook, item_p item) 29152419Sjulian{ 292175954Smav const hi_p hinfo = NG_HOOK_PRIVATE(hook); 293175954Smav hi_p h; 294175954Smav int error = 0; 29570700Sjulian struct mbuf *m; 29652419Sjulian 29770700Sjulian m = NGI_M(item); 29852419Sjulian 29952817Sarchie /* Update stats on incoming hook */ 30052817Sarchie hinfo->stats.inOctets += m->m_pkthdr.len; 30152817Sarchie hinfo->stats.inFrames++; 30252817Sarchie 303131155Sjulian /* Duplicate packet if requried */ 304175954Smav if (hinfo->dup) { 30552817Sarchie struct mbuf *m2; 30652817Sarchie 30770700Sjulian /* Copy packet (failure will not stop the original)*/ 308243882Sglebius m2 = m_dup(m, M_NOWAIT); 30970700Sjulian if (m2) { 31070700Sjulian /* Deliver duplicate */ 311175954Smav h = hinfo->dup; 312175954Smav NG_SEND_DATA_ONLY(error, h->hook, m2); 313147104Sglebius if (error == 0) { 314175954Smav h->stats.outOctets += m->m_pkthdr.len; 315175954Smav h->stats.outFrames++; 316147104Sglebius } 31770700Sjulian } 31852419Sjulian } 31952817Sarchie /* Deliver frame out destination hook */ 320175954Smav if (hinfo->dest) { 321175954Smav h = hinfo->dest; 322175954Smav h->stats.outOctets += m->m_pkthdr.len; 323175954Smav h->stats.outFrames++; 324175954Smav NG_FWD_ITEM_HOOK(error, item, h->hook); 325127286Sjulian } else 32670700Sjulian NG_FREE_ITEM(item); 327117209Sjulian return (error); 32852419Sjulian} 32952419Sjulian 33052419Sjulian/* 331129836Sjulian * We are going to be shut down soon 33252419Sjulian * 333129836Sjulian * If we have both a left and right hook, then we probably want to extricate 334129836Sjulian * ourselves and leave the two peers still linked to each other. Otherwise we 335129836Sjulian * should just shut down as a normal node would. 33652419Sjulian */ 33752419Sjulianstatic int 338175954Smavng_tee_close(node_p node) 339129836Sjulian{ 340129836Sjulian const sc_p privdata = NG_NODE_PRIVATE(node); 341129836Sjulian 342129836Sjulian if (privdata->left.hook && privdata->right.hook) 343129836Sjulian ng_bypass(privdata->left.hook, privdata->right.hook); 344129836Sjulian 345129836Sjulian return (0); 346129836Sjulian} 347129836Sjulian 348129836Sjulian/* 349129836Sjulian * Shutdown processing 350129836Sjulian */ 351129836Sjulianstatic int 352175954Smavng_tee_shutdown(node_p node) 35352419Sjulian{ 35470784Sjulian const sc_p privdata = NG_NODE_PRIVATE(node); 35552419Sjulian 35670784Sjulian NG_NODE_SET_PRIVATE(node, NULL); 357184205Sdes free(privdata, M_NETGRAPH); 358176508Smav NG_NODE_UNREF(node); 35952419Sjulian return (0); 36052419Sjulian} 36152419Sjulian 36252419Sjulian/* 36352419Sjulian * Hook disconnection 36452419Sjulian */ 36552419Sjulianstatic int 366175954Smavng_tee_disconnect(hook_p hook) 36752419Sjulian{ 368175954Smav sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 369175954Smav hi_p const hinfo = NG_HOOK_PRIVATE(hook); 37052419Sjulian 37187599Sobrien KASSERT(hinfo != NULL, ("%s: null info", __func__)); 37252817Sarchie hinfo->hook = NULL; 373175954Smav 374175954Smav /* Recalculate internal pathes. */ 375175954Smav if (sc->left.dest == hinfo) { 376175954Smav sc->left.dest = sc->left.dup; 377175954Smav sc->left.dup = NULL; 378175954Smav } else if (sc->left.dup == hinfo) 379175954Smav sc->left.dup = NULL; 380175954Smav if (sc->right.dest == hinfo) { 381175954Smav sc->right.dest = sc->right.dup; 382175954Smav sc->right.dup = NULL; 383175954Smav } else if (sc->right.dup == hinfo) 384175954Smav sc->right.dup = NULL; 385175954Smav if (sc->left2right.dest == hinfo) 386175954Smav sc->left2right.dest = NULL; 387175954Smav if (sc->right2left.dest == hinfo) 388175954Smav sc->right2left.dest = NULL; 389175954Smav 390175954Smav /* Die when last hook disconnected. */ 391175954Smav if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) && 392175954Smav NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) 39370784Sjulian ng_rmnode_self(NG_HOOK_NODE(hook)); 39452419Sjulian return (0); 39552419Sjulian} 396