1 /*-
2 * Copyright (c) 2004-2005 Gleb Smirnoff <glebius@FreeBSD.org>
3 * Copyright (c) 2001-2003 Roman V. Palagin <romanp@unshadow.net>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $SourceForge: ng_netflow.c,v 1.30 2004/09/05 11:37:43 glebius Exp $
28 */
29
30 static const char rcs_id[] =
31 "@(#) $FreeBSD: releng/8.3/sys/netgraph/netflow/ng_netflow.c 192032 2009-05-13 02:26:34Z mav $";
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/limits.h>
37 #include <sys/mbuf.h>
38 #include <sys/socket.h>
39 #include <sys/syslog.h>
40 #include <sys/ctype.h>
41
42 #include <net/if.h>
43 #include <net/ethernet.h>
44 #include <net/if_arp.h>
45 #include <net/if_var.h>
46 #include <net/if_vlan_var.h>
47 #include <net/bpf.h>
48 #include <netinet/in.h>
49 #include <netinet/in_systm.h>
50 #include <netinet/ip.h>
51 #include <netinet/tcp.h>
52 #include <netinet/udp.h>
53
54 #include <netgraph/ng_message.h>
55 #include <netgraph/ng_parse.h>
56 #include <netgraph/netgraph.h>
57 #include <netgraph/netflow/netflow.h>
58 #include <netgraph/netflow/ng_netflow.h>
59
60 /* Netgraph methods */
61 static ng_constructor_t ng_netflow_constructor;
62 static ng_rcvmsg_t ng_netflow_rcvmsg;
63 static ng_close_t ng_netflow_close;
64 static ng_shutdown_t ng_netflow_rmnode;
65 static ng_newhook_t ng_netflow_newhook;
66 static ng_rcvdata_t ng_netflow_rcvdata;
67 static ng_disconnect_t ng_netflow_disconnect;
68
69 /* Parse type for struct ng_netflow_info */
70 static const struct ng_parse_struct_field ng_netflow_info_type_fields[]
71 = NG_NETFLOW_INFO_TYPE;
72 static const struct ng_parse_type ng_netflow_info_type = {
73 &ng_parse_struct_type,
74 &ng_netflow_info_type_fields
75 };
76
77 /* Parse type for struct ng_netflow_ifinfo */
78 static const struct ng_parse_struct_field ng_netflow_ifinfo_type_fields[]
79 = NG_NETFLOW_IFINFO_TYPE;
80 static const struct ng_parse_type ng_netflow_ifinfo_type = {
81 &ng_parse_struct_type,
82 &ng_netflow_ifinfo_type_fields
83 };
84
85 /* Parse type for struct ng_netflow_setdlt */
86 static const struct ng_parse_struct_field ng_netflow_setdlt_type_fields[]
87 = NG_NETFLOW_SETDLT_TYPE;
88 static const struct ng_parse_type ng_netflow_setdlt_type = {
89 &ng_parse_struct_type,
90 &ng_netflow_setdlt_type_fields
91 };
92
93 /* Parse type for ng_netflow_setifindex */
94 static const struct ng_parse_struct_field ng_netflow_setifindex_type_fields[]
95 = NG_NETFLOW_SETIFINDEX_TYPE;
96 static const struct ng_parse_type ng_netflow_setifindex_type = {
97 &ng_parse_struct_type,
98 &ng_netflow_setifindex_type_fields
99 };
100
101 /* Parse type for ng_netflow_settimeouts */
102 static const struct ng_parse_struct_field ng_netflow_settimeouts_type_fields[]
103 = NG_NETFLOW_SETTIMEOUTS_TYPE;
104 static const struct ng_parse_type ng_netflow_settimeouts_type = {
105 &ng_parse_struct_type,
106 &ng_netflow_settimeouts_type_fields
107 };
108
109 /* Parse type for ng_netflow_setconfig */
110 static const struct ng_parse_struct_field ng_netflow_setconfig_type_fields[]
111 = NG_NETFLOW_SETCONFIG_TYPE;
112 static const struct ng_parse_type ng_netflow_setconfig_type = {
113 &ng_parse_struct_type,
114 &ng_netflow_setconfig_type_fields
115 };
116
117 /* List of commands and how to convert arguments to/from ASCII */
118 static const struct ng_cmdlist ng_netflow_cmds[] = {
119 {
120 NGM_NETFLOW_COOKIE,
121 NGM_NETFLOW_INFO,
122 "info",
123 NULL,
124 &ng_netflow_info_type
125 },
126 {
127 NGM_NETFLOW_COOKIE,
128 NGM_NETFLOW_IFINFO,
129 "ifinfo",
130 &ng_parse_uint16_type,
131 &ng_netflow_ifinfo_type
132 },
133 {
134 NGM_NETFLOW_COOKIE,
135 NGM_NETFLOW_SETDLT,
136 "setdlt",
137 &ng_netflow_setdlt_type,
138 NULL
139 },
140 {
141 NGM_NETFLOW_COOKIE,
142 NGM_NETFLOW_SETIFINDEX,
143 "setifindex",
144 &ng_netflow_setifindex_type,
145 NULL
146 },
147 {
148 NGM_NETFLOW_COOKIE,
149 NGM_NETFLOW_SETTIMEOUTS,
150 "settimeouts",
151 &ng_netflow_settimeouts_type,
152 NULL
153 },
154 {
155 NGM_NETFLOW_COOKIE,
156 NGM_NETFLOW_SETCONFIG,
157 "setconfig",
158 &ng_netflow_setconfig_type,
159 NULL
160 },
161 { 0 }
162 };
163
164
165 /* Netgraph node type descriptor */
166 static struct ng_type ng_netflow_typestruct = {
167 .version = NG_ABI_VERSION,
168 .name = NG_NETFLOW_NODE_TYPE,
169 .constructor = ng_netflow_constructor,
170 .rcvmsg = ng_netflow_rcvmsg,
171 .close = ng_netflow_close,
172 .shutdown = ng_netflow_rmnode,
173 .newhook = ng_netflow_newhook,
174 .rcvdata = ng_netflow_rcvdata,
175 .disconnect = ng_netflow_disconnect,
176 .cmdlist = ng_netflow_cmds,
177 };
178 NETGRAPH_INIT(netflow, &ng_netflow_typestruct);
179
180 /* Called at node creation */
181 static int
182 ng_netflow_constructor(node_p node)
183 {
184 priv_p priv;
185 int error = 0, i;
186
187 /* Initialize private data */
188 priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT);
189 if (priv == NULL)
190 return (ENOMEM);
191 bzero(priv, sizeof(*priv));
192
193 /* Make node and its data point at each other */
194 NG_NODE_SET_PRIVATE(node, priv);
195 priv->node = node;
196
197 /* Initialize timeouts to default values */
198 priv->info.nfinfo_inact_t = INACTIVE_TIMEOUT;
199 priv->info.nfinfo_act_t = ACTIVE_TIMEOUT;
200
201 /* Set default config */
202 for (i = 0; i < NG_NETFLOW_MAXIFACES; i++)
203 priv->ifaces[i].info.conf = NG_NETFLOW_CONF_INGRESS;
204
205 /* Initialize callout handle */
206 callout_init(&priv->exp_callout, CALLOUT_MPSAFE);
207
208 /* Allocate memory and set up flow cache */
209 if ((error = ng_netflow_cache_init(priv)))
210 return (error);
211
212 return (0);
213 }
214
215 /*
216 * ng_netflow supports two hooks: data and export.
217 * Incoming traffic is expected on data, and expired
218 * netflow datagrams are sent to export.
219 */
220 static int
221 ng_netflow_newhook(node_p node, hook_p hook, const char *name)
222 {
223 const priv_p priv = NG_NODE_PRIVATE(node);
224
225 if (strncmp(name, NG_NETFLOW_HOOK_DATA, /* an iface hook? */
226 strlen(NG_NETFLOW_HOOK_DATA)) == 0) {
227 iface_p iface;
228 int ifnum = -1;
229 const char *cp;
230 char *eptr;
231
232 cp = name + strlen(NG_NETFLOW_HOOK_DATA);
233 if (!isdigit(*cp) || (cp[0] == '' && cp[1] != '\0'))
234 return (EINVAL);
235
236 ifnum = (int)strtoul(cp, &eptr, 10);
237 if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES)
238 return (EINVAL);
239
240 /* See if hook is already connected */
241 if (priv->ifaces[ifnum].hook != NULL)
242 return (EISCONN);
243
244 iface = &priv->ifaces[ifnum];
245
246 /* Link private info and hook together */
247 NG_HOOK_SET_PRIVATE(hook, iface);
248 iface->hook = hook;
249
250 /*
251 * In most cases traffic accounting is done on an
252 * Ethernet interface, so default data link type
253 * will be DLT_EN10MB.
254 */
255 iface->info.ifinfo_dlt = DLT_EN10MB;
256
257 } else if (strncmp(name, NG_NETFLOW_HOOK_OUT,
258 strlen(NG_NETFLOW_HOOK_OUT)) == 0) {
259 iface_p iface;
260 int ifnum = -1;
261 const char *cp;
262 char *eptr;
263
264 cp = name + strlen(NG_NETFLOW_HOOK_OUT);
265 if (!isdigit(*cp) || (cp[0] == '' && cp[1] != '\0'))
266 return (EINVAL);
267
268 ifnum = (int)strtoul(cp, &eptr, 10);
269 if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES)
270 return (EINVAL);
271
272 /* See if hook is already connected */
273 if (priv->ifaces[ifnum].out != NULL)
274 return (EISCONN);
275
276 iface = &priv->ifaces[ifnum];
277
278 /* Link private info and hook together */
279 NG_HOOK_SET_PRIVATE(hook, iface);
280 iface->out = hook;
281
282 } else if (strcmp(name, NG_NETFLOW_HOOK_EXPORT) == 0) {
283
284 if (priv->export != NULL)
285 return (EISCONN);
286
287 priv->export = hook;
288
289 #if 0 /* TODO: profile & test first */
290 /*
291 * We send export dgrams in interrupt handlers and in
292 * callout threads. We'd better queue data for later
293 * netgraph ISR processing.
294 */
295 NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
296 #endif
297
298 /* Exporter is ready. Let's schedule expiry. */
299 callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire,
300 (void *)priv);
301 } else
302 return (EINVAL);
303
304 return (0);
305 }
306
307 /* Get a netgraph control message. */
308 static int
309 ng_netflow_rcvmsg (node_p node, item_p item, hook_p lasthook)
310 {
311 const priv_p priv = NG_NODE_PRIVATE(node);
312 struct ng_mesg *resp = NULL;
313 int error = 0;
314 struct ng_mesg *msg;
315
316 NGI_GET_MSG(item, msg);
317
318 /* Deal with message according to cookie and command */
319 switch (msg->header.typecookie) {
320 case NGM_NETFLOW_COOKIE:
321 switch (msg->header.cmd) {
322 case NGM_NETFLOW_INFO:
323 {
324 struct ng_netflow_info *i;
325
326 NG_MKRESPONSE(resp, msg, sizeof(struct ng_netflow_info),
327 M_NOWAIT);
328 i = (struct ng_netflow_info *)resp->data;
329 ng_netflow_copyinfo(priv, i);
330
331 break;
332 }
333 case NGM_NETFLOW_IFINFO:
334 {
335 struct ng_netflow_ifinfo *i;
336 const uint16_t *index;
337
338 if (msg->header.arglen != sizeof(uint16_t))
339 ERROUT(EINVAL);
340
341 index = (uint16_t *)msg->data;
342 if (*index >= NG_NETFLOW_MAXIFACES)
343 ERROUT(EINVAL);
344
345 /* connected iface? */
346 if (priv->ifaces[*index].hook == NULL)
347 ERROUT(EINVAL);
348
349 NG_MKRESPONSE(resp, msg,
350 sizeof(struct ng_netflow_ifinfo), M_NOWAIT);
351 i = (struct ng_netflow_ifinfo *)resp->data;
352 memcpy((void *)i, (void *)&priv->ifaces[*index].info,
353 sizeof(priv->ifaces[*index].info));
354
355 break;
356 }
357 case NGM_NETFLOW_SETDLT:
358 {
359 struct ng_netflow_setdlt *set;
360 struct ng_netflow_iface *iface;
361
362 if (msg->header.arglen != sizeof(struct ng_netflow_setdlt))
363 ERROUT(EINVAL);
364
365 set = (struct ng_netflow_setdlt *)msg->data;
366 if (set->iface >= NG_NETFLOW_MAXIFACES)
367 ERROUT(EINVAL);
368 iface = &priv->ifaces[set->iface];
369
370 /* connected iface? */
371 if (iface->hook == NULL)
372 ERROUT(EINVAL);
373
374 switch (set->dlt) {
375 case DLT_EN10MB:
376 iface->info.ifinfo_dlt = DLT_EN10MB;
377 break;
378 case DLT_RAW:
379 iface->info.ifinfo_dlt = DLT_RAW;
380 break;
381 default:
382 ERROUT(EINVAL);
383 }
384 break;
385 }
386 case NGM_NETFLOW_SETIFINDEX:
387 {
388 struct ng_netflow_setifindex *set;
389 struct ng_netflow_iface *iface;
390
391 if (msg->header.arglen != sizeof(struct ng_netflow_setifindex))
392 ERROUT(EINVAL);
393
394 set = (struct ng_netflow_setifindex *)msg->data;
395 if (set->iface >= NG_NETFLOW_MAXIFACES)
396 ERROUT(EINVAL);
397 iface = &priv->ifaces[set->iface];
398
399 /* connected iface? */
400 if (iface->hook == NULL)
401 ERROUT(EINVAL);
402
403 iface->info.ifinfo_index = set->index;
404
405 break;
406 }
407 case NGM_NETFLOW_SETTIMEOUTS:
408 {
409 struct ng_netflow_settimeouts *set;
410
411 if (msg->header.arglen != sizeof(struct ng_netflow_settimeouts))
412 ERROUT(EINVAL);
413
414 set = (struct ng_netflow_settimeouts *)msg->data;
415
416 priv->info.nfinfo_inact_t = set->inactive_timeout;
417 priv->info.nfinfo_act_t = set->active_timeout;
418
419 break;
420 }
421 case NGM_NETFLOW_SETCONFIG:
422 {
423 struct ng_netflow_setconfig *set;
424
425 if (msg->header.arglen != sizeof(struct ng_netflow_setconfig))
426 ERROUT(EINVAL);
427
428 set = (struct ng_netflow_setconfig *)msg->data;
429
430 if (set->iface >= NG_NETFLOW_MAXIFACES)
431 ERROUT(EINVAL);
432
433 priv->ifaces[set->iface].info.conf = set->conf;
434
435 break;
436 }
437 case NGM_NETFLOW_SHOW:
438 {
439 uint32_t *last;
440
441 if (msg->header.arglen != sizeof(uint32_t))
442 ERROUT(EINVAL);
443
444 last = (uint32_t *)msg->data;
445
446 NG_MKRESPONSE(resp, msg, NGRESP_SIZE, M_NOWAIT);
447
448 if (!resp)
449 ERROUT(ENOMEM);
450
451 error = ng_netflow_flow_show(priv, *last, resp);
452
453 break;
454 }
455 default:
456 ERROUT(EINVAL); /* unknown command */
457 break;
458 }
459 break;
460 default:
461 ERROUT(EINVAL); /* incorrect cookie */
462 break;
463 }
464
465 /*
466 * Take care of synchronous response, if any.
467 * Free memory and return.
468 */
469 done:
470 NG_RESPOND_MSG(error, node, item, resp);
471 NG_FREE_MSG(msg);
472
473 return (error);
474 }
475
476 /* Receive data on hook. */
477 static int
478 ng_netflow_rcvdata (hook_p hook, item_p item)
479 {
480 const node_p node = NG_HOOK_NODE(hook);
481 const priv_p priv = NG_NODE_PRIVATE(node);
482 const iface_p iface = NG_HOOK_PRIVATE(hook);
483 hook_p out;
484 struct mbuf *m = NULL;
485 struct ip *ip;
486 struct m_tag *mtag;
487 int pullup_len = 0;
488 int error = 0, bypass = 0;
489 unsigned int src_if_index;
490
491 if (hook == priv->export) {
492 /*
493 * Data arrived on export hook.
494 * This must not happen.
495 */
496 log(LOG_ERR, "ng_netflow: incoming data on export hook!\n");
497 ERROUT(EINVAL);
498 };
499
500 if (hook == iface->hook) {
501 if ((iface->info.conf & NG_NETFLOW_CONF_INGRESS) == 0)
502 bypass = 1;
503 out = iface->out;
504 } else if (hook == iface->out) {
505 if ((iface->info.conf & NG_NETFLOW_CONF_EGRESS) == 0)
506 bypass = 1;
507 out = iface->hook;
508 } else
509 ERROUT(EINVAL);
510
511 if ((!bypass) &&
512 (iface->info.conf & (NG_NETFLOW_CONF_ONCE | NG_NETFLOW_CONF_THISONCE))) {
513 mtag = m_tag_locate(NGI_M(item), MTAG_NETFLOW,
514 MTAG_NETFLOW_CALLED, NULL);
515 while (mtag != NULL) {
516 if ((iface->info.conf & NG_NETFLOW_CONF_ONCE) ||
517 ((ng_ID_t *)(mtag + 1))[0] == NG_NODE_ID(node)) {
518 bypass = 1;
519 break;
520 }
521 mtag = m_tag_locate(NGI_M(item), MTAG_NETFLOW,
522 MTAG_NETFLOW_CALLED, mtag);
523 }
524 }
525
526 if (bypass) {
527 if (out == NULL)
528 ERROUT(ENOTCONN);
529
530 NG_FWD_ITEM_HOOK(error, item, out);
531 return (error);
532 }
533
534 if (iface->info.conf & (NG_NETFLOW_CONF_ONCE | NG_NETFLOW_CONF_THISONCE)) {
535 mtag = m_tag_alloc(MTAG_NETFLOW, MTAG_NETFLOW_CALLED,
536 sizeof(ng_ID_t), M_NOWAIT);
537 if (mtag) {
538 ((ng_ID_t *)(mtag + 1))[0] = NG_NODE_ID(node);
539 m_tag_prepend(NGI_M(item), mtag);
540 }
541 }
542
543 NGI_GET_M(item, m);
544
545 /* Increase counters. */
546 iface->info.ifinfo_packets++;
547
548 /*
549 * Depending on interface data link type and packet contents
550 * we pullup enough data, so that ng_netflow_flow_add() does not
551 * need to know about mbuf at all. We keep current length of data
552 * needed to be contiguous in pullup_len. mtod() is done at the
553 * very end one more time, since m can had changed after pulluping.
554 *
555 * In case of unrecognized data we don't return error, but just
556 * pass data to downstream hook, if it is available.
557 */
558
559 #define M_CHECK(length) do { \
560 pullup_len += length; \
561 if ((m)->m_pkthdr.len < (pullup_len)) { \
562 error = EINVAL; \
563 goto bypass; \
564 } \
565 if ((m)->m_len < (pullup_len) && \
566 (((m) = m_pullup((m),(pullup_len))) == NULL)) { \
567 error = ENOBUFS; \
568 goto done; \
569 } \
570 } while (0)
571
572 switch (iface->info.ifinfo_dlt) {
573 case DLT_EN10MB: /* Ethernet */
574 {
575 struct ether_header *eh;
576 uint16_t etype;
577
578 M_CHECK(sizeof(struct ether_header));
579 eh = mtod(m, struct ether_header *);
580
581 /* Make sure this is IP frame. */
582 etype = ntohs(eh->ether_type);
583 switch (etype) {
584 case ETHERTYPE_IP:
585 M_CHECK(sizeof(struct ip));
586 eh = mtod(m, struct ether_header *);
587 ip = (struct ip *)(eh + 1);
588 break;
589 case ETHERTYPE_VLAN:
590 {
591 struct ether_vlan_header *evh;
592
593 M_CHECK(sizeof(struct ether_vlan_header) -
594 sizeof(struct ether_header));
595 evh = mtod(m, struct ether_vlan_header *);
596 if (ntohs(evh->evl_proto) == ETHERTYPE_IP) {
597 M_CHECK(sizeof(struct ip));
598 ip = (struct ip *)(evh + 1);
599 break;
600 }
601 }
602 default:
603 goto bypass; /* pass this frame */
604 }
605 break;
606 }
607 case DLT_RAW: /* IP packets */
608 M_CHECK(sizeof(struct ip));
609 ip = mtod(m, struct ip *);
610 break;
611 default:
612 goto bypass;
613 break;
614 }
615
616 if ((ip->ip_off & htons(IP_OFFMASK)) == 0) {
617 /*
618 * In case of IP header with options, we haven't pulled
619 * up enough, yet.
620 */
621 pullup_len += (ip->ip_hl << 2) - sizeof(struct ip);
622
623 switch (ip->ip_p) {
624 case IPPROTO_TCP:
625 M_CHECK(sizeof(struct tcphdr));
626 break;
627 case IPPROTO_UDP:
628 M_CHECK(sizeof(struct udphdr));
629 break;
630 }
631 }
632
633 switch (iface->info.ifinfo_dlt) {
634 case DLT_EN10MB:
635 {
636 struct ether_header *eh;
637
638 eh = mtod(m, struct ether_header *);
639 switch (ntohs(eh->ether_type)) {
640 case ETHERTYPE_IP:
641 ip = (struct ip *)(eh + 1);
642 break;
643 case ETHERTYPE_VLAN:
644 {
645 struct ether_vlan_header *evh;
646
647 evh = mtod(m, struct ether_vlan_header *);
648 ip = (struct ip *)(evh + 1);
649 break;
650 }
651 default:
652 panic("ng_netflow entered deadcode");
653 }
654 break;
655 }
656 case DLT_RAW:
657 ip = mtod(m, struct ip *);
658 break;
659 default:
660 panic("ng_netflow entered deadcode");
661 }
662
663 #undef M_CHECK
664
665 /* Determine packet input interface. Prefer configured. */
666 src_if_index = 0;
667 if (hook == iface->out || iface->info.ifinfo_index == 0) {
668 if (m->m_pkthdr.rcvif != NULL)
669 src_if_index = m->m_pkthdr.rcvif->if_index;
670 } else
671 src_if_index = iface->info.ifinfo_index;
672
673 error = ng_netflow_flow_add(priv, ip, src_if_index);
674
675 bypass:
676 if (out != NULL) {
677 /* XXX: error gets overwritten here */
678 NG_FWD_NEW_DATA(error, item, out, m);
679 return (error);
680 }
681 done:
682 if (item)
683 NG_FREE_ITEM(item);
684 if (m)
685 NG_FREE_M(m);
686
687 return (error);
688 }
689
690 /* We will be shut down in a moment */
691 static int
692 ng_netflow_close(node_p node)
693 {
694 const priv_p priv = NG_NODE_PRIVATE(node);
695
696 callout_drain(&priv->exp_callout);
697 ng_netflow_cache_flush(priv);
698
699 return (0);
700 }
701
702 /* Do local shutdown processing. */
703 static int
704 ng_netflow_rmnode(node_p node)
705 {
706 const priv_p priv = NG_NODE_PRIVATE(node);
707
708 NG_NODE_SET_PRIVATE(node, NULL);
709 NG_NODE_UNREF(priv->node);
710
711 free(priv, M_NETGRAPH);
712
713 return (0);
714 }
715
716 /* Hook disconnection. */
717 static int
718 ng_netflow_disconnect(hook_p hook)
719 {
720 node_p node = NG_HOOK_NODE(hook);
721 priv_p priv = NG_NODE_PRIVATE(node);
722 iface_p iface = NG_HOOK_PRIVATE(hook);
723
724 if (iface != NULL) {
725 if (iface->hook == hook)
726 iface->hook = NULL;
727 if (iface->out == hook)
728 iface->out = NULL;
729 }
730
731 /* if export hook disconnected stop running expire(). */
732 if (hook == priv->export) {
733 callout_drain(&priv->exp_callout);
734 priv->export = NULL;
735 }
736
737 /* Removal of the last link destroys the node. */
738 if (NG_NODE_NUMHOOKS(node) == 0)
739 ng_rmnode_self(node);
740
741 return (0);
742 }
Cache object: aeb13bd5c30da7d92c4a698ba42d4b6e
|