1 2 /* 3 * ng_tee.c 4 * 5 * Copyright (c) 1996-1999 Whistle Communications, Inc. 6 * All rights reserved. 7 * 8 * Subject to the following obligations and disclaimer of warranty, use and 9 * redistribution of this software, in source or object code forms, with or 10 * without modifications are expressly permitted by Whistle Communications; 11 * provided, however, that: 12 * 1. Any and all reproductions of the source or object code must include the 13 * copyright notice above and the following disclaimer of warranties; and 14 * 2. No rights are granted, in any manner or form, to use Whistle 15 * Communications, Inc. trademarks, including the mark "WHISTLE 16 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 17 * such appears in the above copyright notice or in the software. 18 * 19 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 20 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 21 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 22 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 24 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 25 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 26 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 27 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 28 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 29 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 30 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 31 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 35 * OF SUCH DAMAGE. 36 * 37 * Author: Julian Elischer <julian@whistle.com> 38 * 39 * $FreeBSD$ 40 * $Whistle: ng_tee.c,v 1.18 1999/11/01 09:24:52 julian Exp $ 41 */ 42 43 /* 44 * This node is like the tee(1) command and is useful for ``snooping.'' 45 * It has 4 hooks: left, right, left2right, and right2left. Data 46 * entering from the right is passed to the left and duplicated on 47 * right2left, and data entering from the left is passed to the right 48 * and duplicated on left2right. Data entering from left2right is 49 * sent to right, and data from right2left to left. 50 */ 51 52 #include <sys/param.h> 53 #include <sys/systm.h> 54 #include <sys/errno.h> 55 #include <sys/kernel.h> 56 #include <sys/malloc.h> 57 #include <sys/mbuf.h> 58 #include <netgraph/ng_message.h> 59 #include <netgraph/netgraph.h> 60 #include <netgraph/ng_tee.h> 61 62 /* Per hook info */ 63 struct hookinfo { 64 hook_p hook; 65 struct ng_tee_hookstat stats; 66 }; 67 68 /* Per node info */ 69 struct privdata { 70 node_p node; 71 struct hookinfo left; 72 struct hookinfo right; 73 struct hookinfo left2right; 74 struct hookinfo right2left; 75 }; 76 typedef struct privdata *sc_p; 77 78 /* Netgraph methods */ 79 static ng_constructor_t ngt_constructor; 80 static ng_rcvmsg_t ngt_rcvmsg; 81 static ng_shutdown_t ngt_rmnode; 82 static ng_newhook_t ngt_newhook; 83 static ng_rcvdata_t ngt_rcvdata; 84 static ng_disconnect_t ngt_disconnect; 85 86 /* Netgraph type descriptor */ 87 static struct ng_type typestruct = { 88 NG_VERSION, 89 NG_TEE_NODE_TYPE, 90 NULL, 91 ngt_constructor, 92 ngt_rcvmsg, 93 ngt_rmnode, 94 ngt_newhook, 95 NULL, 96 NULL, 97 ngt_rcvdata, 98 ngt_rcvdata, 99 ngt_disconnect, 100 NULL 101 }; 102 NETGRAPH_INIT(tee, &typestruct); 103 104 /* 105 * Node constructor 106 */ 107 static int 108 ngt_constructor(node_p *nodep) 109 { 110 sc_p privdata; 111 int error = 0; 112 113 MALLOC(privdata, sc_p, sizeof(*privdata), M_NETGRAPH, M_WAITOK); 114 if (privdata == NULL) 115 return (ENOMEM); 116 bzero(privdata, sizeof(*privdata)); 117 118 if ((error = ng_make_node_common(&typestruct, nodep))) { 119 FREE(privdata, M_NETGRAPH); 120 return (error); 121 } 122 (*nodep)->private = privdata; 123 privdata->node = *nodep; 124 return (0); 125 } 126 127 /* 128 * Add a hook 129 */ 130 static int 131 ngt_newhook(node_p node, hook_p hook, const char *name) 132 { 133 const sc_p sc = node->private; 134 135 if (strcmp(name, NG_TEE_HOOK_RIGHT) == 0) { 136 sc->right.hook = hook; 137 bzero(&sc->right.stats, sizeof(sc->right.stats)); 138 hook->private = &sc->right; 139 } else if (strcmp(name, NG_TEE_HOOK_LEFT) == 0) { 140 sc->left.hook = hook; 141 bzero(&sc->left.stats, sizeof(sc->left.stats)); 142 hook->private = &sc->left; 143 } else if (strcmp(name, NG_TEE_HOOK_RIGHT2LEFT) == 0) { 144 sc->right2left.hook = hook; 145 bzero(&sc->right2left.stats, sizeof(sc->right2left.stats)); 146 hook->private = &sc->right2left; 147 } else if (strcmp(name, NG_TEE_HOOK_LEFT2RIGHT) == 0) { 148 sc->left2right.hook = hook; 149 bzero(&sc->left2right.stats, sizeof(sc->left2right.stats)); 150 hook->private = &sc->left2right; 151 } else 152 return (EINVAL); 153 return (0); 154 } 155 156 /* 157 * Receive a control message 158 */ 159 static int 160 ngt_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, 161 struct ng_mesg **rptr) 162 { 163 const sc_p sc = node->private; 164 struct ng_mesg *resp = NULL; 165 int error = 0; 166 167 switch (msg->header.typecookie) { 168 case NGM_TEE_COOKIE: 169 switch (msg->header.cmd) { 170 case NGM_TEE_GET_STATS: 171 { 172 struct ng_tee_stats *stats; 173 174 NG_MKRESPONSE(resp, msg, 175 sizeof(struct ng_tee_stats), M_NOWAIT); 176 if (resp == NULL) { 177 error = ENOMEM; 178 goto done; 179 } 180 stats = (struct ng_tee_stats *) resp->data; 181 bcopy(&sc->right.stats, 182 &stats->right, sizeof(stats->right)); 183 bcopy(&sc->left.stats, 184 &stats->left, sizeof(stats->left)); 185 bcopy(&sc->right2left.stats, 186 &stats->right2left, sizeof(stats->right2left)); 187 bcopy(&sc->left2right.stats, 188 &stats->left2right, sizeof(stats->left2right)); 189 break; 190 } 191 case NGM_TEE_CLR_STATS: 192 bzero(&sc->right.stats, sizeof(sc->right.stats)); 193 bzero(&sc->left.stats, sizeof(sc->left.stats)); 194 bzero(&sc->right2left.stats, 195 sizeof(sc->right2left.stats)); 196 bzero(&sc->left2right.stats, 197 sizeof(sc->left2right.stats)); 198 break; 199 default: 200 error = EINVAL; 201 break; 202 } 203 break; 204 default: 205 error = EINVAL; 206 break; 207 } 208 if (rptr) 209 *rptr = resp; 210 else if (resp) 211 FREE(resp, M_NETGRAPH); 212 213 done: 214 FREE(msg, M_NETGRAPH); 215 return (error); 216 } 217 218 /* 219 * Receive data on a hook 220 * 221 * If data comes in the right link send a copy out right2left, and then 222 * send the original onwards out through the left link. 223 * Do the opposite for data coming in from the left link. 224 * Data coming in right2left or left2right is forwarded 225 * on through the appropriate destination hook as if it had come 226 * from the other side. 227 */ 228 static int 229 ngt_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) 230 { 231 const sc_p sc = hook->node->private; 232 struct hookinfo *const hinfo = (struct hookinfo *) hook->private; 233 struct hookinfo *dest; 234 struct hookinfo *dup; 235 int error = 0; 236 237 /* Which hook? */ 238 if (hinfo == &sc->left) { 239 dup = &sc->left2right; 240 dest = &sc->right; 241 } else if (hinfo == &sc->right) { 242 dup = &sc->right2left; 243 dest = &sc->left; 244 } else if (hinfo == &sc->right2left) { 245 dup = NULL; 246 dest = &sc->left; 247 } else if (hinfo == &sc->left2right) { 248 dup = NULL; 249 dest = &sc->right; 250 } else 251 panic("%s: no hook!", __FUNCTION__); 252 253 /* Update stats on incoming hook */ 254 hinfo->stats.inOctets += m->m_pkthdr.len; 255 hinfo->stats.inFrames++; 256 257 /* Duplicate packet and meta info if requried */ 258 if (dup != NULL) { 259 struct mbuf *m2; 260 meta_p meta2; 261 262 /* Copy packet */ 263 m2 = m_dup(m, M_NOWAIT); 264 if (m2 == NULL) { 265 NG_FREE_DATA(m, meta); 266 return (ENOBUFS); 267 } 268 269 /* Copy meta info */ 270 if (meta != NULL) { 271 MALLOC(meta2, meta_p, 272 meta->used_len, M_NETGRAPH, M_NOWAIT); 273 if (meta2 == NULL) { 274 m_freem(m2); 275 NG_FREE_DATA(m, meta); 276 return (ENOMEM); 277 } 278 bcopy(meta, meta2, meta->used_len); 279 meta2->allocated_len = meta->used_len; 280 } else 281 meta2 = NULL; 282 283 /* Deliver duplicate */ 284 dup->stats.outOctets += m->m_pkthdr.len; 285 dup->stats.outFrames++; 286 NG_SEND_DATA(error, dup->hook, m2, meta2); 287 } 288 289 /* Deliver frame out destination hook */ 290 dest->stats.outOctets += m->m_pkthdr.len; 291 dest->stats.outFrames++; 292 NG_SEND_DATA(error, dest->hook, m, meta); 293 return (0); 294 } 295 296 /* 297 * Shutdown processing 298 * 299 * This is tricky. If we have both a left and right hook, then we 300 * probably want to extricate ourselves and leave the two peers 301 * still linked to each other. Otherwise we should just shut down as 302 * a normal node would. 303 * 304 * To keep the scope of info correct the routine to "extract" a node 305 * from two links is in ng_base.c. 306 */ 307 static int 308 ngt_rmnode(node_p node) 309 { 310 const sc_p privdata = node->private; 311 312 node->flags |= NG_INVALID; 313 if (privdata->left.hook && privdata->right.hook) 314 ng_bypass(privdata->left.hook, privdata->right.hook); 315 ng_cutlinks(node); 316 ng_unname(node); 317 node->private = NULL; 318 ng_unref(privdata->node); 319 FREE(privdata, M_NETGRAPH); 320 return (0); 321 } 322 323 /* 324 * Hook disconnection 325 */ 326 static int 327 ngt_disconnect(hook_p hook) 328 { 329 struct hookinfo *const hinfo = (struct hookinfo *) hook->private; 330 331 KASSERT(hinfo != NULL, ("%s: null info", __FUNCTION__)); 332 hinfo->hook = NULL; 333 if (hook->node->numhooks == 0) 334 ng_rmnode(hook->node); 335 return (0); 336 } 337 338