Commit | Line | Data |
---|---|---|
b06ebda0 MD |
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 $ | |
5a975a3d | 28 | * $FreeBSD: src/sys/netgraph/netflow/ng_netflow.c,v 1.17 2008/04/16 16:47:14 kris Exp $ |
b06ebda0 MD |
29 | */ |
30 | ||
b06ebda0 MD |
31 | #include <sys/param.h> |
32 | #include <sys/systm.h> | |
33 | #include <sys/kernel.h> | |
34 | #include <sys/limits.h> | |
805c8e8e | 35 | #include <sys/malloc.h> |
b06ebda0 MD |
36 | #include <sys/mbuf.h> |
37 | #include <sys/socket.h> | |
38 | #include <sys/syslog.h> | |
39 | #include <sys/ctype.h> | |
40 | ||
41 | #include <net/if.h> | |
42 | #include <net/ethernet.h> | |
43 | #include <net/if_arp.h> | |
44 | #include <net/if_var.h> | |
45 | #include <net/if_vlan_var.h> | |
46 | #include <net/bpf.h> | |
47 | #include <netinet/in.h> | |
48 | #include <netinet/in_systm.h> | |
49 | #include <netinet/ip.h> | |
50 | #include <netinet/tcp.h> | |
51 | #include <netinet/udp.h> | |
52 | ||
5a975a3d MD |
53 | #include "ng_message.h" |
54 | #include "ng_parse.h" | |
55 | #include "netgraph.h" | |
56 | #include "netflow/netflow.h" | |
57 | #include "netflow/ng_netflow.h" | |
b06ebda0 MD |
58 | |
59 | /* Netgraph methods */ | |
60 | static ng_constructor_t ng_netflow_constructor; | |
61 | static ng_rcvmsg_t ng_netflow_rcvmsg; | |
62 | static ng_close_t ng_netflow_close; | |
63 | static ng_shutdown_t ng_netflow_rmnode; | |
64 | static ng_newhook_t ng_netflow_newhook; | |
65 | static ng_rcvdata_t ng_netflow_rcvdata; | |
66 | static ng_disconnect_t ng_netflow_disconnect; | |
67 | ||
68 | /* Parse type for struct ng_netflow_info */ | |
69 | static const struct ng_parse_struct_field ng_netflow_info_type_fields[] | |
70 | = NG_NETFLOW_INFO_TYPE; | |
71 | static const struct ng_parse_type ng_netflow_info_type = { | |
72 | &ng_parse_struct_type, | |
73 | &ng_netflow_info_type_fields | |
74 | }; | |
75 | ||
76 | /* Parse type for struct ng_netflow_ifinfo */ | |
77 | static const struct ng_parse_struct_field ng_netflow_ifinfo_type_fields[] | |
78 | = NG_NETFLOW_IFINFO_TYPE; | |
79 | static const struct ng_parse_type ng_netflow_ifinfo_type = { | |
80 | &ng_parse_struct_type, | |
81 | &ng_netflow_ifinfo_type_fields | |
82 | }; | |
83 | ||
84 | /* Parse type for struct ng_netflow_setdlt */ | |
85 | static const struct ng_parse_struct_field ng_netflow_setdlt_type_fields[] | |
86 | = NG_NETFLOW_SETDLT_TYPE; | |
87 | static const struct ng_parse_type ng_netflow_setdlt_type = { | |
88 | &ng_parse_struct_type, | |
89 | &ng_netflow_setdlt_type_fields | |
90 | }; | |
91 | ||
92 | /* Parse type for ng_netflow_setifindex */ | |
93 | static const struct ng_parse_struct_field ng_netflow_setifindex_type_fields[] | |
94 | = NG_NETFLOW_SETIFINDEX_TYPE; | |
95 | static const struct ng_parse_type ng_netflow_setifindex_type = { | |
96 | &ng_parse_struct_type, | |
97 | &ng_netflow_setifindex_type_fields | |
98 | }; | |
99 | ||
100 | /* Parse type for ng_netflow_settimeouts */ | |
101 | static const struct ng_parse_struct_field ng_netflow_settimeouts_type_fields[] | |
102 | = NG_NETFLOW_SETTIMEOUTS_TYPE; | |
103 | static const struct ng_parse_type ng_netflow_settimeouts_type = { | |
104 | &ng_parse_struct_type, | |
105 | &ng_netflow_settimeouts_type_fields | |
106 | }; | |
107 | ||
108 | /* List of commands and how to convert arguments to/from ASCII */ | |
109 | static const struct ng_cmdlist ng_netflow_cmds[] = { | |
110 | { | |
111 | NGM_NETFLOW_COOKIE, | |
112 | NGM_NETFLOW_INFO, | |
113 | "info", | |
114 | NULL, | |
115 | &ng_netflow_info_type | |
116 | }, | |
117 | { | |
118 | NGM_NETFLOW_COOKIE, | |
119 | NGM_NETFLOW_IFINFO, | |
120 | "ifinfo", | |
121 | &ng_parse_uint16_type, | |
122 | &ng_netflow_ifinfo_type | |
123 | }, | |
124 | { | |
125 | NGM_NETFLOW_COOKIE, | |
126 | NGM_NETFLOW_SETDLT, | |
127 | "setdlt", | |
128 | &ng_netflow_setdlt_type, | |
129 | NULL | |
130 | }, | |
131 | { | |
132 | NGM_NETFLOW_COOKIE, | |
133 | NGM_NETFLOW_SETIFINDEX, | |
134 | "setifindex", | |
135 | &ng_netflow_setifindex_type, | |
136 | NULL | |
137 | }, | |
138 | { | |
139 | NGM_NETFLOW_COOKIE, | |
140 | NGM_NETFLOW_SETTIMEOUTS, | |
141 | "settimeouts", | |
142 | &ng_netflow_settimeouts_type, | |
143 | NULL | |
144 | }, | |
145 | { 0 } | |
146 | }; | |
147 | ||
148 | ||
149 | /* Netgraph node type descriptor */ | |
150 | static struct ng_type ng_netflow_typestruct = { | |
151 | .version = NG_ABI_VERSION, | |
152 | .name = NG_NETFLOW_NODE_TYPE, | |
153 | .constructor = ng_netflow_constructor, | |
154 | .rcvmsg = ng_netflow_rcvmsg, | |
155 | .close = ng_netflow_close, | |
156 | .shutdown = ng_netflow_rmnode, | |
157 | .newhook = ng_netflow_newhook, | |
158 | .rcvdata = ng_netflow_rcvdata, | |
159 | .disconnect = ng_netflow_disconnect, | |
160 | .cmdlist = ng_netflow_cmds, | |
161 | }; | |
162 | NETGRAPH_INIT(netflow, &ng_netflow_typestruct); | |
163 | ||
164 | /* Called at node creation */ | |
165 | static int | |
166 | ng_netflow_constructor(node_p node) | |
167 | { | |
168 | priv_p priv; | |
169 | int error = 0; | |
170 | ||
171 | /* Initialize private data */ | |
a2d0f739 | 172 | priv = kmalloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_NULLOK | M_ZERO); |
b06ebda0 MD |
173 | if (priv == NULL) |
174 | return (ENOMEM); | |
b06ebda0 MD |
175 | |
176 | /* Make node and its data point at each other */ | |
177 | NG_NODE_SET_PRIVATE(node, priv); | |
178 | priv->node = node; | |
179 | ||
180 | /* Initialize timeouts to default values */ | |
181 | priv->info.nfinfo_inact_t = INACTIVE_TIMEOUT; | |
182 | priv->info.nfinfo_act_t = ACTIVE_TIMEOUT; | |
183 | ||
184 | /* Initialize callout handle */ | |
185 | callout_init(&priv->exp_callout, CALLOUT_MPSAFE); | |
186 | ||
187 | /* Allocate memory and set up flow cache */ | |
188 | if ((error = ng_netflow_cache_init(priv))) | |
189 | return (error); | |
190 | ||
191 | return (0); | |
192 | } | |
193 | ||
194 | /* | |
195 | * ng_netflow supports two hooks: data and export. | |
196 | * Incoming traffic is expected on data, and expired | |
197 | * netflow datagrams are sent to export. | |
198 | */ | |
199 | static int | |
200 | ng_netflow_newhook(node_p node, hook_p hook, const char *name) | |
201 | { | |
202 | const priv_p priv = NG_NODE_PRIVATE(node); | |
203 | ||
204 | if (strncmp(name, NG_NETFLOW_HOOK_DATA, /* an iface hook? */ | |
205 | strlen(NG_NETFLOW_HOOK_DATA)) == 0) { | |
206 | iface_p iface; | |
207 | int ifnum = -1; | |
208 | const char *cp; | |
209 | char *eptr; | |
210 | ||
211 | cp = name + strlen(NG_NETFLOW_HOOK_DATA); | |
212 | if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) | |
213 | return (EINVAL); | |
214 | ||
215 | ifnum = (int)strtoul(cp, &eptr, 10); | |
216 | if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES) | |
217 | return (EINVAL); | |
218 | ||
219 | /* See if hook is already connected */ | |
220 | if (priv->ifaces[ifnum].hook != NULL) | |
221 | return (EISCONN); | |
222 | ||
223 | iface = &priv->ifaces[ifnum]; | |
224 | ||
225 | /* Link private info and hook together */ | |
226 | NG_HOOK_SET_PRIVATE(hook, iface); | |
227 | iface->hook = hook; | |
228 | ||
229 | /* | |
230 | * In most cases traffic accounting is done on an | |
231 | * Ethernet interface, so default data link type | |
232 | * will be DLT_EN10MB. | |
233 | */ | |
234 | iface->info.ifinfo_dlt = DLT_EN10MB; | |
235 | ||
236 | } else if (strncmp(name, NG_NETFLOW_HOOK_OUT, | |
237 | strlen(NG_NETFLOW_HOOK_OUT)) == 0) { | |
238 | iface_p iface; | |
239 | int ifnum = -1; | |
240 | const char *cp; | |
241 | char *eptr; | |
242 | ||
243 | cp = name + strlen(NG_NETFLOW_HOOK_OUT); | |
244 | if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) | |
245 | return (EINVAL); | |
246 | ||
247 | ifnum = (int)strtoul(cp, &eptr, 10); | |
248 | if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES) | |
249 | return (EINVAL); | |
250 | ||
251 | /* See if hook is already connected */ | |
252 | if (priv->ifaces[ifnum].out != NULL) | |
253 | return (EISCONN); | |
254 | ||
255 | iface = &priv->ifaces[ifnum]; | |
256 | ||
257 | /* Link private info and hook together */ | |
258 | NG_HOOK_SET_PRIVATE(hook, iface); | |
259 | iface->out = hook; | |
260 | ||
261 | } else if (strcmp(name, NG_NETFLOW_HOOK_EXPORT) == 0) { | |
262 | ||
263 | if (priv->export != NULL) | |
264 | return (EISCONN); | |
265 | ||
266 | priv->export = hook; | |
267 | ||
268 | #if 0 /* TODO: profile & test first */ | |
269 | /* | |
270 | * We send export dgrams in interrupt handlers and in | |
271 | * callout threads. We'd better queue data for later | |
272 | * netgraph ISR processing. | |
273 | */ | |
274 | NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); | |
275 | #endif | |
276 | ||
277 | /* Exporter is ready. Let's schedule expiry. */ | |
278 | callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire, | |
279 | (void *)priv); | |
280 | } else | |
281 | return (EINVAL); | |
282 | ||
283 | return (0); | |
284 | } | |
285 | ||
286 | /* Get a netgraph control message. */ | |
287 | static int | |
288 | ng_netflow_rcvmsg (node_p node, item_p item, hook_p lasthook) | |
289 | { | |
290 | const priv_p priv = NG_NODE_PRIVATE(node); | |
291 | struct ng_mesg *resp = NULL; | |
292 | int error = 0; | |
293 | struct ng_mesg *msg; | |
294 | ||
295 | NGI_GET_MSG(item, msg); | |
296 | ||
297 | /* Deal with message according to cookie and command */ | |
298 | switch (msg->header.typecookie) { | |
299 | case NGM_NETFLOW_COOKIE: | |
300 | switch (msg->header.cmd) { | |
301 | case NGM_NETFLOW_INFO: | |
302 | { | |
303 | struct ng_netflow_info *i; | |
304 | ||
305 | NG_MKRESPONSE(resp, msg, sizeof(struct ng_netflow_info), | |
5a975a3d | 306 | M_WAITOK | M_NULLOK); |
b06ebda0 MD |
307 | i = (struct ng_netflow_info *)resp->data; |
308 | ng_netflow_copyinfo(priv, i); | |
309 | ||
310 | break; | |
311 | } | |
312 | case NGM_NETFLOW_IFINFO: | |
313 | { | |
314 | struct ng_netflow_ifinfo *i; | |
315 | const uint16_t *index; | |
316 | ||
317 | if (msg->header.arglen != sizeof(uint16_t)) | |
318 | ERROUT(EINVAL); | |
319 | ||
320 | index = (uint16_t *)msg->data; | |
321 | if (*index >= NG_NETFLOW_MAXIFACES) | |
322 | ERROUT(EINVAL); | |
323 | ||
324 | /* connected iface? */ | |
325 | if (priv->ifaces[*index].hook == NULL) | |
326 | ERROUT(EINVAL); | |
327 | ||
328 | NG_MKRESPONSE(resp, msg, | |
5a975a3d | 329 | sizeof(struct ng_netflow_ifinfo), M_WAITOK | M_NULLOK); |
b06ebda0 MD |
330 | i = (struct ng_netflow_ifinfo *)resp->data; |
331 | memcpy((void *)i, (void *)&priv->ifaces[*index].info, | |
332 | sizeof(priv->ifaces[*index].info)); | |
333 | ||
334 | break; | |
335 | } | |
336 | case NGM_NETFLOW_SETDLT: | |
337 | { | |
338 | struct ng_netflow_setdlt *set; | |
339 | struct ng_netflow_iface *iface; | |
340 | ||
341 | if (msg->header.arglen != sizeof(struct ng_netflow_setdlt)) | |
342 | ERROUT(EINVAL); | |
343 | ||
344 | set = (struct ng_netflow_setdlt *)msg->data; | |
345 | if (set->iface >= NG_NETFLOW_MAXIFACES) | |
346 | ERROUT(EINVAL); | |
347 | iface = &priv->ifaces[set->iface]; | |
348 | ||
349 | /* connected iface? */ | |
350 | if (iface->hook == NULL) | |
351 | ERROUT(EINVAL); | |
352 | ||
353 | switch (set->dlt) { | |
354 | case DLT_EN10MB: | |
355 | iface->info.ifinfo_dlt = DLT_EN10MB; | |
356 | break; | |
357 | case DLT_RAW: | |
358 | iface->info.ifinfo_dlt = DLT_RAW; | |
359 | break; | |
360 | default: | |
361 | ERROUT(EINVAL); | |
362 | } | |
363 | break; | |
364 | } | |
365 | case NGM_NETFLOW_SETIFINDEX: | |
366 | { | |
367 | struct ng_netflow_setifindex *set; | |
368 | struct ng_netflow_iface *iface; | |
369 | ||
370 | if (msg->header.arglen != sizeof(struct ng_netflow_setifindex)) | |
371 | ERROUT(EINVAL); | |
372 | ||
373 | set = (struct ng_netflow_setifindex *)msg->data; | |
374 | if (set->iface >= NG_NETFLOW_MAXIFACES) | |
375 | ERROUT(EINVAL); | |
376 | iface = &priv->ifaces[set->iface]; | |
377 | ||
378 | /* connected iface? */ | |
379 | if (iface->hook == NULL) | |
380 | ERROUT(EINVAL); | |
381 | ||
382 | iface->info.ifinfo_index = set->index; | |
383 | ||
384 | break; | |
385 | } | |
386 | case NGM_NETFLOW_SETTIMEOUTS: | |
387 | { | |
388 | struct ng_netflow_settimeouts *set; | |
389 | ||
390 | if (msg->header.arglen != sizeof(struct ng_netflow_settimeouts)) | |
391 | ERROUT(EINVAL); | |
392 | ||
393 | set = (struct ng_netflow_settimeouts *)msg->data; | |
394 | ||
395 | priv->info.nfinfo_inact_t = set->inactive_timeout; | |
396 | priv->info.nfinfo_act_t = set->active_timeout; | |
397 | ||
398 | break; | |
399 | } | |
400 | case NGM_NETFLOW_SHOW: | |
401 | { | |
402 | uint32_t *last; | |
403 | ||
404 | if (msg->header.arglen != sizeof(uint32_t)) | |
405 | ERROUT(EINVAL); | |
406 | ||
407 | last = (uint32_t *)msg->data; | |
408 | ||
5a975a3d | 409 | NG_MKRESPONSE(resp, msg, NGRESP_SIZE, M_WAITOK | M_NULLOK); |
b06ebda0 MD |
410 | |
411 | if (!resp) | |
412 | ERROUT(ENOMEM); | |
413 | ||
414 | error = ng_netflow_flow_show(priv, *last, resp); | |
415 | ||
416 | break; | |
417 | } | |
418 | default: | |
419 | ERROUT(EINVAL); /* unknown command */ | |
420 | break; | |
421 | } | |
422 | break; | |
423 | default: | |
424 | ERROUT(EINVAL); /* incorrect cookie */ | |
425 | break; | |
426 | } | |
427 | ||
428 | /* | |
429 | * Take care of synchronous response, if any. | |
430 | * Free memory and return. | |
431 | */ | |
432 | done: | |
433 | NG_RESPOND_MSG(error, node, item, resp); | |
434 | NG_FREE_MSG(msg); | |
435 | ||
436 | return (error); | |
437 | } | |
438 | ||
439 | /* Receive data on hook. */ | |
440 | static int | |
441 | ng_netflow_rcvdata (hook_p hook, item_p item) | |
442 | { | |
443 | const node_p node = NG_HOOK_NODE(hook); | |
444 | const priv_p priv = NG_NODE_PRIVATE(node); | |
445 | const iface_p iface = NG_HOOK_PRIVATE(hook); | |
446 | struct mbuf *m = NULL; | |
447 | struct ip *ip; | |
448 | int pullup_len = 0; | |
449 | int error = 0; | |
450 | ||
451 | if (hook == priv->export) { | |
452 | /* | |
453 | * Data arrived on export hook. | |
454 | * This must not happen. | |
455 | */ | |
456 | log(LOG_ERR, "ng_netflow: incoming data on export hook!\n"); | |
457 | ERROUT(EINVAL); | |
0fdb7d01 | 458 | } |
b06ebda0 MD |
459 | |
460 | if (hook == iface->out) { | |
461 | /* | |
462 | * Data arrived on out hook. Bypass it. | |
463 | */ | |
464 | if (iface->hook == NULL) | |
465 | ERROUT(ENOTCONN); | |
466 | ||
467 | NG_FWD_ITEM_HOOK(error, item, iface->hook); | |
468 | return (error); | |
469 | } | |
470 | ||
471 | NGI_GET_M(item, m); | |
472 | ||
473 | /* Increase counters. */ | |
474 | iface->info.ifinfo_packets++; | |
475 | ||
476 | /* | |
477 | * Depending on interface data link type and packet contents | |
478 | * we pullup enough data, so that ng_netflow_flow_add() does not | |
479 | * need to know about mbuf at all. We keep current length of data | |
480 | * needed to be contiguous in pullup_len. mtod() is done at the | |
481 | * very end one more time, since m can had changed after pulluping. | |
482 | * | |
483 | * In case of unrecognized data we don't return error, but just | |
484 | * pass data to downstream hook, if it is available. | |
485 | */ | |
486 | ||
487 | #define M_CHECK(length) do { \ | |
488 | pullup_len += length; \ | |
489 | if ((m)->m_pkthdr.len < (pullup_len)) { \ | |
490 | error = EINVAL; \ | |
491 | goto bypass; \ | |
492 | } \ | |
493 | if ((m)->m_len < (pullup_len) && \ | |
494 | (((m) = m_pullup((m),(pullup_len))) == NULL)) { \ | |
495 | error = ENOBUFS; \ | |
496 | goto done; \ | |
497 | } \ | |
498 | } while (0) | |
499 | ||
500 | switch (iface->info.ifinfo_dlt) { | |
501 | case DLT_EN10MB: /* Ethernet */ | |
502 | { | |
503 | struct ether_header *eh; | |
504 | uint16_t etype; | |
505 | ||
506 | M_CHECK(sizeof(struct ether_header)); | |
507 | eh = mtod(m, struct ether_header *); | |
508 | ||
509 | /* Make sure this is IP frame. */ | |
510 | etype = ntohs(eh->ether_type); | |
511 | switch (etype) { | |
512 | case ETHERTYPE_IP: | |
513 | M_CHECK(sizeof(struct ip)); | |
514 | eh = mtod(m, struct ether_header *); | |
515 | ip = (struct ip *)(eh + 1); | |
516 | break; | |
517 | case ETHERTYPE_VLAN: | |
518 | { | |
519 | struct ether_vlan_header *evh; | |
520 | ||
521 | M_CHECK(sizeof(struct ether_vlan_header) - | |
522 | sizeof(struct ether_header)); | |
523 | evh = mtod(m, struct ether_vlan_header *); | |
524 | if (ntohs(evh->evl_proto) == ETHERTYPE_IP) { | |
525 | M_CHECK(sizeof(struct ip)); | |
526 | ip = (struct ip *)(evh + 1); | |
527 | break; | |
528 | } | |
529 | } | |
530 | default: | |
531 | goto bypass; /* pass this frame */ | |
532 | } | |
533 | break; | |
534 | } | |
535 | case DLT_RAW: /* IP packets */ | |
536 | M_CHECK(sizeof(struct ip)); | |
537 | ip = mtod(m, struct ip *); | |
538 | break; | |
539 | default: | |
540 | goto bypass; | |
541 | break; | |
542 | } | |
543 | ||
544 | if ((ip->ip_off & htons(IP_OFFMASK)) == 0) { | |
545 | /* | |
546 | * In case of IP header with options, we haven't pulled | |
547 | * up enough, yet. | |
548 | */ | |
549 | pullup_len += (ip->ip_hl << 2) - sizeof(struct ip); | |
550 | ||
551 | switch (ip->ip_p) { | |
552 | case IPPROTO_TCP: | |
553 | M_CHECK(sizeof(struct tcphdr)); | |
554 | break; | |
555 | case IPPROTO_UDP: | |
556 | M_CHECK(sizeof(struct udphdr)); | |
557 | break; | |
558 | } | |
559 | } | |
560 | ||
561 | switch (iface->info.ifinfo_dlt) { | |
562 | case DLT_EN10MB: | |
563 | { | |
564 | struct ether_header *eh; | |
565 | ||
566 | eh = mtod(m, struct ether_header *); | |
567 | switch (ntohs(eh->ether_type)) { | |
568 | case ETHERTYPE_IP: | |
569 | ip = (struct ip *)(eh + 1); | |
570 | break; | |
571 | case ETHERTYPE_VLAN: | |
572 | { | |
573 | struct ether_vlan_header *evh; | |
574 | ||
575 | evh = mtod(m, struct ether_vlan_header *); | |
576 | ip = (struct ip *)(evh + 1); | |
577 | break; | |
578 | } | |
579 | default: | |
580 | panic("ng_netflow entered deadcode"); | |
581 | } | |
582 | break; | |
583 | } | |
584 | case DLT_RAW: | |
585 | ip = mtod(m, struct ip *); | |
586 | break; | |
587 | default: | |
588 | panic("ng_netflow entered deadcode"); | |
589 | } | |
590 | ||
591 | #undef M_CHECK | |
592 | ||
593 | error = ng_netflow_flow_add(priv, ip, iface, m->m_pkthdr.rcvif); | |
594 | ||
595 | bypass: | |
596 | if (iface->out != NULL) { | |
597 | /* XXX: error gets overwritten here */ | |
598 | NG_FWD_NEW_DATA(error, item, iface->out, m); | |
599 | return (error); | |
600 | } | |
601 | done: | |
602 | if (item) | |
603 | NG_FREE_ITEM(item); | |
604 | if (m) | |
605 | NG_FREE_M(m); | |
606 | ||
607 | return (error); | |
608 | } | |
609 | ||
610 | /* We will be shut down in a moment */ | |
611 | static int | |
612 | ng_netflow_close(node_p node) | |
613 | { | |
614 | const priv_p priv = NG_NODE_PRIVATE(node); | |
615 | ||
616 | callout_drain(&priv->exp_callout); | |
617 | ng_netflow_cache_flush(priv); | |
618 | ||
619 | return (0); | |
620 | } | |
621 | ||
622 | /* Do local shutdown processing. */ | |
623 | static int | |
624 | ng_netflow_rmnode(node_p node) | |
625 | { | |
626 | const priv_p priv = NG_NODE_PRIVATE(node); | |
627 | ||
628 | NG_NODE_SET_PRIVATE(node, NULL); | |
629 | NG_NODE_UNREF(priv->node); | |
630 | ||
fc025606 | 631 | kfree(priv, M_NETGRAPH); |
b06ebda0 MD |
632 | |
633 | return (0); | |
634 | } | |
635 | ||
636 | /* Hook disconnection. */ | |
637 | static int | |
638 | ng_netflow_disconnect(hook_p hook) | |
639 | { | |
640 | node_p node = NG_HOOK_NODE(hook); | |
641 | priv_p priv = NG_NODE_PRIVATE(node); | |
642 | iface_p iface = NG_HOOK_PRIVATE(hook); | |
643 | ||
644 | if (iface != NULL) { | |
645 | if (iface->hook == hook) | |
646 | iface->hook = NULL; | |
647 | if (iface->out == hook) | |
648 | iface->out = NULL; | |
649 | } | |
650 | ||
651 | /* if export hook disconnected stop running expire(). */ | |
652 | if (hook == priv->export) { | |
653 | callout_drain(&priv->exp_callout); | |
654 | priv->export = NULL; | |
655 | } | |
656 | ||
657 | /* Removal of the last link destroys the node. */ | |
658 | if (NG_NODE_NUMHOOKS(node) == 0) | |
659 | ng_rmnode_self(node); | |
660 | ||
661 | return (0); | |
662 | } |