xref: /dragonfly/sys/netgraph7/tee/ng_tee.c (revision b5523eac31a95e6876e05e20e6fe836ec3a45202)
1 
2 /*
3  * ng_tee.c
4  */
5 
6 /*-
7  * Copyright (c) 1996-1999 Whistle Communications, Inc.
8  * All rights reserved.
9  *
10  * Subject to the following obligations and disclaimer of warranty, use and
11  * redistribution of this software, in source or object code forms, with or
12  * without modifications are expressly permitted by Whistle Communications;
13  * provided, however, that:
14  * 1. Any and all reproductions of the source or object code must include the
15  *    copyright notice above and the following disclaimer of warranties; and
16  * 2. No rights are granted, in any manner or form, to use Whistle
17  *    Communications, Inc. trademarks, including the mark "WHISTLE
18  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
19  *    such appears in the above copyright notice or in the software.
20  *
21  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
22  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
23  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
24  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
26  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
27  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
28  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
29  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
30  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
31  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
32  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
33  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
34  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
36  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
37  * OF SUCH DAMAGE.
38  *
39  * Author: Julian Elischer <julian@freebsd.org>
40  *
41  * $FreeBSD: src/sys/netgraph/ng_tee.c,v 1.35 2008/02/24 10:13:32 mav Exp $
42  * $Whistle: ng_tee.c,v 1.18 1999/11/01 09:24:52 julian Exp $
43  */
44 
45 /*
46  * This node is like the tee(1) command and is useful for ``snooping.''
47  * It has 4 hooks: left, right, left2right, and right2left. Data
48  * entering from the right is passed to the left and duplicated on
49  * right2left, and data entering from the left is passed to the right
50  * and duplicated on left2right. Data entering from left2right is
51  * sent to left, and data from right2left to right.
52  */
53 
54 #include <sys/param.h>
55 #include <sys/systm.h>
56 #include <sys/errno.h>
57 #include <sys/kernel.h>
58 #include <sys/malloc.h>
59 #include <sys/mbuf.h>
60 #include <netgraph7/ng_message.h>
61 #include <netgraph7/netgraph.h>
62 #include <netgraph7/ng_parse.h>
63 #include "ng_tee.h"
64 
65 /* Per hook info */
66 struct hookinfo {
67           hook_p                        hook;
68           struct hookinfo               *dest, *dup;
69           struct ng_tee_hookstat        stats;
70 };
71 typedef struct hookinfo *hi_p;
72 
73 /* Per node info */
74 struct privdata {
75           struct hookinfo               left;
76           struct hookinfo               right;
77           struct hookinfo               left2right;
78           struct hookinfo               right2left;
79 };
80 typedef struct privdata *sc_p;
81 
82 /* Netgraph methods */
83 static ng_constructor_t       ng_tee_constructor;
84 static ng_rcvmsg_t  ng_tee_rcvmsg;
85 static ng_close_t   ng_tee_close;
86 static ng_shutdown_t          ng_tee_shutdown;
87 static ng_newhook_t ng_tee_newhook;
88 static ng_rcvdata_t ng_tee_rcvdata;
89 static ng_disconnect_t        ng_tee_disconnect;
90 
91 /* Parse type for struct ng_tee_hookstat */
92 static const struct ng_parse_struct_field ng_tee_hookstat_type_fields[]
93           = NG_TEE_HOOKSTAT_INFO;
94 static const struct ng_parse_type ng_tee_hookstat_type = {
95           &ng_parse_struct_type,
96           &ng_tee_hookstat_type_fields
97 };
98 
99 /* Parse type for struct ng_tee_stats */
100 static const struct ng_parse_struct_field ng_tee_stats_type_fields[]
101           = NG_TEE_STATS_INFO(&ng_tee_hookstat_type);
102 static const struct ng_parse_type ng_tee_stats_type = {
103           &ng_parse_struct_type,
104           &ng_tee_stats_type_fields
105 };
106 
107 /* List of commands and how to convert arguments to/from ASCII */
108 static const struct ng_cmdlist ng_tee_cmds[] = {
109           {
110             NGM_TEE_COOKIE,
111             NGM_TEE_GET_STATS,
112             "getstats",
113             NULL,
114             &ng_tee_stats_type
115           },
116           {
117             NGM_TEE_COOKIE,
118             NGM_TEE_CLR_STATS,
119             "clrstats",
120             NULL,
121             NULL
122           },
123           {
124             NGM_TEE_COOKIE,
125             NGM_TEE_GETCLR_STATS,
126             "getclrstats",
127             NULL,
128             &ng_tee_stats_type
129           },
130           { 0 }
131 };
132 
133 /* Netgraph type descriptor */
134 static struct ng_type ng_tee_typestruct = {
135           .version =          NG_ABI_VERSION,
136           .name =             NG_TEE_NODE_TYPE,
137           .constructor =  ng_tee_constructor,
138           .rcvmsg = ng_tee_rcvmsg,
139           .close =  ng_tee_close,
140           .shutdown =         ng_tee_shutdown,
141           .newhook =          ng_tee_newhook,
142           .rcvdata =          ng_tee_rcvdata,
143           .disconnect =       ng_tee_disconnect,
144           .cmdlist =          ng_tee_cmds,
145 };
146 NETGRAPH_INIT(tee, &ng_tee_typestruct);
147 
148 /*
149  * Node constructor
150  */
151 static int
ng_tee_constructor(node_p node)152 ng_tee_constructor(node_p node)
153 {
154           sc_p privdata;
155 
156           privdata = kmalloc(sizeof(*privdata), M_NETGRAPH,
157                                  M_WAITOK | M_NULLOK | M_ZERO);
158           if (privdata == NULL)
159                     return (ENOMEM);
160 
161           NG_NODE_SET_PRIVATE(node, privdata);
162           return (0);
163 }
164 
165 /*
166  * Add a hook
167  */
168 static int
ng_tee_newhook(node_p node,hook_p hook,const char * name)169 ng_tee_newhook(node_p node, hook_p hook, const char *name)
170 {
171           sc_p      privdata = NG_NODE_PRIVATE(node);
172           hi_p      hinfo;
173 
174           /* Precalculate internal pathes. */
175           if (strcmp(name, NG_TEE_HOOK_RIGHT) == 0) {
176                     hinfo = &privdata->right;
177                     if (privdata->left.dest)
178                               privdata->left.dup = privdata->left.dest;
179                     privdata->left.dest = hinfo;
180                     privdata->right2left.dest = hinfo;
181           } else if (strcmp(name, NG_TEE_HOOK_LEFT) == 0) {
182                     hinfo = &privdata->left;
183                     if (privdata->right.dest)
184                               privdata->right.dup = privdata->right.dest;
185                     privdata->right.dest = hinfo;
186                     privdata->left2right.dest = hinfo;
187           } else if (strcmp(name, NG_TEE_HOOK_RIGHT2LEFT) == 0) {
188                     hinfo = &privdata->right2left;
189                     if (privdata->right.dest)
190                               privdata->right.dup = hinfo;
191                     else
192                               privdata->right.dest = hinfo;
193           } else if (strcmp(name, NG_TEE_HOOK_LEFT2RIGHT) == 0) {
194                     hinfo = &privdata->left2right;
195                     if (privdata->left.dest)
196                               privdata->left.dup = hinfo;
197                     else
198                               privdata->left.dest = hinfo;
199           } else
200                     return (EINVAL);
201           hinfo->hook = hook;
202           bzero(&hinfo->stats, sizeof(hinfo->stats));
203           NG_HOOK_SET_PRIVATE(hook, hinfo);
204           return (0);
205 }
206 
207 /*
208  * Receive a control message
209  */
210 static int
ng_tee_rcvmsg(node_p node,item_p item,hook_p lasthook)211 ng_tee_rcvmsg(node_p node, item_p item, hook_p lasthook)
212 {
213           const sc_p sc = NG_NODE_PRIVATE(node);
214           struct ng_mesg *resp = NULL;
215           int error = 0;
216           struct ng_mesg *msg;
217 
218           NGI_GET_MSG(item, msg);
219           switch (msg->header.typecookie) {
220           case NGM_TEE_COOKIE:
221                     switch (msg->header.cmd) {
222                     case NGM_TEE_GET_STATS:
223                     case NGM_TEE_CLR_STATS:
224                     case NGM_TEE_GETCLR_STATS:
225                     {
226                               struct ng_tee_stats *stats;
227 
228                         if (msg->header.cmd != NGM_TEE_CLR_STATS) {
229                                 NG_MKRESPONSE(resp, msg,
230                                     sizeof(*stats), M_WAITOK | M_NULLOK);
231                                         if (resp == NULL) {
232                                                   error = ENOMEM;
233                                                   goto done;
234                                         }
235                                         stats = (struct ng_tee_stats *)resp->data;
236                                         bcopy(&sc->right.stats, &stats->right,
237                                             sizeof(stats->right));
238                                         bcopy(&sc->left.stats, &stats->left,
239                                             sizeof(stats->left));
240                                         bcopy(&sc->right2left.stats, &stats->right2left,
241                                             sizeof(stats->right2left));
242                                         bcopy(&sc->left2right.stats, &stats->left2right,
243                                             sizeof(stats->left2right));
244                         }
245                         if (msg->header.cmd != NGM_TEE_GET_STATS) {
246                                         bzero(&sc->right.stats,
247                                             sizeof(sc->right.stats));
248                                         bzero(&sc->left.stats,
249                                             sizeof(sc->left.stats));
250                                         bzero(&sc->right2left.stats,
251                                             sizeof(sc->right2left.stats));
252                                         bzero(&sc->left2right.stats,
253                                             sizeof(sc->left2right.stats));
254                               }
255                         break;
256                         }
257                     default:
258                               error = EINVAL;
259                               break;
260                     }
261                     break;
262           case NGM_FLOW_COOKIE:
263                     if (lasthook == sc->left.hook || lasthook == sc->right.hook)  {
264                               hi_p const hinfo = NG_HOOK_PRIVATE(lasthook);
265                               if (hinfo && hinfo->dest) {
266                                         NGI_MSG(item) = msg;
267                                         NG_FWD_ITEM_HOOK(error, item, hinfo->dest->hook);
268                                         return (error);
269                               }
270                     }
271                     break;
272           default:
273                     error = EINVAL;
274                     break;
275           }
276 done:
277           NG_RESPOND_MSG(error, node, item, resp);
278           NG_FREE_MSG(msg);
279           return (error);
280 }
281 
282 /*
283  * Receive data on a hook
284  *
285  * If data comes in the right link send a copy out right2left, and then
286  * send the original onwards out through the left link.
287  * Do the opposite for data coming in from the left link.
288  * Data coming in right2left or left2right is forwarded
289  * on through the appropriate destination hook as if it had come
290  * from the other side.
291  */
292 static int
ng_tee_rcvdata(hook_p hook,item_p item)293 ng_tee_rcvdata(hook_p hook, item_p item)
294 {
295           const hi_p hinfo = NG_HOOK_PRIVATE(hook);
296           hi_p      h;
297           int       error = 0;
298           struct mbuf *m;
299 
300           m = NGI_M(item);
301 
302           /* Update stats on incoming hook */
303           hinfo->stats.inOctets += m->m_pkthdr.len;
304           hinfo->stats.inFrames++;
305 
306           /* Duplicate packet if requried */
307           if (hinfo->dup) {
308                     struct mbuf *m2;
309 
310                     /* Copy packet (failure will not stop the original)*/
311                     m2 = m_dup(m, M_NOWAIT);
312                     if (m2) {
313                               /* Deliver duplicate */
314                               h = hinfo->dup;
315                               NG_SEND_DATA_ONLY(error, h->hook, m2);
316                               if (error == 0) {
317                                         h->stats.outOctets += m->m_pkthdr.len;
318                                         h->stats.outFrames++;
319                               }
320                     }
321           }
322           /* Deliver frame out destination hook */
323           if (hinfo->dest) {
324                     h = hinfo->dest;
325                     h->stats.outOctets += m->m_pkthdr.len;
326                     h->stats.outFrames++;
327                     NG_FWD_ITEM_HOOK(error, item, h->hook);
328           } else
329                     NG_FREE_ITEM(item);
330           return (error);
331 }
332 
333 /*
334  * We are going to be shut down soon
335  *
336  * If we have both a left and right hook, then we probably want to extricate
337  * ourselves and leave the two peers still linked to each other. Otherwise we
338  * should just shut down as a normal node would.
339  */
340 static int
ng_tee_close(node_p node)341 ng_tee_close(node_p node)
342 {
343           const sc_p privdata = NG_NODE_PRIVATE(node);
344 
345           if (privdata->left.hook && privdata->right.hook)
346                     ng_bypass(privdata->left.hook, privdata->right.hook);
347 
348           return (0);
349 }
350 
351 /*
352  * Shutdown processing
353  */
354 static int
ng_tee_shutdown(node_p node)355 ng_tee_shutdown(node_p node)
356 {
357           const sc_p privdata = NG_NODE_PRIVATE(node);
358 
359           NG_NODE_SET_PRIVATE(node, NULL);
360           kfree(privdata, M_NETGRAPH);
361           NG_NODE_UNREF(node);
362           return (0);
363 }
364 
365 /*
366  * Hook disconnection
367  */
368 static int
ng_tee_disconnect(hook_p hook)369 ng_tee_disconnect(hook_p hook)
370 {
371           sc_p      sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
372           hi_p const hinfo = NG_HOOK_PRIVATE(hook);
373 
374           KASSERT(hinfo != NULL, ("%s: null info", __func__));
375           hinfo->hook = NULL;
376 
377           /* Recalculate internal pathes. */
378           if (sc->left.dest == hinfo) {
379                     sc->left.dest = sc->left.dup;
380                     sc->left.dup = NULL;
381           } else if (sc->left.dup == hinfo)
382                     sc->left.dup = NULL;
383           if (sc->right.dest == hinfo) {
384                     sc->right.dest = sc->right.dup;
385                     sc->right.dup = NULL;
386           } else if (sc->right.dup == hinfo)
387                     sc->right.dup = NULL;
388           if (sc->left2right.dest == hinfo)
389                     sc->left2right.dest = NULL;
390           if (sc->right2left.dest == hinfo)
391                     sc->right2left.dest = NULL;
392 
393           /* Die when last hook disconnected. */
394           if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) &&
395               NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))
396                     ng_rmnode_self(NG_HOOK_NODE(hook));
397           return (0);
398 }
399